/* * Copyright (c) 2016 Stanislav Yudin * Copyright (c) 2017 Joris Vink * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "kore.h" #if !defined(KORE_NO_HTTP) #include "http.h" #endif #include "python_api.h" #include "python_methods.h" static PyMODINIT_FUNC python_module_init(void); static PyObject *python_import(const char *); static void python_log_error(const char *); static PyObject *pyconnection_alloc(struct connection *); static PyObject *python_callable(PyObject *, const char *); #if !defined(KORE_NO_HTTP) static PyObject *pyhttp_request_alloc(struct http_request *); #endif static void python_append_path(const char *); static void python_push_integer(PyObject *, const char *, long); static void python_push_type(const char *, PyObject *, PyTypeObject *); #if !defined(KORE_NO_HTTP) static int python_runtime_http_request(void *, struct http_request *); static int python_runtime_validator(void *, struct http_request *, void *); #endif static int python_runtime_onload(void *, int); static void python_runtime_connect(void *, struct connection *); static void python_module_free(struct kore_module *); static void python_module_reload(struct kore_module *); static void python_module_load(struct kore_module *, const char *); static void *python_module_getsym(struct kore_module *, const char *); struct kore_module_functions kore_python_module = { .free = python_module_free, .load = python_module_load, .getsym = python_module_getsym, .reload = python_module_reload }; struct kore_runtime kore_python_runtime = { KORE_RUNTIME_PYTHON, #if !defined(KORE_NO_HTTP) .http_request = python_runtime_http_request, .validator = python_runtime_validator, #endif .onload = python_runtime_onload, .connect = python_runtime_connect }; void kore_python_init(void) { if (PyImport_AppendInittab("kore", &python_module_init) == -1) fatal("kore_python_init: failed to add new module"); Py_Initialize(); } void kore_python_cleanup(void) { if (Py_IsInitialized()) Py_Finalize(); } static void python_log_error(const char *function) { PyObject *type, *value, *traceback; if (!PyErr_Occurred()) return; PyErr_Fetch(&type, &value, &traceback); if (type == NULL || value == NULL || traceback == NULL) fatal("python_log_error(): exception but no error trace?"); kore_log(LOG_ERR, "python exception in '%s' - type:%s - value:%s - trace:%s", function, PyUnicode_AsUTF8AndSize(type, NULL), PyUnicode_AsUTF8AndSize(value, NULL), PyUnicode_AsUTF8AndSize(traceback, NULL)); Py_DECREF(type); Py_DECREF(value); Py_DECREF(traceback); } static void python_module_free(struct kore_module *module) { kore_free(module->path); Py_DECREF(module->handle); kore_free(module); } static void python_module_reload(struct kore_module *module) { /* Calls through to kore_python_module_load() below. */ module->fun->load(module, module->onload); } static void python_module_load(struct kore_module *module, const char *onload) { if (module->handle != NULL) Py_DECREF(module->handle); kore_python_cleanup(); kore_python_init(); module->handle = python_import(module->path); if (module->handle == NULL) fatal("%s: failed to import module", module->path); } static void * python_module_getsym(struct kore_module *module, const char *symbol) { return (python_callable(module->handle, symbol)); } static void pyhttp_dealloc(struct pyhttp_request *pyreq) { printf("pyreq %p goes byebye\n", (void *)pyreq); PyObject_Del((PyObject *)pyreq); } static void pyconnection_dealloc(struct pyconnection *pyc) { printf("pyc %p goes byebye\n", (void *)pyc); PyObject_Del((PyObject *)pyc); } #if !defined(KORE_NO_HTTP) static int python_runtime_http_request(void *addr, struct http_request *req) { int ret; PyObject *pyret, *pyreq, *args, *callable; callable = (PyObject *)addr; pyreq = pyhttp_request_alloc(req); if (pyreq == NULL) { kore_log(LOG_ERR, "cannot create new pyhttp_request"); http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0); return (KORE_RESULT_OK); } if ((args = PyTuple_New(1)) == NULL) fatal("python_runtime_http_request: PyTuple_New failed"); printf(" args tuple: %p\n", (void *)args); if (PyTuple_SetItem(args, 0, pyreq) != 0) fatal("python_runtime_http_request: PyTuple_SetItem failed"); PyErr_Clear(); pyret = PyObject_Call(callable, args, NULL); Py_DECREF(args); if (pyret == NULL) { Py_XDECREF(req->py_object); python_log_error("python_runtime_http_request"); http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0); return (KORE_RESULT_OK); } if (!PyLong_Check(pyret)) fatal("python_runtime_http_request: unexpected return type"); ret = (int)PyLong_AsLong(pyret); if (ret != KORE_RESULT_RETRY) Py_XDECREF(req->py_object); Py_DECREF(pyret); return (ret); } static int python_runtime_validator(void *addr, struct http_request *req, void *data) { printf("python_runtime_validator: XXX %s\n", req->path); return (KORE_RESULT_OK); } #endif static int python_runtime_onload(void *addr, int action) { int ret; PyObject *pyret, *args, *pyact, *callable; callable = (PyObject *)addr; if ((pyact = PyLong_FromLong(action)) == NULL) fatal("python_runtime_onload: PyLong_FromLong failed"); if ((args = PyTuple_New(1)) == NULL) fatal("python_runtime_onload: PyTuple_New failed"); if (PyTuple_SetItem(args, 0, pyact) != 0) fatal("python_runtime_onload: PyTuple_SetItem failed"); pyret = PyObject_Call(callable, args, NULL); Py_DECREF(args); if (pyret == NULL) { python_log_error("python_runtime_onload"); return (KORE_RESULT_ERROR); } if (!PyLong_Check(pyret)) fatal("python_runtime_onload: unexpected return type"); ret = (int)PyLong_AsLong(pyret); Py_DECREF(pyret); return (ret); } static void python_runtime_connect(void *addr, struct connection *c) { PyObject *pyc, *pyret, *args, *callable; callable = (PyObject *)addr; if ((pyc = pyconnection_alloc(c)) == NULL) { kore_log(LOG_ERR, "cannot create new pyconnection"); kore_connection_disconnect(c); return; } if ((args = PyTuple_New(1)) == NULL) fatal("python_runtime_connect: PyTuple_New failed"); if (PyTuple_SetItem(args, 0, pyc) != 0) fatal("python_runtime_connect: PyTuple_SetItem failed"); pyret = PyObject_Call(callable, args, NULL); Py_DECREF(args); if (pyret == NULL) { python_log_error("python_runtime_connect"); kore_connection_disconnect(c); } Py_DECREF(pyret); } static PyMODINIT_FUNC python_module_init(void) { PyObject *pykore; if ((pykore = PyModule_Create(&pykore_module)) == NULL) fatal("python_module_init: failed to setup pykore module"); python_push_type("pyconnection", pykore, &pyconnection_type); python_push_integer(pykore, "RESULT_OK", KORE_RESULT_OK); python_push_integer(pykore, "RESULT_RETRY", KORE_RESULT_RETRY); python_push_integer(pykore, "RESULT_ERROR", KORE_RESULT_ERROR); python_push_integer(pykore, "MODULE_LOAD", KORE_MODULE_LOAD); python_push_integer(pykore, "MODULE_UNLOAD", KORE_MODULE_UNLOAD); python_push_integer(pykore, "LOG_ERR", LOG_ERR); python_push_integer(pykore, "LOG_INFO", LOG_INFO); python_push_integer(pykore, "LOG_NOTICE", LOG_NOTICE); #if !defined(KORE_NO_HTTP) python_push_type("pyhttp_request", pykore, &pyhttp_request_type); python_push_integer(pykore, "METHOD_GET", HTTP_METHOD_GET); python_push_integer(pykore, "METHOD_PUT", HTTP_METHOD_PUT); python_push_integer(pykore, "METHOD_HEAD", HTTP_METHOD_HEAD); python_push_integer(pykore, "METHOD_POST", HTTP_METHOD_POST); python_push_integer(pykore, "METHOD_DELETE", HTTP_METHOD_DELETE); python_push_integer(pykore, "WEBSOCKET_BROADCAST_LOCAL", WEBSOCKET_BROADCAST_LOCAL); python_push_integer(pykore, "WEBSOCKET_BROADCAST_GLOBAL", WEBSOCKET_BROADCAST_GLOBAL); #endif return (pykore); } static void python_append_path(const char *path) { PyObject *mpath, *spath; if ((mpath = PyUnicode_FromString(path)) == NULL) fatal("python_append_path: PyUnicode_FromString failed"); if ((spath = PySys_GetObject("path")) == NULL) fatal("python_append_path: PySys_GetObject failed"); PyList_Append(spath, mpath); Py_DECREF(mpath); } static void python_push_type(const char *name, PyObject *module, PyTypeObject *type) { if (PyType_Ready(type) == -1) fatal("python_push_type: failed to ready %s", name); Py_INCREF(type); if (PyModule_AddObject(module, name, (PyObject *)type) == -1) fatal("python_push_type: failed to push %s", name); } static void python_push_integer(PyObject *module, const char *name, long value) { int ret; if ((ret = PyModule_AddIntConstant(module, name, value)) == -1) fatal("python_push_integer: failed to add %s", name); } static PyObject * python_exported_log(PyObject *self, PyObject *args) { int prio; const char *message; if (!PyArg_ParseTuple(args, "is", &prio, &message)) { PyErr_SetString(PyExc_TypeError, "invalid parameters"); return (NULL); } kore_log(prio, "%s", message); Py_RETURN_TRUE; } static PyObject * python_import(const char *path) { PyObject *module; char *dir, *file, *copy, *p; copy = kore_strdup(path); if ((file = basename(copy)) == NULL) fatal("basename: %s: %s", path, errno_s); if ((dir = dirname(copy)) == NULL) fatal("dirname: %s: %s", path, errno_s); if ((p = strrchr(file, '.')) != NULL) *p = '\0'; python_append_path(dir); module = PyImport_ImportModule(file); if (module == NULL) PyErr_Print(); kore_free(copy); return (module); } static PyObject * python_callable(PyObject *module, const char *symbol) { PyObject *obj; if ((obj = PyObject_GetAttrString(module, symbol)) == NULL) return (NULL); if (!PyCallable_Check(obj)) { Py_DECREF(obj); return (NULL); } return (obj); } static PyObject * pyconnection_alloc(struct connection *c) { struct pyconnection *pyc; pyc = PyObject_New(struct pyconnection, &pyconnection_type); if (pyc == NULL) return (NULL); printf(" pyc: %p\n", (void *)pyc); pyc->c = c; return ((PyObject *)pyc); } #if !defined(KORE_NO_HTTP) static PyObject * pyhttp_request_alloc(struct http_request *req) { struct pyhttp_request *pyreq; pyreq = PyObject_New(struct pyhttp_request, &pyhttp_request_type); if (pyreq == NULL) return (NULL); pyreq->req = req; return ((PyObject *)pyreq); } static PyObject * pyhttp_response(struct pyhttp_request *pyreq, PyObject *args) { Py_buffer body; int status; if (!PyArg_ParseTuple(args, "iy*", &status, &body)) { PyErr_SetString(PyExc_TypeError, "invalid parameters"); return (NULL); } http_response(pyreq->req, status, body.buf, body.len); PyBuffer_Release(&body); Py_RETURN_TRUE; } static PyObject * pyhttp_response_header(struct pyhttp_request *pyreq, PyObject *args) { const char *header, *value; if (!PyArg_ParseTuple(args, "ss", &header, &value)) { PyErr_SetString(PyExc_TypeError, "invalid parameters"); return (NULL); } http_response_header(pyreq->req, header, value); Py_RETURN_TRUE; } static PyObject * pyhttp_request_header(struct pyhttp_request *pyreq, PyObject *args) { char *value; const char *header; PyObject *result; if (!PyArg_ParseTuple(args, "s", &header)) { PyErr_SetString(PyExc_TypeError, "invalid parameters"); return (NULL); } if (!http_request_header(pyreq->req, header, &value)) { Py_RETURN_NONE; } if ((result = PyUnicode_FromString(value)) == NULL) return (PyErr_NoMemory()); return (result); } static PyObject * pyhttp_body_read(struct pyhttp_request *pyreq, PyObject *args) { ssize_t ret; size_t len; Py_ssize_t pylen; PyObject *result; u_int8_t buf[1024]; if (!PyArg_ParseTuple(args, "n", &pylen) || pylen < 0) { PyErr_SetString(PyExc_TypeError, "invalid parameters"); return (NULL); } len = (size_t)pylen; if (len > sizeof(buf)) { PyErr_SetString(PyExc_RuntimeError, "len > sizeof(buf)"); return (NULL); } ret = http_body_read(pyreq->req, buf, len); if (ret == -1) { PyErr_SetString(PyExc_RuntimeError, "http_body_read() failed"); return (NULL); } if (ret > INT_MAX) { PyErr_SetString(PyExc_RuntimeError, "ret > INT_MAX"); return (NULL); } result = Py_BuildValue("ny#", ret, buf, (int)ret); if (result == NULL) return (PyErr_NoMemory()); return (result); } static PyObject * pyhttp_populate_get(struct pyhttp_request *pyreq, PyObject *args) { http_populate_get(pyreq->req); Py_RETURN_TRUE; } static PyObject * pyhttp_populate_post(struct pyhttp_request *pyreq, PyObject *args) { http_populate_post(pyreq->req); Py_RETURN_TRUE; } static PyObject * pyhttp_get_host(struct pyhttp_request *pyreq, void *closure) { PyObject *host; if ((host = PyUnicode_FromString(pyreq->req->host)) == NULL) return (PyErr_NoMemory()); return (host); } static PyObject * pyhttp_get_path(struct pyhttp_request *pyreq, void *closure) { PyObject *path; if ((path = PyUnicode_FromString(pyreq->req->path)) == NULL) return (PyErr_NoMemory()); return (path); } static PyObject * pyhttp_get_body(struct pyhttp_request *pyreq, void *closure) { ssize_t ret; struct kore_buf buf; PyObject *body; u_int8_t data[BUFSIZ]; kore_buf_init(&buf, 1024); for (;;) { ret = http_body_read(pyreq->req, data, sizeof(data)); if (ret == -1) { kore_buf_cleanup(&buf); PyErr_SetString(PyExc_RuntimeError, "http_body_read() failed"); return (NULL); } if (ret == 0) break; kore_buf_append(&buf, data, (size_t)ret); } body = PyBytes_FromStringAndSize((char *)buf.data, buf.offset); kore_buf_free(&buf); if (body == NULL) return (PyErr_NoMemory()); return (body); } static PyObject * pyhttp_get_agent(struct pyhttp_request *pyreq, void *closure) { PyObject *agent; if (pyreq->req->agent == NULL) { Py_RETURN_NONE; } if ((agent = PyUnicode_FromString(pyreq->req->path)) == NULL) return (PyErr_NoMemory()); return (agent); } static int pyhttp_set_state(struct pyhttp_request *pyreq, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, "pyhttp_set_state: value is NULL"); return (-1); } Py_XDECREF(pyreq->req->py_object); pyreq->req->py_object = value; Py_INCREF(pyreq->req->py_object); return (0); } static PyObject * pyhttp_get_state(struct pyhttp_request *pyreq, void *closure) { if (pyreq->req->py_object == NULL) Py_RETURN_NONE; Py_INCREF(pyreq->req->py_object); return (pyreq->req->py_object); } static PyObject * pyhttp_get_method(struct pyhttp_request *pyreq, void *closure) { PyObject *method; if ((method = PyLong_FromUnsignedLong(pyreq->req->method)) == NULL) return (PyErr_NoMemory()); return (method); } static PyObject * pyhttp_get_connection(struct pyhttp_request *pyreq, void *closure) { PyObject *pyc; if ((pyc = pyconnection_alloc(pyreq->req->owner)) == NULL) return (PyErr_NoMemory()); return (pyc); } #endif