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 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 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 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 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 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 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 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 643 struct module_func_block* pythonmod_get_funcblock(void) 644 { 645 return &pythonmod_block; 646 } 647