Home | History | Annotate | Download | only in Modules
      1 /***********************************************************
      2     Written by:
      3     Fred Gansevles <Fred.Gansevles (at) 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 */
     23 extern int yp_get_default_domain(char **);
     24 #endif
     25 
     26 PyDoc_STRVAR(get_default_domain__doc__,
     27 "get_default_domain() -> str\n\
     28 Corresponds to the C library yp_get_default_domain() call, returning\n\
     29 the default NIS domain.\n");
     30 
     31 PyDoc_STRVAR(match__doc__,
     32 "match(key, map, domain = defaultdomain)\n\
     33 Corresponds to the C library yp_match() call, returning the value of\n\
     34 key in the given map. Optionally domain can be specified but it\n\
     35 defaults to the system default domain.\n");
     36 
     37 PyDoc_STRVAR(cat__doc__,
     38 "cat(map, domain = defaultdomain)\n\
     39 Returns the entire map as a dictionary. Optionally domain can be\n\
     40 specified but it defaults to the system default domain.\n");
     41 
     42 PyDoc_STRVAR(maps__doc__,
     43 "maps(domain = defaultdomain)\n\
     44 Returns an array of all available NIS maps within a domain. If domain\n\
     45 is not specified it defaults to the system default domain.\n");
     46 
     47 static PyObject *NisError;
     48 
     49 static PyObject *
     50 nis_error (int err)
     51 {
     52     PyErr_SetString(NisError, yperr_string(err));
     53     return NULL;
     54 }
     55 
     56 static 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 
     72 static char *
     73 nis_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__)
     93 typedef int (*foreachfunc)(unsigned long, char *, int, char *, int, void *);
     94 #else
     95 typedef int (*foreachfunc)(int, char *, int, char *, int, char *);
     96 #endif
     97 
     98 struct ypcallback_data {
     99     PyObject            *dict;
    100     int                         fix;
    101     PyThreadState *state;
    102 };
    103 
    104 static int
    105 nis_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 
    143 static PyObject *
    144 nis_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 
    157 static PyObject *
    158 nis_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 
    190 static PyObject *
    191 nis_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 
    235 typedef char *domainname;
    236 typedef char *mapname;
    237 
    238 enum 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 };
    251 typedef enum nisstat nisstat;
    252 
    253 struct nismaplist {
    254     mapname map;
    255     struct nismaplist *next;
    256 };
    257 typedef struct nismaplist nismaplist;
    258 
    259 struct nisresp_maplist {
    260     nisstat stat;
    261     nismaplist *maps;
    262 };
    263 typedef struct nisresp_maplist nisresp_maplist;
    264 
    265 static struct timeval TIMEOUT = { 25, 0 };
    266 
    267 static
    268 bool_t
    269 nis_xdr_domainname(XDR *xdrs, domainname *objp)
    270 {
    271     if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) {
    272         return (FALSE);
    273     }
    274     return (TRUE);
    275 }
    276 
    277 static
    278 bool_t
    279 nis_xdr_mapname(XDR *xdrs, mapname *objp)
    280 {
    281     if (!xdr_string(xdrs, objp, YPMAXMAP)) {
    282         return (FALSE);
    283     }
    284     return (TRUE);
    285 }
    286 
    287 static
    288 bool_t
    289 nis_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 
    302 static
    303 bool_t
    304 nis_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 
    313 static
    314 bool_t
    315 nis_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 
    329 static
    330 nisresp_maplist *
    331 nisproc_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 
    346 static
    347 nismaplist *
    348 nis_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 
    383 static PyObject *
    384 nis_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 
    418 static 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 
    434 PyDoc_STRVAR(nis__doc__,
    435 "This module contains functions for accessing NIS maps.\n");
    436 
    437 void
    438 initnis (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