#include <autoconf.h>
#if HAVE_PYTHON_H
#include <Python.h>
#elif HAVE_PYTHON2_3_PYTHON_H
#include <python2.3/Python.h>
#else
#error "Where's the Python header file?"
#endif
#include <errno.h>
#include "k5-platform.h"
#include "fake-addrinfo.h"
#include <krb5/locate_plugin.h>
#define LIBDIR "/tmp"
#define SCRIPT_PATH LIBDIR "/krb5/locate-service.py"
#define LOOKUP_FUNC_NAME "locate"
static PyObject *locatefn;
MAKE_INIT_FUNCTION(my_init);
MAKE_FINI_FUNCTION(my_fini);
#define F (strchr(__FILE__, '/') ? 1 + strrchr(__FILE__, '/') : __FILE__)
static krb5_context sctx;
int
my_init (void)
{
PyObject *mainmodule;
FILE *f;
Py_Initialize ();
f = fopen(SCRIPT_PATH, "r");
if (f == NULL) {
if (sctx)
krb5_set_error_message(sctx, -1,
"couldn't open Python script %s (%s)",
SCRIPT_PATH, strerror(errno));
return -1;
}
set_cloexec_file(f);
PyRun_SimpleFile (f, SCRIPT_PATH);
fclose(f);
mainmodule = PyModule_GetDict(PyImport_AddModule("__main__"));
if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
locatefn = PyDict_GetItemString (mainmodule, LOOKUP_FUNC_NAME);
if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
if (locatefn == 0)
return -1;
if (!PyCallable_Check (locatefn)) {
Py_DECREF (locatefn);
locatefn = 0;
return -1;
}
if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
return 0;
}
void
my_fini (void)
{
if (! INITIALIZER_RAN (my_init))
return;
Py_DECREF (locatefn);
locatefn = 0;
Py_Finalize ();
}
static krb5_error_code
ctxinit (krb5_context ctx, void **blobptr)
{
*blobptr = ctx;
return 0;
}
static void
ctxfini (void *blob)
{
}
static krb5_error_code
lookup (void *blob, enum locate_service_type svc, const char *realm,
int socktype, int family,
int (*cbfunc)(void *, int, struct sockaddr *), void *cbdata)
{
PyObject *py_result, *svcarg, *realmarg, *arglist;
int listsize, i, x;
struct addrinfo aihints, *airesult;
int thissocktype;
sctx = blob;
i = CALL_INIT_FUNCTION (my_init);
if (i) {
#if 0
fprintf(stderr, "%s:%d: module initialization failed\n", F, __LINE__);
#endif
return i;
}
if (locatefn == 0)
return KRB5_PLUGIN_NO_HANDLE;
svcarg = PyInt_FromLong (svc);
realmarg = PyString_FromString ((char *) realm);
arglist = PyTuple_New (4);
PyTuple_SetItem (arglist, 0, svcarg);
PyTuple_SetItem (arglist, 1, realmarg);
PyTuple_SetItem (arglist, 2, PyInt_FromLong (socktype));
PyTuple_SetItem (arglist, 3, PyInt_FromLong (family));
py_result = PyObject_CallObject (locatefn, arglist);
Py_DECREF (arglist);
if (PyErr_Occurred()) {
fprintf(stderr,"%s:%d: python error\n", F, __LINE__);
PyErr_Print();
krb5_set_error_message(blob, -1,
"Python evaluation error, see stderr");
return -1;
}
if (py_result == 0) {
fprintf(stderr, "%s:%d: returned null object\n", F, __LINE__);
return -1;
}
if (py_result == Py_False)
return KRB5_PLUGIN_NO_HANDLE;
if (! PyList_Check (py_result)) {
Py_DECREF (py_result);
fprintf(stderr, "%s:%d: returned non-list, non-False\n", F, __LINE__);
krb5_set_error_message(blob, -1,
"Python script error -- returned non-list, non-False result");
return -1;
}
listsize = PyList_Size (py_result);
memset(&aihints, 0, sizeof(aihints));
aihints.ai_flags = AI_NUMERICHOST;
aihints.ai_family = family;
for (i = 0; i < listsize; i++) {
PyObject *answer, *field;
char *hoststr, *portstr, portbuf[3*sizeof(long) + 4];
int cbret;
answer = PyList_GetItem (py_result, i);
if (! PyTuple_Check (answer)) {
krb5_set_error_message(blob, -1,
"Python script error -- returned item %d not a tuple", i);
return -1;
}
if (PyTuple_Size (answer) != 3) {
krb5_set_error_message(blob, -1,
"Python script error -- returned tuple %d size %d should be 3",
i, PyTuple_Size (answer));
return -1;
}
field = PyTuple_GetItem (answer, 0);
if (! PyString_Check (field)) {
krb5_set_error_message(blob, -1,
"Python script error -- first component of tuple %d is not a string",
i);
return -1;
}
hoststr = PyString_AsString (field);
field = PyTuple_GetItem (answer, 1);
if (PyString_Check (field)) {
portstr = PyString_AsString (field);
} else if (PyInt_Check (field)) {
sprintf(portbuf, "%ld", PyInt_AsLong (field));
portstr = portbuf;
} else {
krb5_set_error_message(blob, -1,
"Python script error -- second component of tuple %d neither a string nor an integer",
i);
return -1;
}
field = PyTuple_GetItem (answer, 2);
if (! PyInt_Check (field)) {
krb5_set_error_message(blob, -1,
"Python script error -- third component of tuple %d not an integer",
i);
return -1;
}
thissocktype = PyInt_AsLong (field);
switch (thissocktype) {
case SOCK_STREAM:
case SOCK_DGRAM:
if (socktype != 0 && socktype != thissocktype) {
krb5_set_error_message(blob, -1,
"Python script error -- tuple %d has socket type %d, should only have %d",
i, thissocktype, socktype);
return -1;
}
break;
default:
krb5_set_error_message(blob, -1,
"Python script error -- tuple %d has invalid socket type %d",
i, thissocktype);
return -1;
}
aihints.ai_socktype = thissocktype;
x = getaddrinfo (hoststr, portstr, &aihints, &airesult);
if (x != 0)
continue;
cbret = cbfunc(cbdata, airesult->ai_socktype, airesult->ai_addr);
freeaddrinfo(airesult);
if (cbret != 0)
break;
}
Py_DECREF (py_result);
return 0;
}
const krb5plugin_service_locate_ftable service_locator = {
0,
ctxinit, ctxfini, lookup,
};