1 /*
2 * pythonmod.c: unbound module C wrapper
3 *
4 * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
5 * Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
6 *
7 * This software is open source.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 *
20 * * Neither the name of the organization nor the names of its
21 * contributors may be used to endorse or promote products derived from this
22 * software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36 /**
37 * \file
38 * Python module for unbound. Calls python script.
39 */
40
41 /* ignore the varargs unused warning from SWIGs internal vararg support */
42 #ifdef __GNUC__
43 #pragma GCC diagnostic ignored "-Wunused-parameter"
44 #ifndef __clang__
45 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
46 #endif
47 #endif
48
49 #include "config.h"
50 #include "sldns/sbuffer.h"
51
52 #undef _POSIX_C_SOURCE
53 #undef _XOPEN_SOURCE
54 #include <Python.h>
55
56 #include "pythonmod/pythonmod.h"
57 #include "util/module.h"
58 #include "util/config_file.h"
59 #include "pythonmod_utils.h"
60
61 #ifdef S_SPLINT_S
62 typedef struct PyObject PyObject;
63 typedef struct PyThreadState PyThreadState;
64 typedef void* PyGILState_STATE;
65 #endif
66
67 /**
68 * counter for python module instances
69 * incremented by pythonmod_init(...)
70 */
71 int py_mod_count = 0;
72
73 /** Python main thread */
74 PyThreadState* mainthr;
75
76 /**
77 * Global state for the module.
78 */
79 struct pythonmod_env {
80
81 /** Python script filename. */
82 const char* fname;
83
84 /** Python module. */
85 PyObject* module;
86
87 /** Module init function */
88 PyObject* func_init;
89 /** Module deinit function */
90 PyObject* func_deinit;
91 /** Module operate function */
92 PyObject* func_operate;
93 /** Module super_inform function */
94 PyObject* func_inform;
95
96 /** Python dictionary. */
97 PyObject* dict;
98
99 /** Module data. */
100 PyObject* data;
101
102 /** Module qstate. */
103 struct module_qstate* qstate;
104 };
105
106 /**
107 * Per query state for the iterator module.
108 */
109 struct pythonmod_qstate {
110
111 /** Module per query data. */
112 PyObject* data;
113 };
114
115 /* Generated */
116 #ifndef S_SPLINT_S
117 #include "pythonmod/interface.h"
118 #endif
119
120 /** log python error */
121 static void
log_py_err(void)122 log_py_err(void)
123 {
124 char *result = NULL;
125 const char* iomod = "cStringIO";
126 PyObject *modStringIO = NULL;
127 PyObject *modTB = NULL;
128 PyObject *obFuncStringIO = NULL;
129 PyObject *obStringIO = NULL;
130 PyObject *obFuncTB = NULL;
131 PyObject *argsTB = NULL;
132 PyObject *obResult = NULL;
133 PyObject *ascstr = NULL;
134 PyObject *exc_typ, *exc_val, *exc_tb;
135
136 /* Fetch the error state now before we cruch it */
137 /* exc val contains the error message
138 * exc tb contains stack traceback and other info. */
139 PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
140 PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb);
141
142 /* Import the modules we need - cStringIO and traceback */
143 modStringIO = PyImport_ImportModule("cStringIO");
144 if (modStringIO==NULL) {
145 /* python 1.4 and before */
146 modStringIO = PyImport_ImportModule("StringIO");
147 iomod = "StringIO";
148 }
149 if (modStringIO==NULL) {
150 /* python 3 */
151 modStringIO = PyImport_ImportModule("io");
152 iomod = "io";
153 }
154 if (modStringIO==NULL) {
155 log_err("pythonmod: cannot print exception, "
156 "cannot ImportModule cStringIO or StringIO or io");
157 goto cleanup;
158 }
159 modTB = PyImport_ImportModule("traceback");
160 if (modTB==NULL) {
161 log_err("pythonmod: cannot print exception, "
162 "cannot ImportModule traceback");
163 goto cleanup;
164 }
165
166 /* Construct a cStringIO object */
167 obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
168 if (obFuncStringIO==NULL) {
169 log_err("pythonmod: cannot print exception, "
170 "cannot GetAttrString %s.StringIO", iomod);
171 goto cleanup;
172 }
173 obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
174 if (obStringIO==NULL) {
175 log_err("pythonmod: cannot print exception, "
176 "cannot call %s.StringIO()", iomod);
177 goto cleanup;
178 }
179
180 /* Get the traceback.print_exception function, and call it. */
181 obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
182 if (obFuncTB==NULL) {
183 log_err("pythonmod: cannot print exception, "
184 "cannot GetAttrString traceback.print_exception");
185 goto cleanup;
186 }
187 argsTB = Py_BuildValue("OOOOO", (exc_typ ? exc_typ : Py_None),
188 (exc_val ? exc_val : Py_None), (exc_tb ? exc_tb : Py_None),
189 Py_None, obStringIO);
190 if (argsTB==NULL) {
191 log_err("pythonmod: cannot print exception, "
192 "cannot BuildValue for print_exception");
193 goto cleanup;
194 }
195
196 obResult = PyObject_CallObject(obFuncTB, argsTB);
197 if (obResult==NULL) {
198 PyErr_Print();
199 log_err("pythonmod: cannot print exception, "
200 "call traceback.print_exception() failed");
201 goto cleanup;
202 }
203
204 /* Now call the getvalue() method in the StringIO instance */
205 Py_DECREF(obFuncStringIO);
206 obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
207 if (obFuncStringIO==NULL) {
208 log_err("pythonmod: cannot print exception, "
209 "cannot GetAttrString StringIO.getvalue");
210 goto cleanup;
211 }
212 Py_DECREF(obResult);
213 obResult = PyObject_CallObject(obFuncStringIO, NULL);
214 if (obResult==NULL) {
215 log_err("pythonmod: cannot print exception, "
216 "call StringIO.getvalue() failed");
217 goto cleanup;
218 }
219
220 /* And it should be a string all ready to go - duplicate it. */
221 if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
222 log_err("pythonmod: cannot print exception, "
223 "StringIO.getvalue() result did not String_Check"
224 " or Unicode_Check");
225 goto cleanup;
226 }
227 if(PyString_Check(obResult)) {
228 result = PyString_AsString(obResult);
229 } else {
230 ascstr = PyUnicode_AsASCIIString(obResult);
231 result = PyBytes_AsString(ascstr);
232 }
233 log_err("pythonmod: python error: %s", result);
234
235 cleanup:
236 Py_XDECREF(modStringIO);
237 Py_XDECREF(modTB);
238 Py_XDECREF(obFuncStringIO);
239 Py_XDECREF(obStringIO);
240 Py_XDECREF(obFuncTB);
241 Py_XDECREF(argsTB);
242 Py_XDECREF(obResult);
243 Py_XDECREF(ascstr);
244
245 /* clear the exception, by not restoring it */
246 /* Restore the exception state */
247 /* PyErr_Restore(exc_typ, exc_val, exc_tb); */
248 /* when using PyErr_Restore there is no need to Py_XDECREF for
249 * these 3 pointers. */
250 Py_XDECREF(exc_typ);
251 Py_XDECREF(exc_val);
252 Py_XDECREF(exc_tb);
253 }
254
pythonmod_init(struct module_env * env,int id)255 int pythonmod_init(struct module_env* env, int id)
256 {
257 int py_mod_idx = py_mod_count++;
258
259 /* Initialize module */
260 FILE* script_py = NULL;
261 PyObject* py_init_arg, *res;
262 PyGILState_STATE gil;
263 int init_standard = 1, i = 0;
264 #if PY_MAJOR_VERSION < 3
265 PyObject* PyFileObject = NULL;
266 #endif
267 struct config_strlist* cfg_item = env->cfg->python_script;
268
269 struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env));
270 if (!pe)
271 {
272 log_err("pythonmod: malloc failure");
273 return 0;
274 }
275
276 env->modinfo[id] = (void*) pe;
277
278 /* Initialize module */
279 pe->fname=NULL; i = 0;
280 while (cfg_item!=NULL) {
281 if (py_mod_idx==i++) {
282 pe->fname=cfg_item->str;
283 break;
284 }
285 cfg_item = cfg_item->next;
286 }
287 if(pe->fname==NULL || pe->fname[0]==0) {
288 log_err("pythonmod[%d]: no script given.", py_mod_idx);
289 return 0;
290 }
291
292 /* Initialize Python libraries */
293 if (py_mod_count==1 && !Py_IsInitialized())
294 {
295 #if PY_MAJOR_VERSION >= 3
296 wchar_t progname[8];
297 mbstowcs(progname, "unbound", 8);
298 #else
299 char *progname = "unbound";
300 #endif
301 Py_SetProgramName(progname);
302 Py_NoSiteFlag = 1;
303 #if PY_MAJOR_VERSION >= 3
304 PyImport_AppendInittab(SWIG_name, (void*)SWIG_init);
305 #endif
306 Py_Initialize();
307 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION <= 6)
308 /* initthreads only for python 3.6 and older */
309 PyEval_InitThreads();
310 #endif
311 SWIG_init();
312 mainthr = PyEval_SaveThread();
313 }
314
315 gil = PyGILState_Ensure();
316
317 if (py_mod_count==1) {
318 /* Initialize Python */
319 PyRun_SimpleString("import sys \n");
320 PyRun_SimpleString("sys.path.append('.') \n");
321 if(env->cfg->directory && env->cfg->directory[0]) {
322 char wdir[1524];
323 snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n",
324 env->cfg->directory);
325 PyRun_SimpleString(wdir);
326 }
327 PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n");
328 PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n");
329 PyRun_SimpleString("import distutils.sysconfig \n");
330 PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n");
331 if (PyRun_SimpleString("from unboundmodule import *\n") < 0)
332 {
333 log_err("pythonmod: cannot initialize core module: unboundmodule.py");
334 PyGILState_Release(gil);
335 return 0;
336 }
337 }
338
339 /* Check Python file load */
340 /* uses python to open the file, this works on other platforms,
341 * eg. Windows, to open the file in the correct mode for python */
342 #if PY_MAJOR_VERSION < 3
343 PyFileObject = PyFile_FromString((char*)pe->fname, "r");
344 script_py = PyFile_AsFile(PyFileObject);
345 #else
346 script_py = fopen(pe->fname, "r");
347 #endif
348 if (script_py == NULL)
349 {
350 log_err("pythonmod: can't open file %s for reading", pe->fname);
351 PyGILState_Release(gil);
352 return 0;
353 }
354
355 /* Load file */
356 pe->module = PyImport_AddModule("__main__");
357 pe->dict = PyModule_GetDict(pe->module);
358 pe->data = PyDict_New();
359 Py_XINCREF(pe->data);
360 PyModule_AddObject(pe->module, "mod_env", pe->data);
361
362 /* TODO: deallocation of pe->... if an error occurs */
363
364 if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
365 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
366 /* for python before 3.9 */
367 log_err("pythonmod: can't parse Python script %s", pe->fname);
368 /* print the error to logs too, run it again */
369 fseek(script_py, 0, SEEK_SET);
370 /* we don't run the file, like this, because then side-effects
371 * s = PyRun_File(script_py, pe->fname, Py_file_input,
372 * PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
373 * could happen (again). Instead we parse the file again to get
374 * the error string in the logs, for when the daemon has stderr
375 * removed. SimpleFile run already printed to stderr, for then
376 * this is called from unbound-checkconf or unbound -dd the user
377 * has a nice formatted error.
378 */
379 /* ignore the NULL return of _node, it is NULL due to the parse failure
380 * that we are expecting */
381 (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
382 #else
383 /* for python 3.9 and newer */
384 char* fstr = NULL;
385 size_t flen = 0;
386 log_err("pythonmod: can't parse Python script %s", pe->fname);
387 /* print the error to logs too, run it again */
388 fseek(script_py, 0, SEEK_END);
389 flen = (size_t)ftell(script_py);
390 fstr = malloc(flen+1);
391 if(!fstr) {
392 log_err("malloc failure to print parse error");
393 PyGILState_Release(gil);
394 fclose(script_py);
395 return 0;
396 }
397 fseek(script_py, 0, SEEK_SET);
398 if(fread(fstr, flen, 1, script_py) < 1) {
399 log_err("file read failed to print parse error: %s: %s",
400 pe->fname, strerror(errno));
401 PyGILState_Release(gil);
402 fclose(script_py);
403 free(fstr);
404 return 0;
405 }
406 fstr[flen] = 0;
407 /* we compile the string, but do not run it, to stop side-effects */
408 /* ignore the NULL return of _node, it is NULL due to the parse failure
409 * that we are expecting */
410 (void)Py_CompileString(fstr, pe->fname, Py_file_input);
411 #endif
412 log_py_err();
413 PyGILState_Release(gil);
414 fclose(script_py);
415 #if PY_MAJOR_VERSION <= 2 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 9)
416 /* no cleanup needed for python before 3.9 */
417 #else
418 /* cleanup for python 3.9 and newer */
419 free(fstr);
420 #endif
421 return 0;
422 }
423 #if PY_MAJOR_VERSION < 3
424 Py_XDECREF(PyFileObject);
425 #else
426 fclose(script_py);
427 #endif
428
429 if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
430 {
431 init_standard = 0;
432 if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL)
433 {
434 log_err("pythonmod: function init is missing in %s", pe->fname);
435 PyGILState_Release(gil);
436 return 0;
437 }
438 }
439 if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL)
440 {
441 log_err("pythonmod: function deinit is missing in %s", pe->fname);
442 PyGILState_Release(gil);
443 return 0;
444 }
445 if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL)
446 {
447 log_err("pythonmod: function operate is missing in %s", pe->fname);
448 PyGILState_Release(gil);
449 return 0;
450 }
451 if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL)
452 {
453 log_err("pythonmod: function inform_super is missing in %s", pe->fname);
454 PyGILState_Release(gil);
455 return 0;
456 }
457
458 if (init_standard)
459 {
460 py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0);
461 }
462 else
463 {
464 py_init_arg = SWIG_NewPointerObj((void*) env->cfg,
465 SWIGTYPE_p_config_file, 0);
466 }
467 res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg);
468 if (PyErr_Occurred())
469 {
470 log_err("pythonmod: Exception occurred in function init");
471 log_py_err();
472 Py_XDECREF(res);
473 Py_XDECREF(py_init_arg);
474 PyGILState_Release(gil);
475 return 0;
476 }
477
478 Py_XDECREF(res);
479 Py_XDECREF(py_init_arg);
480 PyGILState_Release(gil);
481
482 return 1;
483 }
484
pythonmod_deinit(struct module_env * env,int id)485 void pythonmod_deinit(struct module_env* env, int id)
486 {
487 struct pythonmod_env* pe = env->modinfo[id];
488 if(pe == NULL)
489 return;
490
491 /* Free Python resources */
492 if(pe->module != NULL)
493 {
494 PyObject* res;
495 PyGILState_STATE gil = PyGILState_Ensure();
496
497 /* Deinit module */
498 res = PyObject_CallFunction(pe->func_deinit, "i", id);
499 if (PyErr_Occurred()) {
500 log_err("pythonmod: Exception occurred in function deinit");
501 log_py_err();
502 }
503 /* Free result if any */
504 Py_XDECREF(res);
505 /* Free shared data if any */
506 Py_XDECREF(pe->data);
507 PyGILState_Release(gil);
508
509 if(--py_mod_count==0) {
510 PyEval_RestoreThread(mainthr);
511 Py_Finalize();
512 mainthr = NULL;
513 }
514 }
515 pe->fname = NULL;
516 free(pe);
517
518 /* Module is deallocated in Python */
519 env->modinfo[id] = NULL;
520 }
521
pythonmod_inform_super(struct module_qstate * qstate,int id,struct module_qstate * super)522 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super)
523 {
524 struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
525 struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
526 PyObject* py_qstate, *py_sqstate, *res;
527 PyGILState_STATE gil = PyGILState_Ensure();
528
529 log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo);
530 log_query_info(VERB_ALGO, "super is", &super->qinfo);
531
532 py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
533 py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0);
534
535 res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate,
536 py_sqstate, pq->data);
537
538 if (PyErr_Occurred())
539 {
540 log_err("pythonmod: Exception occurred in function inform_super");
541 log_py_err();
542 qstate->ext_state[id] = module_error;
543 }
544 else if ((res == NULL) || (!PyObject_IsTrue(res)))
545 {
546 log_err("pythonmod: python returned bad code in inform_super");
547 qstate->ext_state[id] = module_error;
548 }
549
550 Py_XDECREF(res);
551 Py_XDECREF(py_sqstate);
552 Py_XDECREF(py_qstate);
553
554 PyGILState_Release(gil);
555 }
556
pythonmod_operate(struct module_qstate * qstate,enum module_ev event,int id,struct outbound_entry * ATTR_UNUSED (outbound))557 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event,
558 int id, struct outbound_entry* ATTR_UNUSED(outbound))
559 {
560 struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id];
561 struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id];
562 PyObject* py_qstate, *res;
563 PyGILState_STATE gil = PyGILState_Ensure();
564
565 if ( pq == NULL)
566 {
567 /* create qstate */
568 pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate));
569 if(!pq) {
570 log_err("pythonmod_operate: malloc failure for qstate");
571 PyGILState_Release(gil);
572 return;
573 }
574
575 /* Initialize per query data */
576 pq->data = PyDict_New();
577 if(!pq->data) {
578 log_err("pythonmod_operate: malloc failure for query data dict");
579 PyGILState_Release(gil);
580 return;
581 }
582 }
583
584 /* Call operate */
585 py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0);
586 res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event,
587 py_qstate, pq->data);
588 if (PyErr_Occurred())
589 {
590 log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
591 log_py_err();
592 qstate->ext_state[id] = module_error;
593 }
594 else if ((res == NULL) || (!PyObject_IsTrue(res)))
595 {
596 log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event));
597 qstate->ext_state[id] = module_error;
598 }
599 Py_XDECREF(res);
600 Py_XDECREF(py_qstate);
601
602 PyGILState_Release(gil);
603 }
604
pythonmod_clear(struct module_qstate * qstate,int id)605 void pythonmod_clear(struct module_qstate* qstate, int id)
606 {
607 struct pythonmod_qstate* pq;
608 if (qstate == NULL)
609 return;
610
611 pq = (struct pythonmod_qstate*)qstate->minfo[id];
612 verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%p", id, pq);
613 if(pq != NULL)
614 {
615 PyGILState_STATE gil = PyGILState_Ensure();
616 Py_DECREF(pq->data);
617 PyGILState_Release(gil);
618 /* Free qstate */
619 free(pq);
620 }
621
622 qstate->minfo[id] = NULL;
623 }
624
pythonmod_get_mem(struct module_env * env,int id)625 size_t pythonmod_get_mem(struct module_env* env, int id)
626 {
627 struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id];
628 verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%p", id, pe);
629 if(!pe)
630 return 0;
631 return sizeof(*pe);
632 }
633
634 /**
635 * The module function block
636 */
637 static struct module_func_block pythonmod_block = {
638 "python",
639 &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super,
640 &pythonmod_clear, &pythonmod_get_mem
641 };
642
pythonmod_get_funcblock(void)643 struct module_func_block* pythonmod_get_funcblock(void)
644 {
645 return &pythonmod_block;
646 }
647