13b6c3722Schristos /*
23b6c3722Schristos * pythonmod.c: unbound module C wrapper
33b6c3722Schristos *
43b6c3722Schristos * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
53b6c3722Schristos * Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
63b6c3722Schristos *
73b6c3722Schristos * This software is open source.
83b6c3722Schristos *
93b6c3722Schristos * Redistribution and use in source and binary forms, with or without
103b6c3722Schristos * modification, are permitted provided that the following conditions
113b6c3722Schristos * are met:
123b6c3722Schristos *
133b6c3722Schristos * * Redistributions of source code must retain the above copyright notice,
143b6c3722Schristos * this list of conditions and the following disclaimer.
153b6c3722Schristos *
163b6c3722Schristos * * Redistributions in binary form must reproduce the above copyright notice,
173b6c3722Schristos * this list of conditions and the following disclaimer in the documentation
183b6c3722Schristos * and/or other materials provided with the distribution.
193b6c3722Schristos *
203b6c3722Schristos * * Neither the name of the organization nor the names of its
213b6c3722Schristos * contributors may be used to endorse or promote products derived from this
223b6c3722Schristos * software without specific prior written permission.
233b6c3722Schristos *
243b6c3722Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
253b6c3722Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
263b6c3722Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
273b6c3722Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
283b6c3722Schristos * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
293b6c3722Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
303b6c3722Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
313b6c3722Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
323b6c3722Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
333b6c3722Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
343b6c3722Schristos * POSSIBILITY OF SUCH DAMAGE.
353b6c3722Schristos */
363b6c3722Schristos /**
373b6c3722Schristos * \file
383b6c3722Schristos * Python module for unbound. Calls python script.
393b6c3722Schristos */
403b6c3722Schristos
413b6c3722Schristos /* ignore the varargs unused warning from SWIGs internal vararg support */
423b6c3722Schristos #ifdef __GNUC__
433b6c3722Schristos #pragma GCC diagnostic ignored "-Wunused-parameter"
440cd9f4ecSchristos #ifndef __clang__
453b6c3722Schristos #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
463b6c3722Schristos #endif
470cd9f4ecSchristos #endif
483b6c3722Schristos
493b6c3722Schristos #include "config.h"
503b6c3722Schristos #include "sldns/sbuffer.h"
513b6c3722Schristos
523b6c3722Schristos #undef _POSIX_C_SOURCE
533b6c3722Schristos #undef _XOPEN_SOURCE
543b6c3722Schristos #include <Python.h>
553b6c3722Schristos
563b6c3722Schristos #include "pythonmod/pythonmod.h"
573b6c3722Schristos #include "util/module.h"
583b6c3722Schristos #include "util/config_file.h"
593b6c3722Schristos #include "pythonmod_utils.h"
603b6c3722Schristos
613b6c3722Schristos #ifdef S_SPLINT_S
623b6c3722Schristos typedef struct PyObject PyObject;
633b6c3722Schristos typedef struct PyThreadState PyThreadState;
643b6c3722Schristos typedef void* PyGILState_STATE;
653b6c3722Schristos #endif
663b6c3722Schristos
673b6c3722Schristos /**
6801049ae6Schristos * counter for python module instances
6901049ae6Schristos * incremented by pythonmod_init(...)
7001049ae6Schristos */
7101049ae6Schristos int py_mod_count = 0;
7201049ae6Schristos
7301049ae6Schristos /** Python main thread */
7401049ae6Schristos PyThreadState* mainthr;
7501049ae6Schristos
7601049ae6Schristos /**
773b6c3722Schristos * Global state for the module.
783b6c3722Schristos */
793b6c3722Schristos struct pythonmod_env {
803b6c3722Schristos
813b6c3722Schristos /** Python script filename. */
823b6c3722Schristos const char* fname;
833b6c3722Schristos
843b6c3722Schristos /** Python module. */
853b6c3722Schristos PyObject* module;
863b6c3722Schristos
873b6c3722Schristos /** Module init function */
883b6c3722Schristos PyObject* func_init;
893b6c3722Schristos /** Module deinit function */
903b6c3722Schristos PyObject* func_deinit;
913b6c3722Schristos /** Module operate function */
923b6c3722Schristos PyObject* func_operate;
933b6c3722Schristos /** Module super_inform function */
943b6c3722Schristos PyObject* func_inform;
953b6c3722Schristos
963b6c3722Schristos /** Python dictionary. */
973b6c3722Schristos PyObject* dict;
983b6c3722Schristos
993b6c3722Schristos /** Module data. */
1003b6c3722Schristos PyObject* data;
1013b6c3722Schristos
1023b6c3722Schristos /** Module qstate. */
1033b6c3722Schristos struct module_qstate* qstate;
1043b6c3722Schristos };
1053b6c3722Schristos
1063b6c3722Schristos /**
1073b6c3722Schristos * Per query state for the iterator module.
1083b6c3722Schristos */
1093b6c3722Schristos struct pythonmod_qstate {
1103b6c3722Schristos
1113b6c3722Schristos /** Module per query data. */
1123b6c3722Schristos PyObject* data;
1133b6c3722Schristos };
1143b6c3722Schristos
115*91f7d55fSchristos /* The dict from __main__ could have remnants from a previous script
116*91f7d55fSchristos * invocation, in a multi python module setup. Usually this is fine since newer
117*91f7d55fSchristos * scripts will update their values. The obvious erroneous case is when mixing
118*91f7d55fSchristos * python scripts that make use of both 'init' and 'init_standard'. This
119*91f7d55fSchristos * results in 'init_standard' to persist on following scripts that don't use it
120*91f7d55fSchristos * (thus not replacing it). This is also problematic in case where a script
121*91f7d55fSchristos * does not define a required function but a previously loaded script did. The
122*91f7d55fSchristos * current solution is to make sure to clean offensive remnants that influence
123*91f7d55fSchristos * further parsing of the individual scripts.
124*91f7d55fSchristos */
125*91f7d55fSchristos static void
clean_python_function_objects(PyObject * dict)126*91f7d55fSchristos clean_python_function_objects(PyObject* dict) {
127*91f7d55fSchristos const char* function_names[] = {
128*91f7d55fSchristos "init",
129*91f7d55fSchristos "init_standard",
130*91f7d55fSchristos "deinit",
131*91f7d55fSchristos "operate",
132*91f7d55fSchristos "inform_super"
133*91f7d55fSchristos };
134*91f7d55fSchristos size_t i;
135*91f7d55fSchristos
136*91f7d55fSchristos for(i=0; i<sizeof(function_names)/sizeof(function_names[0]); i++) {
137*91f7d55fSchristos if(PyDict_GetItemString(dict, function_names[i]) != NULL) {
138*91f7d55fSchristos PyDict_DelItemString(dict, function_names[i]);
139*91f7d55fSchristos }
140*91f7d55fSchristos }
141*91f7d55fSchristos };
142*91f7d55fSchristos
1433b6c3722Schristos /* Generated */
1443b6c3722Schristos #ifndef S_SPLINT_S
1453b6c3722Schristos #include "pythonmod/interface.h"
1463b6c3722Schristos #endif
1473b6c3722Schristos
148f42d8de7Schristos /** log python error */
149f42d8de7Schristos static void
log_py_err(void)150f42d8de7Schristos log_py_err(void)
151f42d8de7Schristos {
152f42d8de7Schristos char *result = NULL;
153f42d8de7Schristos const char* iomod = "cStringIO";
154f42d8de7Schristos PyObject *modStringIO = NULL;
155f42d8de7Schristos PyObject *modTB = NULL;
156f42d8de7Schristos PyObject *obFuncStringIO = NULL;
157f42d8de7Schristos PyObject *obStringIO = NULL;
158f42d8de7Schristos PyObject *obFuncTB = NULL;
159f42d8de7Schristos PyObject *argsTB = NULL;
160f42d8de7Schristos PyObject *obResult = NULL;
161f42d8de7Schristos PyObject *ascstr = NULL;
162f42d8de7Schristos PyObject *exc_typ, *exc_val, *exc_tb;
163f42d8de7Schristos
164f42d8de7Schristos /* Fetch the error state now before we cruch it */
165f42d8de7Schristos /* exc val contains the error message
166f42d8de7Schristos * exc tb contains stack traceback and other info. */
167f42d8de7Schristos PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
168f42d8de7Schristos PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb);
169f42d8de7Schristos
170f42d8de7Schristos /* Import the modules we need - cStringIO and traceback */
171f42d8de7Schristos modStringIO = PyImport_ImportModule("cStringIO");
172f42d8de7Schristos if (modStringIO==NULL) {
173f42d8de7Schristos /* python 1.4 and before */
174f42d8de7Schristos modStringIO = PyImport_ImportModule("StringIO");
175f42d8de7Schristos iomod = "StringIO";
176f42d8de7Schristos }
177f42d8de7Schristos if (modStringIO==NULL) {
178f42d8de7Schristos /* python 3 */
179f42d8de7Schristos modStringIO = PyImport_ImportModule("io");
180f42d8de7Schristos iomod = "io";
181f42d8de7Schristos }
182f42d8de7Schristos if (modStringIO==NULL) {
183f42d8de7Schristos log_err("pythonmod: cannot print exception, "
184f42d8de7Schristos "cannot ImportModule cStringIO or StringIO or io");
185f42d8de7Schristos goto cleanup;
186f42d8de7Schristos }
187f42d8de7Schristos modTB = PyImport_ImportModule("traceback");
188f42d8de7Schristos if (modTB==NULL) {
189f42d8de7Schristos log_err("pythonmod: cannot print exception, "
190f42d8de7Schristos "cannot ImportModule traceback");
191f42d8de7Schristos goto cleanup;
192f42d8de7Schristos }
193f42d8de7Schristos
194f42d8de7Schristos /* Construct a cStringIO object */
195f42d8de7Schristos obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
196f42d8de7Schristos if (obFuncStringIO==NULL) {
197f42d8de7Schristos log_err("pythonmod: cannot print exception, "
198f42d8de7Schristos "cannot GetAttrString %s.StringIO", iomod);
199f42d8de7Schristos goto cleanup;
200f42d8de7Schristos }
201f42d8de7Schristos obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
202f42d8de7Schristos if (obStringIO==NULL) {
203f42d8de7Schristos log_err("pythonmod: cannot print exception, "
204f42d8de7Schristos "cannot call %s.StringIO()", iomod);
205f42d8de7Schristos goto cleanup;
206f42d8de7Schristos }
207f42d8de7Schristos
208f42d8de7Schristos /* Get the traceback.print_exception function, and call it. */
209f42d8de7Schristos obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
210f42d8de7Schristos if (obFuncTB==NULL) {
211f42d8de7Schristos log_err("pythonmod: cannot print exception, "
212f42d8de7Schristos "cannot GetAttrString traceback.print_exception");
213f42d8de7Schristos goto cleanup;
214f42d8de7Schristos }
215f42d8de7Schristos argsTB = Py_BuildValue("OOOOO", (exc_typ ? exc_typ : Py_None),
216f42d8de7Schristos (exc_val ? exc_val : Py_None), (exc_tb ? exc_tb : Py_None),
217f42d8de7Schristos Py_None, obStringIO);
218f42d8de7Schristos if (argsTB==NULL) {
219f42d8de7Schristos log_err("pythonmod: cannot print exception, "
220f42d8de7Schristos "cannot BuildValue for print_exception");
221f42d8de7Schristos goto cleanup;
222f42d8de7Schristos }
223f42d8de7Schristos
224f42d8de7Schristos obResult = PyObject_CallObject(obFuncTB, argsTB);
225f42d8de7Schristos if (obResult==NULL) {
226f42d8de7Schristos PyErr_Print();
227f42d8de7Schristos log_err("pythonmod: cannot print exception, "
228f42d8de7Schristos "call traceback.print_exception() failed");
229f42d8de7Schristos goto cleanup;
230f42d8de7Schristos }
231f42d8de7Schristos
232f42d8de7Schristos /* Now call the getvalue() method in the StringIO instance */
233f42d8de7Schristos Py_DECREF(obFuncStringIO);
234f42d8de7Schristos obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
235f42d8de7Schristos if (obFuncStringIO==NULL) {
236f42d8de7Schristos log_err("pythonmod: cannot print exception, "
237f42d8de7Schristos "cannot GetAttrString StringIO.getvalue");
238f42d8de7Schristos goto cleanup;
239f42d8de7Schristos }
240f42d8de7Schristos Py_DECREF(obResult);
241f42d8de7Schristos obResult = PyObject_CallObject(obFuncStringIO, NULL);
242f42d8de7Schristos if (obResult==NULL) {
243f42d8de7Schristos log_err("pythonmod: cannot print exception, "
244f42d8de7Schristos "call StringIO.getvalue() failed");
245f42d8de7Schristos goto cleanup;
246f42d8de7Schristos }
247f42d8de7Schristos
248f42d8de7Schristos /* And it should be a string all ready to go - duplicate it. */
249f42d8de7Schristos if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
250f42d8de7Schristos log_err("pythonmod: cannot print exception, "
251f42d8de7Schristos "StringIO.getvalue() result did not String_Check"
252f42d8de7Schristos " or Unicode_Check");
253f42d8de7Schristos goto cleanup;
254f42d8de7Schristos }
255f42d8de7Schristos if(PyString_Check(obResult)) {
256f42d8de7Schristos result = PyString_AsString(obResult);
257f42d8de7Schristos } else {
258f42d8de7Schristos ascstr = PyUnicode_AsASCIIString(obResult);
259f42d8de7Schristos result = PyBytes_AsString(ascstr);
260f42d8de7Schristos }
261f42d8de7Schristos log_err("pythonmod: python error: %s", result);
262f42d8de7Schristos
263f42d8de7Schristos cleanup:
264f42d8de7Schristos Py_XDECREF(modStringIO);
265f42d8de7Schristos Py_XDECREF(modTB);
266f42d8de7Schristos Py_XDECREF(obFuncStringIO);
267f42d8de7Schristos Py_XDECREF(obStringIO);
268f42d8de7Schristos Py_XDECREF(obFuncTB);
269f42d8de7Schristos Py_XDECREF(argsTB);
270f42d8de7Schristos Py_XDECREF(obResult);
271f42d8de7Schristos Py_XDECREF(ascstr);
272f42d8de7Schristos
273f42d8de7Schristos /* clear the exception, by not restoring it */
274f42d8de7Schristos /* Restore the exception state */
275f42d8de7Schristos /* PyErr_Restore(exc_typ, exc_val, exc_tb); */
2767a540f2bSchristos /* when using PyErr_Restore there is no need to Py_XDECREF for
2777a540f2bSchristos * these 3 pointers. */
2787a540f2bSchristos Py_XDECREF(exc_typ);
2797a540f2bSchristos Py_XDECREF(exc_val);
2807a540f2bSchristos Py_XDECREF(exc_tb);
281f42d8de7Schristos }
282f42d8de7Schristos
283*91f7d55fSchristos /* we only want to unwind Python once at exit */
284*91f7d55fSchristos static void
pythonmod_atexit(void)285*91f7d55fSchristos pythonmod_atexit(void)
286*91f7d55fSchristos {
287*91f7d55fSchristos log_assert(py_mod_count == 0);
288*91f7d55fSchristos log_assert(mainthr != NULL);
289*91f7d55fSchristos
290*91f7d55fSchristos PyEval_RestoreThread(mainthr);
291*91f7d55fSchristos Py_Finalize();
292*91f7d55fSchristos }
293*91f7d55fSchristos
pythonmod_init(struct module_env * env,int id)2943b6c3722Schristos int pythonmod_init(struct module_env* env, int id)
2953b6c3722Schristos {
29601049ae6Schristos int py_mod_idx = py_mod_count++;
29701049ae6Schristos
2983b6c3722Schristos /* Initialize module */
2993b6c3722Schristos FILE* script_py = NULL;
300*91f7d55fSchristos PyObject* py_init_arg = NULL, *res = NULL, *fname = NULL;
3013b6c3722Schristos PyGILState_STATE gil;
30201049ae6Schristos int init_standard = 1, i = 0;
303f42d8de7Schristos #if PY_MAJOR_VERSION < 3
304f42d8de7Schristos PyObject* PyFileObject = NULL;
305f42d8de7Schristos #endif
30601049ae6Schristos struct config_strlist* cfg_item = env->cfg->python_script;
3070cd9f4ecSchristos
3083b6c3722Schristos struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
3093b6c3722Schristos if (!pe)
3103b6c3722Schristos {
3113b6c3722Schristos log_err("pythonmod: malloc failure");
3123b6c3722Schristos return 0;
3133b6c3722Schristos }
3143b6c3722Schristos
3153b6c3722Schristos env->modinfo[id] = (void*) pe;
3163b6c3722Schristos
3173b6c3722Schristos /* Initialize module */
31801049ae6Schristos pe->fname=NULL; i = 0;
31901049ae6Schristos while (cfg_item!=NULL) {
32001049ae6Schristos if (py_mod_idx==i++) {
32101049ae6Schristos pe->fname=cfg_item->str;
32201049ae6Schristos break;
32301049ae6Schristos }
32401049ae6Schristos cfg_item = cfg_item->next;
32501049ae6Schristos }
3263b6c3722Schristos if(pe->fname==NULL || pe->fname[0]==0) {
32701049ae6Schristos log_err("pythonmod[%d]: no script given.", py_mod_idx);
3283b6c3722Schristos return 0;
3293b6c3722Schristos }
3303b6c3722Schristos
3313b6c3722Schristos /* Initialize Python libraries */
33201049ae6Schristos if (py_mod_count==1 && !Py_IsInitialized())
3333b6c3722Schristos {
334*91f7d55fSchristos #if PY_VERSION_HEX >= 0x03080000
335*91f7d55fSchristos PyStatus status;
336*91f7d55fSchristos PyPreConfig preconfig;
337*91f7d55fSchristos PyConfig config;
338*91f7d55fSchristos #endif
3393b6c3722Schristos #if PY_MAJOR_VERSION >= 3
3403b6c3722Schristos wchar_t progname[8];
3413b6c3722Schristos mbstowcs(progname, "unbound", 8);
3423b6c3722Schristos #else
3433b6c3722Schristos char *progname = "unbound";
3443b6c3722Schristos #endif
345*91f7d55fSchristos #if PY_VERSION_HEX < 0x03080000
3463b6c3722Schristos Py_SetProgramName(progname);
347*91f7d55fSchristos #else
348*91f7d55fSchristos /* Python must be preinitialized, before the PyImport_AppendInittab
349*91f7d55fSchristos * call. */
350*91f7d55fSchristos PyPreConfig_InitPythonConfig(&preconfig);
351*91f7d55fSchristos status = Py_PreInitialize(&preconfig);
352*91f7d55fSchristos if(PyStatus_Exception(status)) {
353*91f7d55fSchristos log_err("python exception in Py_PreInitialize: %s%s%s",
354*91f7d55fSchristos (status.func?status.func:""), (status.func?": ":""),
355*91f7d55fSchristos (status.err_msg?status.err_msg:""));
356*91f7d55fSchristos return 0;
357*91f7d55fSchristos }
358*91f7d55fSchristos #endif
3593b6c3722Schristos Py_NoSiteFlag = 1;
3603b6c3722Schristos #if PY_MAJOR_VERSION >= 3
3613b6c3722Schristos PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
3623b6c3722Schristos #endif
363*91f7d55fSchristos #if PY_VERSION_HEX < 0x03080000
3643b6c3722Schristos Py_Initialize();
365*91f7d55fSchristos #else
366*91f7d55fSchristos PyConfig_InitPythonConfig(&config);
367*91f7d55fSchristos status = PyConfig_SetString(&config, &config.program_name, progname);
368*91f7d55fSchristos if(PyStatus_Exception(status)) {
369*91f7d55fSchristos log_err("python exception in PyConfig_SetString(.. program_name ..): %s%s%s",
370*91f7d55fSchristos (status.func?status.func:""), (status.func?": ":""),
371*91f7d55fSchristos (status.err_msg?status.err_msg:""));
372*91f7d55fSchristos PyConfig_Clear(&config);
373*91f7d55fSchristos return 0;
374*91f7d55fSchristos }
375*91f7d55fSchristos config.site_import = 0;
376*91f7d55fSchristos status = Py_InitializeFromConfig(&config);
377*91f7d55fSchristos if(PyStatus_Exception(status)) {
378*91f7d55fSchristos log_err("python exception in Py_InitializeFromConfig: %s%s%s",
379*91f7d55fSchristos (status.func?status.func:""), (status.func?": ":""),
380*91f7d55fSchristos (status.err_msg?status.err_msg:""));
381*91f7d55fSchristos PyConfig_Clear(&config);
382*91f7d55fSchristos return 0;
383*91f7d55fSchristos }
384*91f7d55fSchristos PyConfig_Clear(&config);
385*91f7d55fSchristos #endif
3867a540f2bSchristos #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION <= 6)
3877a540f2bSchristos /* initthreads only for python 3.6 and older */
3883b6c3722Schristos PyEval_InitThreads();
3897a540f2bSchristos #endif
3903b6c3722Schristos SWIG_init();
39101049ae6Schristos mainthr = PyEval_SaveThread();
392*91f7d55fSchristos
393*91f7d55fSchristos /* register callback to unwind Python at exit */
394*91f7d55fSchristos atexit(pythonmod_atexit);
3953b6c3722Schristos }
3963b6c3722Schristos
3973b6c3722Schristos gil = PyGILState_Ensure();
3983b6c3722Schristos
39901049ae6Schristos if (py_mod_count==1) {
4003b6c3722Schristos /* Initialize Python */
401*91f7d55fSchristos if(PyRun_SimpleString("import sys \n") < 0 ) {
402*91f7d55fSchristos log_err("pythonmod: cannot initialize core module: unboundmodule.py");
403*91f7d55fSchristos goto python_init_fail;
404*91f7d55fSchristos }
4053b6c3722Schristos PyRun_SimpleString("sys.path.append('.') \n");
406*91f7d55fSchristos PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
407*91f7d55fSchristos PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
4083b6c3722Schristos if(env->cfg->directory && env->cfg->directory[0]) {
4093b6c3722Schristos char wdir[1524];
4103b6c3722Schristos snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
4113b6c3722Schristos env->cfg->directory);
4123b6c3722Schristos PyRun_SimpleString(wdir);
4133b6c3722Schristos }
414*91f7d55fSchristos if(PyRun_SimpleString("import site\n") < 0) {
415*91f7d55fSchristos log_err("pythonmod: cannot initialize core module: unboundmodule.py");
416*91f7d55fSchristos goto python_init_fail;
417*91f7d55fSchristos }
418*91f7d55fSchristos if(PyRun_SimpleString("sys.path.extend(site.getsitepackages())\n") < 0) {
419*91f7d55fSchristos log_err("pythonmod: cannot initialize core module: unboundmodule.py");
420*91f7d55fSchristos goto python_init_fail;
421*91f7d55fSchristos }
4223b6c3722Schristos if(PyRun_SimpleString("from unboundmodule import *\n") < 0)
4233b6c3722Schristos {
4243b6c3722Schristos log_err("pythonmod: cannot initialize core module: unboundmodule.py");
425*91f7d55fSchristos goto python_init_fail;
4263b6c3722Schristos }
42701049ae6Schristos }
4283b6c3722Schristos
4293b6c3722Schristos /* Check Python file load */
430f42d8de7Schristos /* uses python to open the file, this works on other platforms,
431f42d8de7Schristos * eg. Windows, to open the file in the correct mode for python */
432f42d8de7Schristos #if PY_MAJOR_VERSION < 3
433f42d8de7Schristos PyFileObject = PyFile_FromString((char*)pe->fname, "r");
434f42d8de7Schristos script_py = PyFile_AsFile(PyFileObject);
435f42d8de7Schristos #else
4367a540f2bSchristos script_py = fopen(pe->fname, "r");
437f42d8de7Schristos #endif
438f42d8de7Schristos if (script_py == NULL)
4393b6c3722Schristos {
4403b6c3722Schristos log_err("pythonmod: can't open file %s for reading", pe->fname);
441*91f7d55fSchristos goto python_init_fail;
4423b6c3722Schristos }
4433b6c3722Schristos
4443b6c3722Schristos /* Load file */
4453b6c3722Schristos pe->module = PyImport_AddModule("__main__");
446*91f7d55fSchristos Py_XINCREF(pe->module);
4473b6c3722Schristos pe->dict = PyModule_GetDict(pe->module);
448*91f7d55fSchristos Py_XINCREF(pe->dict);
449*91f7d55fSchristos clean_python_function_objects(pe->dict);
4503b6c3722Schristos
451*91f7d55fSchristos pe->data = PyDict_New();
452*91f7d55fSchristos /* add the script filename to the global "mod_env" for trivial access */
453*91f7d55fSchristos fname = PyString_FromString(pe->fname);
454*91f7d55fSchristos if(PyDict_SetItemString(pe->data, "script", fname) < 0) {
455*91f7d55fSchristos log_err("pythonmod: could not add item to dictionary");
456*91f7d55fSchristos Py_XDECREF(fname);
457*91f7d55fSchristos goto python_init_fail;
458*91f7d55fSchristos }
459*91f7d55fSchristos Py_XDECREF(fname);
460*91f7d55fSchristos Py_XINCREF(pe->data); /* reference will be stolen below */
461*91f7d55fSchristos if(PyModule_AddObject(pe->module, "mod_env", pe->data) < 0) {
462*91f7d55fSchristos log_err("pythonmod: could not add mod_env object");
463*91f7d55fSchristos Py_XDECREF(pe->data); /* 2 times, here and on python_init_fail; */
464*91f7d55fSchristos /* on failure the reference is not stolen */
465*91f7d55fSchristos goto python_init_fail;
466*91f7d55fSchristos }
4673b6c3722Schristos
468f42d8de7Schristos if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
4697a540f2bSchristos #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
4707a540f2bSchristos /* for python before 3.9 */
4713b6c3722Schristos log_err("pythonmod: can't parse Python script %s", pe->fname);
472f42d8de7Schristos /* print the error to logs too, run it again */
473f42d8de7Schristos fseek(script_py, 0, SEEK_SET);
474f42d8de7Schristos /* we don't run the file, like this, because then side-effects
475f42d8de7Schristos * s = PyRun_File(script_py, pe->fname, Py_file_input,
476f42d8de7Schristos * PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
477f42d8de7Schristos * could happen (again). Instead we parse the file again to get
478f42d8de7Schristos * the error string in the logs, for when the daemon has stderr
479f42d8de7Schristos * removed. SimpleFile run already printed to stderr, for then
480f42d8de7Schristos * this is called from unbound-checkconf or unbound -dd the user
481f42d8de7Schristos * has a nice formatted error.
482f42d8de7Schristos */
483f42d8de7Schristos /* ignore the NULL return of _node, it is NULL due to the parse failure
484f42d8de7Schristos * that we are expecting */
485f42d8de7Schristos (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
4867a540f2bSchristos #else
4877a540f2bSchristos /* for python 3.9 and newer */
4887a540f2bSchristos char* fstr = NULL;
4897a540f2bSchristos size_t flen = 0;
4907a540f2bSchristos log_err("pythonmod: can't parse Python script %s", pe->fname);
4917a540f2bSchristos /* print the error to logs too, run it again */
4927a540f2bSchristos fseek(script_py, 0, SEEK_END);
4937a540f2bSchristos flen = (size_t)ftell(script_py);
4947a540f2bSchristos fstr = malloc(flen+1);
4957a540f2bSchristos if(!fstr) {
4967a540f2bSchristos log_err("malloc failure to print parse error");
497*91f7d55fSchristos
498*91f7d55fSchristos /* close the file */
499*91f7d55fSchristos #if PY_MAJOR_VERSION < 3
500*91f7d55fSchristos Py_XDECREF(PyFileObject);
501*91f7d55fSchristos #else
5027a540f2bSchristos fclose(script_py);
503*91f7d55fSchristos #endif
504*91f7d55fSchristos
505*91f7d55fSchristos goto python_init_fail;
5067a540f2bSchristos }
5077a540f2bSchristos fseek(script_py, 0, SEEK_SET);
5087a540f2bSchristos if(fread(fstr, flen, 1, script_py) < 1) {
5097a540f2bSchristos log_err("file read failed to print parse error: %s: %s",
5107a540f2bSchristos pe->fname, strerror(errno));
5117a540f2bSchristos free(fstr);
512*91f7d55fSchristos
513*91f7d55fSchristos /* close the file */
514*91f7d55fSchristos #if PY_MAJOR_VERSION < 3
515*91f7d55fSchristos Py_XDECREF(PyFileObject);
516*91f7d55fSchristos #else
517*91f7d55fSchristos fclose(script_py);
518*91f7d55fSchristos #endif
519*91f7d55fSchristos
520*91f7d55fSchristos goto python_init_fail;
5217a540f2bSchristos }
5227a540f2bSchristos fstr[flen] = 0;
5237a540f2bSchristos /* we compile the string, but do not run it, to stop side-effects */
5247a540f2bSchristos /* ignore the NULL return of _node, it is NULL due to the parse failure
5257a540f2bSchristos * that we are expecting */
5267a540f2bSchristos (void)Py_CompileString(fstr, pe->fname, Py_file_input);
5277a540f2bSchristos #endif
528*91f7d55fSchristos
529f42d8de7Schristos log_py_err();
530*91f7d55fSchristos
531*91f7d55fSchristos /* close the file */
532*91f7d55fSchristos #if PY_MAJOR_VERSION < 3
533*91f7d55fSchristos Py_XDECREF(PyFileObject);
534*91f7d55fSchristos #else
53501049ae6Schristos fclose(script_py);
536*91f7d55fSchristos #endif
537*91f7d55fSchristos
5387a540f2bSchristos #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
5397a540f2bSchristos /* no cleanup needed for python before 3.9 */
5407a540f2bSchristos #else
5417a540f2bSchristos /* cleanup for python 3.9 and newer */
5427a540f2bSchristos free(fstr);
5437a540f2bSchristos #endif
544*91f7d55fSchristos goto python_init_fail;
5453b6c3722Schristos }
546*91f7d55fSchristos
547*91f7d55fSchristos /* close the file */
548f42d8de7Schristos #if PY_MAJOR_VERSION < 3
549f42d8de7Schristos Py_XDECREF(PyFileObject);
550f42d8de7Schristos #else
5513b6c3722Schristos fclose(script_py);
552f42d8de7Schristos #endif
5533b6c3722Schristos
5540cd9f4ecSchristos if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
5550cd9f4ecSchristos {
5560cd9f4ecSchristos init_standard = 0;
5573b6c3722Schristos if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
5583b6c3722Schristos {
5593b6c3722Schristos log_err("pythonmod: function init is missing in %s", pe->fname);
560*91f7d55fSchristos goto python_init_fail;
5613b6c3722Schristos }
5620cd9f4ecSchristos }
563*91f7d55fSchristos Py_XINCREF(pe->func_init);
5643b6c3722Schristos if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
5653b6c3722Schristos {
5663b6c3722Schristos log_err("pythonmod: function deinit is missing in %s", pe->fname);
567*91f7d55fSchristos goto python_init_fail;
5683b6c3722Schristos }
569*91f7d55fSchristos Py_XINCREF(pe->func_deinit);
5703b6c3722Schristos if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
5713b6c3722Schristos {
5723b6c3722Schristos log_err("pythonmod: function operate is missing in %s", pe->fname);
573*91f7d55fSchristos goto python_init_fail;
5743b6c3722Schristos }
575*91f7d55fSchristos Py_XINCREF(pe->func_operate);
5763b6c3722Schristos if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
5773b6c3722Schristos {
5783b6c3722Schristos log_err("pythonmod: function inform_super is missing in %s", pe->fname);
579*91f7d55fSchristos goto python_init_fail;
5803b6c3722Schristos }
581*91f7d55fSchristos Py_XINCREF(pe->func_inform);
5823b6c3722Schristos
5830cd9f4ecSchristos if (init_standard)
5840cd9f4ecSchristos {
5850cd9f4ecSchristos py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
5860cd9f4ecSchristos }
5870cd9f4ecSchristos else
5880cd9f4ecSchristos {
5890cd9f4ecSchristos py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
5900cd9f4ecSchristos SWIGTYPE_p_config_file, 0);
5910cd9f4ecSchristos }
5920cd9f4ecSchristos res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
5933b6c3722Schristos if (PyErr_Occurred())
5943b6c3722Schristos {
5953b6c3722Schristos log_err("pythonmod: Exception occurred in function init");
596f42d8de7Schristos log_py_err();
597*91f7d55fSchristos goto python_init_fail;
598*91f7d55fSchristos }
599*91f7d55fSchristos
600*91f7d55fSchristos Py_XDECREF(res);
601*91f7d55fSchristos Py_XDECREF(py_init_arg);
602*91f7d55fSchristos PyGILState_Release(gil);
603*91f7d55fSchristos return 1;
604*91f7d55fSchristos
605*91f7d55fSchristos python_init_fail:
606*91f7d55fSchristos Py_XDECREF(pe->module);
607*91f7d55fSchristos Py_XDECREF(pe->dict);
608*91f7d55fSchristos Py_XDECREF(pe->data);
609*91f7d55fSchristos Py_XDECREF(pe->func_init);
610*91f7d55fSchristos Py_XDECREF(pe->func_deinit);
611*91f7d55fSchristos Py_XDECREF(pe->func_operate);
612*91f7d55fSchristos Py_XDECREF(pe->func_inform);
6130cd9f4ecSchristos Py_XDECREF(res);
6140cd9f4ecSchristos Py_XDECREF(py_init_arg);
6150cd9f4ecSchristos PyGILState_Release(gil);
6160cd9f4ecSchristos return 0;
6173b6c3722Schristos }
6183b6c3722Schristos
pythonmod_deinit(struct module_env * env,int id)6193b6c3722Schristos void pythonmod_deinit(struct module_env* env, int id)
6203b6c3722Schristos {
621*91f7d55fSchristos int cbtype;
6223b6c3722Schristos struct pythonmod_env* pe = env->modinfo[id];
6233b6c3722Schristos if(pe == NULL)
6243b6c3722Schristos return;
6253b6c3722Schristos
6263b6c3722Schristos /* Free Python resources */
6273b6c3722Schristos if(pe->module != NULL)
6283b6c3722Schristos {
6293b6c3722Schristos PyObject* res;
6303b6c3722Schristos PyGILState_STATE gil = PyGILState_Ensure();
6313b6c3722Schristos
6323b6c3722Schristos /* Deinit module */
6333b6c3722Schristos res = PyObject_CallFunction(pe->func_deinit, "i", id);
6343b6c3722Schristos if (PyErr_Occurred()) {
6353b6c3722Schristos log_err("pythonmod: Exception occurred in function deinit");
636f42d8de7Schristos log_py_err();
6373b6c3722Schristos }
6383b6c3722Schristos /* Free result if any */
6393b6c3722Schristos Py_XDECREF(res);
6403b6c3722Schristos /* Free shared data if any */
641*91f7d55fSchristos Py_XDECREF(pe->module);
642*91f7d55fSchristos Py_XDECREF(pe->dict);
6433b6c3722Schristos Py_XDECREF(pe->data);
644*91f7d55fSchristos Py_XDECREF(pe->func_init);
645*91f7d55fSchristos Py_XDECREF(pe->func_deinit);
646*91f7d55fSchristos Py_XDECREF(pe->func_inform);
647*91f7d55fSchristos Py_XDECREF(pe->func_operate);
6483b6c3722Schristos PyGILState_Release(gil);
6493b6c3722Schristos
650*91f7d55fSchristos py_mod_count--;
6513b6c3722Schristos }
6523b6c3722Schristos pe->fname = NULL;
6533b6c3722Schristos free(pe);
6543b6c3722Schristos
655*91f7d55fSchristos /* iterate over all possible callback types and clean up each in turn */
656*91f7d55fSchristos for (cbtype = 0; cbtype < inplace_cb_types_total; cbtype++)
657*91f7d55fSchristos inplace_cb_delete(env, cbtype, id);
658*91f7d55fSchristos
6593b6c3722Schristos /* Module is deallocated in Python */
6603b6c3722Schristos env->modinfo[id] = NULL;
6613b6c3722Schristos }
6623b6c3722Schristos
pythonmod_inform_super(struct module_qstate * qstate,int id,struct module_qstate * super)6633b6c3722Schristos void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
6643b6c3722Schristos {
6653b6c3722Schristos struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
6663b6c3722Schristos struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
6673b6c3722Schristos PyObject* py_qstate, *py_sqstate, *res;
6683b6c3722Schristos PyGILState_STATE gil = PyGILState_Ensure();
6693b6c3722Schristos
6703b6c3722Schristos log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
6713b6c3722Schristos log_query_info(VERB_ALGO, "super is", &super->qinfo);
6723b6c3722Schristos
6733b6c3722Schristos py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
6743b6c3722Schristos py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
6753b6c3722Schristos
6763b6c3722Schristos res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
6773b6c3722Schristos py_sqstate, pq->data);
6783b6c3722Schristos
6793b6c3722Schristos if (PyErr_Occurred())
6803b6c3722Schristos {
6813b6c3722Schristos log_err("pythonmod: Exception occurred in function inform_super");
682f42d8de7Schristos log_py_err();
6833b6c3722Schristos qstate->ext_state[id] = module_error;
6843b6c3722Schristos }
6853b6c3722Schristos else if ((res == NULL) || (!PyObject_IsTrue(res)))
6863b6c3722Schristos {
6873b6c3722Schristos log_err("pythonmod: python returned bad code in inform_super");
6883b6c3722Schristos qstate->ext_state[id] = module_error;
6893b6c3722Schristos }
6903b6c3722Schristos
6913b6c3722Schristos Py_XDECREF(res);
6923b6c3722Schristos Py_XDECREF(py_sqstate);
6933b6c3722Schristos Py_XDECREF(py_qstate);
6943b6c3722Schristos
6953b6c3722Schristos PyGILState_Release(gil);
6963b6c3722Schristos }
6973b6c3722Schristos
pythonmod_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * ATTR_UNUSED (outbound))6983b6c3722Schristos void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
6993b6c3722Schristos int id, struct outbound_entry* ATTR_UNUSED(outbound))
7003b6c3722Schristos {
7013b6c3722Schristos struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
7023b6c3722Schristos struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
7033b6c3722Schristos PyObject* py_qstate, *res;
7043b6c3722Schristos PyGILState_STATE gil = PyGILState_Ensure();
7053b6c3722Schristos
7063b6c3722Schristos if ( pq == NULL)
7073b6c3722Schristos {
7083b6c3722Schristos /* create qstate */
7093b6c3722Schristos pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
7107a540f2bSchristos if(!pq) {
7117a540f2bSchristos log_err("pythonmod_operate: malloc failure for qstate");
7127a540f2bSchristos PyGILState_Release(gil);
7137a540f2bSchristos return;
7147a540f2bSchristos }
7153b6c3722Schristos
7163b6c3722Schristos /* Initialize per query data */
71701049ae6Schristos pq->data = PyDict_New();
7187a540f2bSchristos if(!pq->data) {
7197a540f2bSchristos log_err("pythonmod_operate: malloc failure for query data dict");
7207a540f2bSchristos PyGILState_Release(gil);
7217a540f2bSchristos return;
7227a540f2bSchristos }
7233b6c3722Schristos }
7243b6c3722Schristos
7253b6c3722Schristos /* Call operate */
7263b6c3722Schristos py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
7273b6c3722Schristos res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
7283b6c3722Schristos py_qstate, pq->data);
7293b6c3722Schristos if (PyErr_Occurred())
7303b6c3722Schristos {
7313b6c3722Schristos log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
732f42d8de7Schristos log_py_err();
7333b6c3722Schristos qstate->ext_state[id] = module_error;
7343b6c3722Schristos }
7353b6c3722Schristos else if ((res == NULL) || (!PyObject_IsTrue(res)))
7363b6c3722Schristos {
7373b6c3722Schristos log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
7383b6c3722Schristos qstate->ext_state[id] = module_error;
7393b6c3722Schristos }
7403b6c3722Schristos Py_XDECREF(res);
7413b6c3722Schristos Py_XDECREF(py_qstate);
7423b6c3722Schristos
7433b6c3722Schristos PyGILState_Release(gil);
7443b6c3722Schristos }
7453b6c3722Schristos
pythonmod_clear(struct module_qstate * qstate,int id)7463b6c3722Schristos void pythonmod_clear(struct module_qstate* qstate, int id)
7473b6c3722Schristos {
7483b6c3722Schristos struct pythonmod_qstate* pq;
7493b6c3722Schristos if (qstate == NULL)
7503b6c3722Schristos return;
7513b6c3722Schristos
7523b6c3722Schristos pq = (struct pythonmod_qstate*)qstate->minfo[id];
753f42d8de7Schristos verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%p", id, pq);
7543b6c3722Schristos if(pq != NULL)
7553b6c3722Schristos {
7563b6c3722Schristos PyGILState_STATE gil = PyGILState_Ensure();
7573b6c3722Schristos Py_DECREF(pq->data);
7583b6c3722Schristos PyGILState_Release(gil);
7593b6c3722Schristos /* Free qstate */
7603b6c3722Schristos free(pq);
7613b6c3722Schristos }
7623b6c3722Schristos
7633b6c3722Schristos qstate->minfo[id] = NULL;
7643b6c3722Schristos }
7653b6c3722Schristos
pythonmod_get_mem(struct module_env * env,int id)7663b6c3722Schristos size_t pythonmod_get_mem(struct module_env* env, int id)
7673b6c3722Schristos {
7683b6c3722Schristos struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
769f42d8de7Schristos verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%p", id, pe);
7703b6c3722Schristos if(!pe)
7713b6c3722Schristos return 0;
7723b6c3722Schristos return sizeof(*pe);
7733b6c3722Schristos }
7743b6c3722Schristos
7753b6c3722Schristos /**
7763b6c3722Schristos * The module function block
7773b6c3722Schristos */
7783b6c3722Schristos static struct module_func_block pythonmod_block = {
7793b6c3722Schristos "python",
7803b6c3722Schristos &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
7813b6c3722Schristos &pythonmod_clear, &pythonmod_get_mem
7823b6c3722Schristos };
7833b6c3722Schristos
pythonmod_get_funcblock(void)7843b6c3722Schristos struct module_func_block* pythonmod_get_funcblock(void)
7853b6c3722Schristos {
7863b6c3722Schristos return &pythonmod_block;
7873b6c3722Schristos }
788