1/***********************************************************
2    Written by:
3    Fred Gansevles <Fred.Gansevles@cs.utwente.nl>
4    B&O group,
5    Faculteit der Informatica,
6    Universiteit Twente,
7    Enschede,
8    the Netherlands.
9******************************************************************/
10
11/* NIS module implementation */
12
13#include "Python.h"
14
15#include <sys/time.h>
16#include <sys/types.h>
17#include <rpc/rpc.h>
18#include <rpcsvc/yp_prot.h>
19#include <rpcsvc/ypclnt.h>
20
21#ifdef __sgi
22/* This is missing from rpcsvc/ypclnt.h */
23extern int yp_get_default_domain(char **);
24#endif
25
26PyDoc_STRVAR(get_default_domain__doc__,
27"get_default_domain() -> str\n\
28Corresponds to the C library yp_get_default_domain() call, returning\n\
29the default NIS domain.\n");
30
31PyDoc_STRVAR(match__doc__,
32"match(key, map, domain = defaultdomain)\n\
33Corresponds to the C library yp_match() call, returning the value of\n\
34key in the given map. Optionally domain can be specified but it\n\
35defaults to the system default domain.\n");
36
37PyDoc_STRVAR(cat__doc__,
38"cat(map, domain = defaultdomain)\n\
39Returns the entire map as a dictionary. Optionally domain can be\n\
40specified but it defaults to the system default domain.\n");
41
42PyDoc_STRVAR(maps__doc__,
43"maps(domain = defaultdomain)\n\
44Returns an array of all available NIS maps within a domain. If domain\n\
45is not specified it defaults to the system default domain.\n");
46
47static PyObject *NisError;
48
49static PyObject *
50nis_error (int err)
51{
52    PyErr_SetString(NisError, yperr_string(err));
53    return NULL;
54}
55
56static struct nis_map {
57    char *alias;
58    char *map;
59    int  fix;
60} aliases [] = {
61    {"passwd",          "passwd.byname",        0},
62    {"group",           "group.byname",         0},
63    {"networks",        "networks.byaddr",      0},
64    {"hosts",           "hosts.byname",         0},
65    {"protocols",       "protocols.bynumber",   0},
66    {"services",        "services.byname",      0},
67    {"aliases",         "mail.aliases",         1}, /* created with 'makedbm -a' */
68    {"ethers",          "ethers.byname",        0},
69    {0L,                0L,                     0}
70};
71
72static char *
73nis_mapname (char *map, int *pfix)
74{
75    int i;
76
77    *pfix = 0;
78    for (i=0; aliases[i].alias != 0L; i++) {
79        if (!strcmp (aliases[i].alias, map)) {
80            *pfix = aliases[i].fix;
81            return aliases[i].map;
82        }
83        if (!strcmp (aliases[i].map, map)) {
84            *pfix = aliases[i].fix;
85            return aliases[i].map;
86        }
87    }
88
89    return map;
90}
91
92#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
93typedef int (*foreachfunc)(unsigned long, char *, int, char *, int, void *);
94#else
95typedef int (*foreachfunc)(int, char *, int, char *, int, char *);
96#endif
97
98struct ypcallback_data {
99    PyObject            *dict;
100    int                         fix;
101    PyThreadState *state;
102};
103
104static int
105nis_foreach (int instatus, char *inkey, int inkeylen, char *inval,
106             int invallen, struct ypcallback_data *indata)
107{
108    if (instatus == YP_TRUE) {
109        PyObject *key;
110        PyObject *val;
111        int err;
112
113        PyEval_RestoreThread(indata->state);
114        if (indata->fix) {
115            if (inkeylen > 0 && inkey[inkeylen-1] == '\0')
116            inkeylen--;
117            if (invallen > 0 && inval[invallen-1] == '\0')
118            invallen--;
119        }
120        key = PyString_FromStringAndSize(inkey, inkeylen);
121        val = PyString_FromStringAndSize(inval, invallen);
122        if (key == NULL || val == NULL) {
123            /* XXX error -- don't know how to handle */
124            PyErr_Clear();
125            Py_XDECREF(key);
126            Py_XDECREF(val);
127            indata->state = PyEval_SaveThread();
128            return 1;
129        }
130        err = PyDict_SetItem(indata->dict, key, val);
131        Py_DECREF(key);
132        Py_DECREF(val);
133        if (err != 0)
134            PyErr_Clear();
135        indata->state = PyEval_SaveThread();
136        if (err != 0)
137            return 1;
138        return 0;
139    }
140    return 1;
141}
142
143static PyObject *
144nis_get_default_domain (PyObject *self)
145{
146    char *domain;
147    int err;
148    PyObject *res;
149
150    if ((err = yp_get_default_domain(&domain)) != 0)
151        return nis_error(err);
152
153    res = PyString_FromStringAndSize (domain, strlen(domain));
154    return res;
155}
156
157static PyObject *
158nis_match (PyObject *self, PyObject *args, PyObject *kwdict)
159{
160    char *match;
161    char *domain = NULL;
162    int keylen, len;
163    char *key, *map;
164    int err;
165    PyObject *res;
166    int fix;
167    static char *kwlist[] = {"key", "map", "domain", NULL};
168
169    if (!PyArg_ParseTupleAndKeywords(args, kwdict,
170                                     "t#s|s:match", kwlist,
171                                     &key, &keylen, &map, &domain))
172        return NULL;
173    if (!domain && ((err = yp_get_default_domain(&domain)) != 0))
174        return nis_error(err);
175    map = nis_mapname (map, &fix);
176    if (fix)
177        keylen++;
178    Py_BEGIN_ALLOW_THREADS
179    err = yp_match (domain, map, key, keylen, &match, &len);
180    Py_END_ALLOW_THREADS
181    if (fix)
182        len--;
183    if (err != 0)
184        return nis_error(err);
185    res = PyString_FromStringAndSize (match, len);
186    free (match);
187    return res;
188}
189
190static PyObject *
191nis_cat (PyObject *self, PyObject *args, PyObject *kwdict)
192{
193    char *domain = NULL;
194    char *map;
195    struct ypall_callback cb;
196    struct ypcallback_data data;
197    PyObject *dict;
198    int err;
199    static char *kwlist[] = {"map", "domain", NULL};
200
201    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s|s:cat",
202                                     kwlist, &map, &domain))
203        return NULL;
204    if (!domain && ((err = yp_get_default_domain(&domain)) != 0))
205        return nis_error(err);
206    dict = PyDict_New ();
207    if (dict == NULL)
208        return NULL;
209    cb.foreach = (foreachfunc)nis_foreach;
210    data.dict = dict;
211    map = nis_mapname (map, &data.fix);
212    cb.data = (char *)&data;
213    data.state = PyEval_SaveThread();
214    err = yp_all (domain, map, &cb);
215    PyEval_RestoreThread(data.state);
216    if (err != 0) {
217        Py_DECREF(dict);
218        return nis_error(err);
219    }
220    return dict;
221}
222
223/* These should be u_long on Sun h/w but not on 64-bit h/w.
224   This is not portable to machines with 16-bit ints and no prototypes */
225#ifndef YPPROC_MAPLIST
226#define YPPROC_MAPLIST  11
227#endif
228#ifndef YPPROG
229#define YPPROG          100004
230#endif
231#ifndef YPVERS
232#define YPVERS          2
233#endif
234
235typedef char *domainname;
236typedef char *mapname;
237
238enum nisstat {
239    NIS_TRUE = 1,
240    NIS_NOMORE = 2,
241    NIS_FALSE = 0,
242    NIS_NOMAP = -1,
243    NIS_NODOM = -2,
244    NIS_NOKEY = -3,
245    NIS_BADOP = -4,
246    NIS_BADDB = -5,
247    NIS_YPERR = -6,
248    NIS_BADARGS = -7,
249    NIS_VERS = -8
250};
251typedef enum nisstat nisstat;
252
253struct nismaplist {
254    mapname map;
255    struct nismaplist *next;
256};
257typedef struct nismaplist nismaplist;
258
259struct nisresp_maplist {
260    nisstat stat;
261    nismaplist *maps;
262};
263typedef struct nisresp_maplist nisresp_maplist;
264
265static struct timeval TIMEOUT = { 25, 0 };
266
267static
268bool_t
269nis_xdr_domainname(XDR *xdrs, domainname *objp)
270{
271    if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) {
272        return (FALSE);
273    }
274    return (TRUE);
275}
276
277static
278bool_t
279nis_xdr_mapname(XDR *xdrs, mapname *objp)
280{
281    if (!xdr_string(xdrs, objp, YPMAXMAP)) {
282        return (FALSE);
283    }
284    return (TRUE);
285}
286
287static
288bool_t
289nis_xdr_ypmaplist(XDR *xdrs, nismaplist *objp)
290{
291    if (!nis_xdr_mapname(xdrs, &objp->map)) {
292        return (FALSE);
293    }
294    if (!xdr_pointer(xdrs, (char **)&objp->next,
295                     sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
296    {
297        return (FALSE);
298    }
299    return (TRUE);
300}
301
302static
303bool_t
304nis_xdr_ypstat(XDR *xdrs, nisstat *objp)
305{
306    if (!xdr_enum(xdrs, (enum_t *)objp)) {
307        return (FALSE);
308    }
309    return (TRUE);
310}
311
312
313static
314bool_t
315nis_xdr_ypresp_maplist(XDR *xdrs, nisresp_maplist *objp)
316{
317    if (!nis_xdr_ypstat(xdrs, &objp->stat)) {
318        return (FALSE);
319    }
320    if (!xdr_pointer(xdrs, (char **)&objp->maps,
321                     sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
322    {
323        return (FALSE);
324    }
325    return (TRUE);
326}
327
328
329static
330nisresp_maplist *
331nisproc_maplist_2(domainname *argp, CLIENT *clnt)
332{
333    static nisresp_maplist res;
334
335    memset(&res, 0, sizeof(res));
336    if (clnt_call(clnt, YPPROC_MAPLIST,
337                  (xdrproc_t)nis_xdr_domainname, (caddr_t)argp,
338                  (xdrproc_t)nis_xdr_ypresp_maplist, (caddr_t)&res,
339                  TIMEOUT) != RPC_SUCCESS)
340    {
341        return (NULL);
342    }
343    return (&res);
344}
345
346static
347nismaplist *
348nis_maplist (char *dom)
349{
350    nisresp_maplist *list;
351    CLIENT *cl;
352    char *server = NULL;
353    int mapi = 0;
354
355    while (!server && aliases[mapi].map != 0L) {
356        yp_master (dom, aliases[mapi].map, &server);
357        mapi++;
358    }
359    if (!server) {
360        PyErr_SetString(NisError, "No NIS master found for any map");
361        return NULL;
362    }
363    cl = clnt_create(server, YPPROG, YPVERS, "tcp");
364    if (cl == NULL) {
365        PyErr_SetString(NisError, clnt_spcreateerror(server));
366        goto finally;
367    }
368    list = nisproc_maplist_2 (&dom, cl);
369    clnt_destroy(cl);
370    if (list == NULL)
371        goto finally;
372    if (list->stat != NIS_TRUE)
373        goto finally;
374
375    free(server);
376    return list->maps;
377
378  finally:
379    free(server);
380    return NULL;
381}
382
383static PyObject *
384nis_maps (PyObject *self, PyObject *args, PyObject *kwdict)
385{
386    char *domain = NULL;
387    nismaplist *maps;
388    PyObject *list;
389    int err;
390    static char *kwlist[] = {"domain", NULL};
391
392    if (!PyArg_ParseTupleAndKeywords(args, kwdict,
393                                     "|s:maps", kwlist, &domain))
394        return NULL;
395    if (!domain && ((err = yp_get_default_domain (&domain)) != 0)) {
396        nis_error(err);
397        return NULL;
398    }
399
400    if ((maps = nis_maplist (domain)) == NULL)
401        return NULL;
402    if ((list = PyList_New(0)) == NULL)
403        return NULL;
404    for (maps = maps; maps; maps = maps->next) {
405        PyObject *str = PyString_FromString(maps->map);
406        if (!str || PyList_Append(list, str) < 0)
407        {
408            Py_DECREF(list);
409            list = NULL;
410            break;
411        }
412        Py_DECREF(str);
413    }
414    /* XXX Shouldn't we free the list of maps now? */
415    return list;
416}
417
418static PyMethodDef nis_methods[] = {
419    {"match",                   (PyCFunction)nis_match,
420                                    METH_VARARGS | METH_KEYWORDS,
421                                    match__doc__},
422    {"cat",                     (PyCFunction)nis_cat,
423                                    METH_VARARGS | METH_KEYWORDS,
424                                    cat__doc__},
425    {"maps",                    (PyCFunction)nis_maps,
426                                    METH_VARARGS | METH_KEYWORDS,
427                                    maps__doc__},
428    {"get_default_domain",      (PyCFunction)nis_get_default_domain,
429                                    METH_NOARGS,
430                                    get_default_domain__doc__},
431    {NULL,                      NULL}            /* Sentinel */
432};
433
434PyDoc_STRVAR(nis__doc__,
435"This module contains functions for accessing NIS maps.\n");
436
437void
438initnis (void)
439{
440    PyObject *m, *d;
441    m = Py_InitModule3("nis", nis_methods, nis__doc__);
442    if (m == NULL)
443        return;
444    d = PyModule_GetDict(m);
445    NisError = PyErr_NewException("nis.error", NULL, NULL);
446    if (NisError != NULL)
447        PyDict_SetItemString(d, "error", NisError);
448}
449