nismodule.c 11.7 KB
Newer Older
1 2
/***********************************************************
    Written by:
3 4 5 6 7 8
    Fred Gansevles <Fred.Gansevles@cs.utwente.nl>
    B&O group,
    Faculteit der Informatica,
    Universiteit Twente,
    Enschede,
    the Netherlands.
9 10 11 12
******************************************************************/

/* NIS module implementation */

Barry Warsaw's avatar
Barry Warsaw committed
13
#include "Python.h"
14 15 16 17 18

#include <sys/time.h>
#include <sys/types.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
19
#include <rpcsvc/ypclnt.h>
20

21 22
#ifdef __sgi
/* This is missing from rpcsvc/ypclnt.h */
23
extern int yp_get_default_domain(char **);
24 25
#endif

26
PyDoc_STRVAR(get_default_domain__doc__,
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
"get_default_domain() -> str\n\
Corresponds to the C library yp_get_default_domain() call, returning\n\
the default NIS domain.\n");

PyDoc_STRVAR(match__doc__,
"match(key, map, domain = defaultdomain)\n\
Corresponds to the C library yp_match() call, returning the value of\n\
key in the given map. Optionally domain can be specified but it\n\
defaults to the system default domain.\n");

PyDoc_STRVAR(cat__doc__,
"cat(map, domain = defaultdomain)\n\
Returns the entire map as a dictionary. Optionally domain can be\n\
specified but it defaults to the system default domain.\n");

PyDoc_STRVAR(maps__doc__,
"maps(domain = defaultdomain)\n\
Returns an array of all available NIS maps within a domain. If domain\n\
is not specified it defaults to the system default domain.\n");

Barry Warsaw's avatar
Barry Warsaw committed
47
static PyObject *NisError;
48

Barry Warsaw's avatar
Barry Warsaw committed
49
static PyObject *
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
50
nis_error (int err)
51
{
52 53
    PyErr_SetString(NisError, yperr_string(err));
    return NULL;
54 55
}

56
static struct nis_map {
57 58 59
    char *alias;
    char *map;
    int  fix;
60
} aliases [] = {
61 62 63 64 65 66 67 68 69
    {"passwd",          "passwd.byname",        0},
    {"group",           "group.byname",         0},
    {"networks",        "networks.byaddr",      0},
    {"hosts",           "hosts.byname",         0},
    {"protocols",       "protocols.bynumber",   0},
    {"services",        "services.byname",      0},
    {"aliases",         "mail.aliases",         1}, /* created with 'makedbm -a' */
    {"ethers",          "ethers.byname",        0},
    {0L,                0L,                     0}
70 71 72
};

static char *
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
73
nis_mapname (char *map, int *pfix)
74
{
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    int i;

    *pfix = 0;
    for (i=0; aliases[i].alias != 0L; i++) {
        if (!strcmp (aliases[i].alias, map)) {
            *pfix = aliases[i].fix;
            return aliases[i].map;
        }
        if (!strcmp (aliases[i].map, map)) {
            *pfix = aliases[i].fix;
            return aliases[i].map;
        }
    }

    return map;
90 91
}

92
#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
93 94
typedef int (*foreachfunc)(unsigned long, char *, int, char *, int, void *);
#else
95
typedef int (*foreachfunc)(int, char *, int, char *, int, char *);
96
#endif
97

98
struct ypcallback_data {
99 100 101
    PyObject            *dict;
    int                         fix;
    PyThreadState *state;
102 103
};

104
static int
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
105 106
nis_foreach (int instatus, char *inkey, int inkeylen, char *inval,
             int invallen, struct ypcallback_data *indata)
107
{
108 109 110 111 112 113 114 115 116 117 118 119
    if (instatus == YP_TRUE) {
        PyObject *key;
        PyObject *val;
        int err;

        PyEval_RestoreThread(indata->state);
        if (indata->fix) {
            if (inkeylen > 0 && inkey[inkeylen-1] == '\0')
            inkeylen--;
            if (invallen > 0 && inval[invallen-1] == '\0')
            invallen--;
        }
120 121
        key = PyUnicode_DecodeFSDefaultAndSize(inkey, inkeylen);
        val = PyUnicode_DecodeFSDefaultAndSize(inval, invallen);
122 123 124 125 126
        if (key == NULL || val == NULL) {
            /* XXX error -- don't know how to handle */
            PyErr_Clear();
            Py_XDECREF(key);
            Py_XDECREF(val);
127
            indata->state = PyEval_SaveThread();
128 129 130 131 132 133 134 135 136 137 138 139 140
            return 1;
        }
        err = PyDict_SetItem(indata->dict, key, val);
        Py_DECREF(key);
        Py_DECREF(val);
        if (err != 0)
            PyErr_Clear();
        indata->state = PyEval_SaveThread();
        if (err != 0)
            return 1;
        return 0;
    }
    return 1;
141 142
}

Barry Warsaw's avatar
Barry Warsaw committed
143
static PyObject *
144
nis_get_default_domain (PyObject *self)
145
{
146 147 148
    char *domain;
    int err;
    PyObject *res;
149

150 151
    if ((err = yp_get_default_domain(&domain)) != 0)
        return nis_error(err);
152

153 154
    res = PyUnicode_FromStringAndSize (domain, strlen(domain));
    return res;
155 156 157 158 159
}

static PyObject *
nis_match (PyObject *self, PyObject *args, PyObject *kwdict)
{
160 161
    char *match;
    char *domain = NULL;
162 163
    Py_ssize_t keylen;
    int len;
164 165
    char *key, *map;
    int err;
166
    PyObject *ukey, *bkey, *res;
167 168 169 170
    int fix;
    static char *kwlist[] = {"key", "map", "domain", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwdict,
171 172 173 174 175 176 177
                                     "Us|s:match", kwlist,
                                     &ukey, &map, &domain))
        return NULL;
    if ((bkey = PyUnicode_EncodeFSDefault(ukey)) == NULL)
        return NULL;
    if (PyBytes_AsStringAndSize(bkey, &key, &keylen) == -1) {
        Py_DECREF(bkey);
178
        return NULL;
179 180 181
    }
    if (!domain && ((err = yp_get_default_domain(&domain)) != 0)) {
        Py_DECREF(bkey);
182
        return nis_error(err);
183
    }
184 185 186 187 188 189
    map = nis_mapname (map, &fix);
    if (fix)
        keylen++;
    Py_BEGIN_ALLOW_THREADS
    err = yp_match (domain, map, key, keylen, &match, &len);
    Py_END_ALLOW_THREADS
190
    Py_DECREF(bkey);
191 192 193 194
    if (fix)
        len--;
    if (err != 0)
        return nis_error(err);
195
    res = PyUnicode_DecodeFSDefaultAndSize(match, len);
196 197
    free (match);
    return res;
198 199
}

Barry Warsaw's avatar
Barry Warsaw committed
200
static PyObject *
201
nis_cat (PyObject *self, PyObject *args, PyObject *kwdict)
202
{
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    char *domain = NULL;
    char *map;
    struct ypall_callback cb;
    struct ypcallback_data data;
    PyObject *dict;
    int err;
    static char *kwlist[] = {"map", "domain", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwdict, "s|s:cat",
                                     kwlist, &map, &domain))
        return NULL;
    if (!domain && ((err = yp_get_default_domain(&domain)) != 0))
        return nis_error(err);
    dict = PyDict_New ();
    if (dict == NULL)
        return NULL;
    cb.foreach = (foreachfunc)nis_foreach;
    data.dict = dict;
    map = nis_mapname (map, &data.fix);
    cb.data = (char *)&data;
    data.state = PyEval_SaveThread();
    err = yp_all (domain, map, &cb);
    PyEval_RestoreThread(data.state);
    if (err != 0) {
        Py_DECREF(dict);
        return nis_error(err);
    }
    return dict;
231 232
}

233 234 235
/* These should be u_long on Sun h/w but not on 64-bit h/w.
   This is not portable to machines with 16-bit ints and no prototypes */
#ifndef YPPROC_MAPLIST
236
#define YPPROC_MAPLIST  11
237 238
#endif
#ifndef YPPROG
239
#define YPPROG          100004
240 241
#endif
#ifndef YPVERS
242
#define YPVERS          2
243
#endif
244 245 246 247 248

typedef char *domainname;
typedef char *mapname;

enum nisstat {
249 250 251 252 253 254 255 256 257 258 259
    NIS_TRUE = 1,
    NIS_NOMORE = 2,
    NIS_FALSE = 0,
    NIS_NOMAP = -1,
    NIS_NODOM = -2,
    NIS_NOKEY = -3,
    NIS_BADOP = -4,
    NIS_BADDB = -5,
    NIS_YPERR = -6,
    NIS_BADARGS = -7,
    NIS_VERS = -8
260 261 262 263
};
typedef enum nisstat nisstat;

struct nismaplist {
264 265
    mapname map;
    struct nismaplist *next;
266 267 268 269
};
typedef struct nismaplist nismaplist;

struct nisresp_maplist {
270 271
    nisstat stat;
    nismaplist *maps;
272 273 274 275 276 277 278
};
typedef struct nisresp_maplist nisresp_maplist;

static struct timeval TIMEOUT = { 25, 0 };

static
bool_t
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
279
nis_xdr_domainname(XDR *xdrs, domainname *objp)
280
{
281 282 283 284
    if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) {
        return (FALSE);
    }
    return (TRUE);
285 286 287 288
}

static
bool_t
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
289
nis_xdr_mapname(XDR *xdrs, mapname *objp)
290
{
291 292 293 294
    if (!xdr_string(xdrs, objp, YPMAXMAP)) {
        return (FALSE);
    }
    return (TRUE);
295 296 297 298
}

static
bool_t
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
299
nis_xdr_ypmaplist(XDR *xdrs, nismaplist *objp)
300
{
301 302 303 304 305 306 307 308 309
    if (!nis_xdr_mapname(xdrs, &objp->map)) {
        return (FALSE);
    }
    if (!xdr_pointer(xdrs, (char **)&objp->next,
                     sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
    {
        return (FALSE);
    }
    return (TRUE);
310 311 312 313
}

static
bool_t
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
314
nis_xdr_ypstat(XDR *xdrs, nisstat *objp)
315
{
316 317 318 319
    if (!xdr_enum(xdrs, (enum_t *)objp)) {
        return (FALSE);
    }
    return (TRUE);
320 321 322 323 324
}


static
bool_t
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
325
nis_xdr_ypresp_maplist(XDR *xdrs, nisresp_maplist *objp)
326
{
327 328 329 330 331 332 333 334 335
    if (!nis_xdr_ypstat(xdrs, &objp->stat)) {
        return (FALSE);
    }
    if (!xdr_pointer(xdrs, (char **)&objp->maps,
                     sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist))
    {
        return (FALSE);
    }
    return (TRUE);
336 337 338 339 340
}


static
nisresp_maplist *
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
341
nisproc_maplist_2(domainname *argp, CLIENT *clnt)
342
{
343 344 345 346 347 348 349 350 351 352 353
    static nisresp_maplist res;

    memset(&res, 0, sizeof(res));
    if (clnt_call(clnt, YPPROC_MAPLIST,
                  (xdrproc_t)nis_xdr_domainname, (caddr_t)argp,
                  (xdrproc_t)nis_xdr_ypresp_maplist, (caddr_t)&res,
                  TIMEOUT) != RPC_SUCCESS)
    {
        return (NULL);
    }
    return (&res);
354 355 356 357
}

static
nismaplist *
358
nis_maplist (char *dom)
359
{
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
    nisresp_maplist *list;
    CLIENT *cl;
    char *server = NULL;
    int mapi = 0;

    while (!server && aliases[mapi].map != 0L) {
        yp_master (dom, aliases[mapi].map, &server);
        mapi++;
    }
    if (!server) {
        PyErr_SetString(NisError, "No NIS master found for any map");
        return NULL;
    }
    cl = clnt_create(server, YPPROG, YPVERS, "tcp");
    if (cl == NULL) {
        PyErr_SetString(NisError, clnt_spcreateerror(server));
        goto finally;
    }
    list = nisproc_maplist_2 (&dom, cl);
    clnt_destroy(cl);
    if (list == NULL)
        goto finally;
    if (list->stat != NIS_TRUE)
        goto finally;

    free(server);
    return list->maps;
387 388

  finally:
389 390
    free(server);
    return NULL;
391 392
}

Barry Warsaw's avatar
Barry Warsaw committed
393
static PyObject *
394
nis_maps (PyObject *self, PyObject *args, PyObject *kwdict)
395
{
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    char *domain = NULL;
    nismaplist *maps;
    PyObject *list;
    int err;
    static char *kwlist[] = {"domain", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwdict,
                                     "|s:maps", kwlist, &domain))
        return NULL;
    if (!domain && ((err = yp_get_default_domain (&domain)) != 0)) {
        nis_error(err);
        return NULL;
    }

    if ((maps = nis_maplist (domain)) == NULL)
        return NULL;
    if ((list = PyList_New(0)) == NULL)
        return NULL;
414
    for (; maps; maps = maps->next) {
415 416 417 418 419 420 421 422 423 424 425
        PyObject *str = PyUnicode_FromString(maps->map);
        if (!str || PyList_Append(list, str) < 0)
        {
            Py_DECREF(list);
            list = NULL;
            break;
        }
        Py_DECREF(str);
    }
    /* XXX Shouldn't we free the list of maps now? */
    return list;
426 427
}

Barry Warsaw's avatar
Barry Warsaw committed
428
static PyMethodDef nis_methods[] = {
429 430 431 432 433 434 435 436 437 438 439 440 441
    {"match",                   (PyCFunction)nis_match,
                                    METH_VARARGS | METH_KEYWORDS,
                                    match__doc__},
    {"cat",                     (PyCFunction)nis_cat,
                                    METH_VARARGS | METH_KEYWORDS,
                                    cat__doc__},
    {"maps",                    (PyCFunction)nis_maps,
                                    METH_VARARGS | METH_KEYWORDS,
                                    maps__doc__},
    {"get_default_domain",      (PyCFunction)nis_get_default_domain,
                                    METH_NOARGS,
                                    get_default_domain__doc__},
    {NULL,                      NULL}            /* Sentinel */
442 443
};

444 445 446
PyDoc_STRVAR(nis__doc__,
"This module contains functions for accessing NIS maps.\n");

447
static struct PyModuleDef nismodule = {
448 449 450 451 452 453 454 455 456
    PyModuleDef_HEAD_INIT,
    "nis",
    nis__doc__,
    -1,
    nis_methods,
    NULL,
    NULL,
    NULL,
    NULL
457 458 459 460
};

PyObject*
PyInit_nis (void)
461
{
462 463 464 465 466 467 468 469 470
    PyObject *m, *d;
    m = PyModule_Create(&nismodule);
    if (m == NULL)
        return NULL;
    d = PyModule_GetDict(m);
    NisError = PyErr_NewException("nis.error", NULL, NULL);
    if (NisError != NULL)
        PyDict_SetItemString(d, "error", NisError);
    return m;
471
}