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 * Global state for the module. 69 */ 70 struct pythonmod_env { 71 72 /** Python script filename. */ 73 const char* fname; 74 75 /** Python main thread */ 76 PyThreadState* mainthr; 77 /** Python module. */ 78 PyObject* module; 79 80 /** Module init function */ 81 PyObject* func_init; 82 /** Module deinit function */ 83 PyObject* func_deinit; 84 /** Module operate function */ 85 PyObject* func_operate; 86 /** Module super_inform function */ 87 PyObject* func_inform; 88 89 /** Python dictionary. */ 90 PyObject* dict; 91 92 /** Module data. */ 93 PyObject* data; 94 95 /** Module qstate. */ 96 struct module_qstate* qstate; 97 }; 98 99 /** 100 * Per query state for the iterator module. 101 */ 102 struct pythonmod_qstate { 103 104 /** Module per query data. */ 105 PyObject* data; 106 }; 107 108 /* Generated */ 109 #ifndef S_SPLINT_S 110 #include "pythonmod/interface.h" 111 #endif 112 113 /** log python error */ 114 static void 115 log_py_err(void) 116 { 117 char *result = NULL; 118 const char* iomod = "cStringIO"; 119 PyObject *modStringIO = NULL; 120 PyObject *modTB = NULL; 121 PyObject *obFuncStringIO = NULL; 122 PyObject *obStringIO = NULL; 123 PyObject *obFuncTB = NULL; 124 PyObject *argsTB = NULL; 125 PyObject *obResult = NULL; 126 PyObject *ascstr = NULL; 127 PyObject *exc_typ, *exc_val, *exc_tb; 128 129 /* Fetch the error state now before we cruch it */ 130 /* exc val contains the error message 131 * exc tb contains stack traceback and other info. */ 132 PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); 133 PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb); 134 135 /* Import the modules we need - cStringIO and traceback */ 136 modStringIO = PyImport_ImportModule("cStringIO"); 137 if (modStringIO==NULL) { 138 /* python 1.4 and before */ 139 modStringIO = PyImport_ImportModule("StringIO"); 140 iomod = "StringIO"; 141 } 142 if (modStringIO==NULL) { 143 /* python 3 */ 144 modStringIO = PyImport_ImportModule("io"); 145 iomod = "io"; 146 } 147 if (modStringIO==NULL) { 148 log_err("pythonmod: cannot print exception, " 149 "cannot ImportModule cStringIO or StringIO or io"); 150 goto cleanup; 151 } 152 modTB = PyImport_ImportModule("traceback"); 153 if (modTB==NULL) { 154 log_err("pythonmod: cannot print exception, " 155 "cannot ImportModule traceback"); 156 goto cleanup; 157 } 158 159 /* Construct a cStringIO object */ 160 obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); 161 if (obFuncStringIO==NULL) { 162 log_err("pythonmod: cannot print exception, " 163 "cannot GetAttrString %s.StringIO", iomod); 164 goto cleanup; 165 } 166 obStringIO = PyObject_CallObject(obFuncStringIO, NULL); 167 if (obStringIO==NULL) { 168 log_err("pythonmod: cannot print exception, " 169 "cannot call %s.StringIO()", iomod); 170 goto cleanup; 171 } 172 173 /* Get the traceback.print_exception function, and call it. */ 174 obFuncTB = PyObject_GetAttrString(modTB, "print_exception"); 175 if (obFuncTB==NULL) { 176 log_err("pythonmod: cannot print exception, " 177 "cannot GetAttrString traceback.print_exception"); 178 goto cleanup; 179 } 180 argsTB = Py_BuildValue("OOOOO", (exc_typ ? exc_typ : Py_None), 181 (exc_val ? exc_val : Py_None), (exc_tb ? exc_tb : Py_None), 182 Py_None, obStringIO); 183 if (argsTB==NULL) { 184 log_err("pythonmod: cannot print exception, " 185 "cannot BuildValue for print_exception"); 186 goto cleanup; 187 } 188 189 obResult = PyObject_CallObject(obFuncTB, argsTB); 190 if (obResult==NULL) { 191 PyErr_Print(); 192 log_err("pythonmod: cannot print exception, " 193 "call traceback.print_exception() failed"); 194 goto cleanup; 195 } 196 197 /* Now call the getvalue() method in the StringIO instance */ 198 Py_DECREF(obFuncStringIO); 199 obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue"); 200 if (obFuncStringIO==NULL) { 201 log_err("pythonmod: cannot print exception, " 202 "cannot GetAttrString StringIO.getvalue"); 203 goto cleanup; 204 } 205 Py_DECREF(obResult); 206 obResult = PyObject_CallObject(obFuncStringIO, NULL); 207 if (obResult==NULL) { 208 log_err("pythonmod: cannot print exception, " 209 "call StringIO.getvalue() failed"); 210 goto cleanup; 211 } 212 213 /* And it should be a string all ready to go - duplicate it. */ 214 if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) { 215 log_err("pythonmod: cannot print exception, " 216 "StringIO.getvalue() result did not String_Check" 217 " or Unicode_Check"); 218 goto cleanup; 219 } 220 if(PyString_Check(obResult)) { 221 result = PyString_AsString(obResult); 222 } else { 223 ascstr = PyUnicode_AsASCIIString(obResult); 224 result = PyBytes_AsString(ascstr); 225 } 226 log_err("pythonmod: python error: %s", result); 227 228 cleanup: 229 Py_XDECREF(modStringIO); 230 Py_XDECREF(modTB); 231 Py_XDECREF(obFuncStringIO); 232 Py_XDECREF(obStringIO); 233 Py_XDECREF(obFuncTB); 234 Py_XDECREF(argsTB); 235 Py_XDECREF(obResult); 236 Py_XDECREF(ascstr); 237 238 /* clear the exception, by not restoring it */ 239 /* Restore the exception state */ 240 /* PyErr_Restore(exc_typ, exc_val, exc_tb); */ 241 } 242 243 int pythonmod_init(struct module_env* env, int id) 244 { 245 /* Initialize module */ 246 FILE* script_py = NULL; 247 PyObject* py_init_arg, *res; 248 PyGILState_STATE gil; 249 int init_standard = 1; 250 #if PY_MAJOR_VERSION < 3 251 PyObject* PyFileObject = NULL; 252 #endif 253 254 struct pythonmod_env* pe = (struct pythonmod_env*)calloc(1, sizeof(struct pythonmod_env)); 255 if (!pe) 256 { 257 log_err("pythonmod: malloc failure"); 258 return 0; 259 } 260 261 env->modinfo[id] = (void*) pe; 262 263 /* Initialize module */ 264 pe->fname = env->cfg->python_script; 265 if(pe->fname==NULL || pe->fname[0]==0) { 266 log_err("pythonmod: no script given."); 267 return 0; 268 } 269 270 /* Initialize Python libraries */ 271 if (!Py_IsInitialized()) 272 { 273 #if PY_MAJOR_VERSION >= 3 274 wchar_t progname[8]; 275 mbstowcs(progname, "unbound", 8); 276 #else 277 char *progname = "unbound"; 278 #endif 279 Py_SetProgramName(progname); 280 Py_NoSiteFlag = 1; 281 #if PY_MAJOR_VERSION >= 3 282 PyImport_AppendInittab(SWIG_name, (void*)SWIG_init); 283 #endif 284 Py_Initialize(); 285 PyEval_InitThreads(); 286 SWIG_init(); 287 pe->mainthr = PyEval_SaveThread(); 288 } 289 290 gil = PyGILState_Ensure(); 291 292 /* Initialize Python */ 293 PyRun_SimpleString("import sys \n"); 294 PyRun_SimpleString("sys.path.append('.') \n"); 295 if(env->cfg->directory && env->cfg->directory[0]) { 296 char wdir[1524]; 297 snprintf(wdir, sizeof(wdir), "sys.path.append('%s') \n", 298 env->cfg->directory); 299 PyRun_SimpleString(wdir); 300 } 301 PyRun_SimpleString("sys.path.append('"RUN_DIR"') \n"); 302 PyRun_SimpleString("sys.path.append('"SHARE_DIR"') \n"); 303 PyRun_SimpleString("import distutils.sysconfig \n"); 304 PyRun_SimpleString("sys.path.append(distutils.sysconfig.get_python_lib(1,0)) \n"); 305 if (PyRun_SimpleString("from unboundmodule import *\n") < 0) 306 { 307 log_err("pythonmod: cannot initialize core module: unboundmodule.py"); 308 PyGILState_Release(gil); 309 return 0; 310 } 311 312 /* Check Python file load */ 313 /* uses python to open the file, this works on other platforms, 314 * eg. Windows, to open the file in the correct mode for python */ 315 #if PY_MAJOR_VERSION < 3 316 PyFileObject = PyFile_FromString((char*)pe->fname, "r"); 317 script_py = PyFile_AsFile(PyFileObject); 318 #else 319 script_py = _Py_fopen(pe->fname, "r"); 320 #endif 321 if (script_py == NULL) 322 { 323 log_err("pythonmod: can't open file %s for reading", pe->fname); 324 PyGILState_Release(gil); 325 return 0; 326 } 327 328 /* Load file */ 329 pe->module = PyImport_AddModule("__main__"); 330 pe->dict = PyModule_GetDict(pe->module); 331 pe->data = Py_None; 332 Py_INCREF(pe->data); 333 PyModule_AddObject(pe->module, "mod_env", pe->data); 334 335 /* TODO: deallocation of pe->... if an error occurs */ 336 337 if (PyRun_SimpleFile(script_py, pe->fname) < 0) { 338 log_err("pythonmod: can't parse Python script %s", pe->fname); 339 /* print the error to logs too, run it again */ 340 fseek(script_py, 0, SEEK_SET); 341 /* we don't run the file, like this, because then side-effects 342 * s = PyRun_File(script_py, pe->fname, Py_file_input, 343 * PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict); 344 * could happen (again). Instead we parse the file again to get 345 * the error string in the logs, for when the daemon has stderr 346 * removed. SimpleFile run already printed to stderr, for then 347 * this is called from unbound-checkconf or unbound -dd the user 348 * has a nice formatted error. 349 */ 350 /* ignore the NULL return of _node, it is NULL due to the parse failure 351 * that we are expecting */ 352 (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input); 353 log_py_err(); 354 PyGILState_Release(gil); 355 return 0; 356 } 357 #if PY_MAJOR_VERSION < 3 358 Py_XDECREF(PyFileObject); 359 #else 360 fclose(script_py); 361 #endif 362 363 if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL) 364 { 365 init_standard = 0; 366 if ((pe->func_init = PyDict_GetItemString(pe->dict, "init")) == NULL) 367 { 368 log_err("pythonmod: function init is missing in %s", pe->fname); 369 PyGILState_Release(gil); 370 return 0; 371 } 372 } 373 if ((pe->func_deinit = PyDict_GetItemString(pe->dict, "deinit")) == NULL) 374 { 375 log_err("pythonmod: function deinit is missing in %s", pe->fname); 376 PyGILState_Release(gil); 377 return 0; 378 } 379 if ((pe->func_operate = PyDict_GetItemString(pe->dict, "operate")) == NULL) 380 { 381 log_err("pythonmod: function operate is missing in %s", pe->fname); 382 PyGILState_Release(gil); 383 return 0; 384 } 385 if ((pe->func_inform = PyDict_GetItemString(pe->dict, "inform_super")) == NULL) 386 { 387 log_err("pythonmod: function inform_super is missing in %s", pe->fname); 388 PyGILState_Release(gil); 389 return 0; 390 } 391 392 if (init_standard) 393 { 394 py_init_arg = SWIG_NewPointerObj((void*) env, SWIGTYPE_p_module_env, 0); 395 } 396 else 397 { 398 py_init_arg = SWIG_NewPointerObj((void*) env->cfg, 399 SWIGTYPE_p_config_file, 0); 400 } 401 res = PyObject_CallFunction(pe->func_init, "iO", id, py_init_arg); 402 if (PyErr_Occurred()) 403 { 404 log_err("pythonmod: Exception occurred in function init"); 405 log_py_err(); 406 Py_XDECREF(res); 407 Py_XDECREF(py_init_arg); 408 PyGILState_Release(gil); 409 return 0; 410 } 411 412 Py_XDECREF(res); 413 Py_XDECREF(py_init_arg); 414 PyGILState_Release(gil); 415 416 return 1; 417 } 418 419 void pythonmod_deinit(struct module_env* env, int id) 420 { 421 struct pythonmod_env* pe = env->modinfo[id]; 422 if(pe == NULL) 423 return; 424 425 /* Free Python resources */ 426 if(pe->module != NULL) 427 { 428 PyObject* res; 429 PyGILState_STATE gil = PyGILState_Ensure(); 430 431 /* Deinit module */ 432 res = PyObject_CallFunction(pe->func_deinit, "i", id); 433 if (PyErr_Occurred()) { 434 log_err("pythonmod: Exception occurred in function deinit"); 435 log_py_err(); 436 } 437 /* Free result if any */ 438 Py_XDECREF(res); 439 /* Free shared data if any */ 440 Py_XDECREF(pe->data); 441 PyGILState_Release(gil); 442 443 PyEval_RestoreThread(pe->mainthr); 444 Py_Finalize(); 445 pe->mainthr = NULL; 446 } 447 pe->fname = NULL; 448 free(pe); 449 450 /* Module is deallocated in Python */ 451 env->modinfo[id] = NULL; 452 } 453 454 void pythonmod_inform_super(struct module_qstate* qstate, int id, struct module_qstate* super) 455 { 456 struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id]; 457 struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id]; 458 PyObject* py_qstate, *py_sqstate, *res; 459 PyGILState_STATE gil = PyGILState_Ensure(); 460 461 log_query_info(VERB_ALGO, "pythonmod: inform_super, sub is", &qstate->qinfo); 462 log_query_info(VERB_ALGO, "super is", &super->qinfo); 463 464 py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0); 465 py_sqstate = SWIG_NewPointerObj((void*) super, SWIGTYPE_p_module_qstate, 0); 466 467 res = PyObject_CallFunction(pe->func_inform, "iOOO", id, py_qstate, 468 py_sqstate, pq->data); 469 470 if (PyErr_Occurred()) 471 { 472 log_err("pythonmod: Exception occurred in function inform_super"); 473 log_py_err(); 474 qstate->ext_state[id] = module_error; 475 } 476 else if ((res == NULL) || (!PyObject_IsTrue(res))) 477 { 478 log_err("pythonmod: python returned bad code in inform_super"); 479 qstate->ext_state[id] = module_error; 480 } 481 482 Py_XDECREF(res); 483 Py_XDECREF(py_sqstate); 484 Py_XDECREF(py_qstate); 485 486 PyGILState_Release(gil); 487 } 488 489 void pythonmod_operate(struct module_qstate* qstate, enum module_ev event, 490 int id, struct outbound_entry* ATTR_UNUSED(outbound)) 491 { 492 struct pythonmod_env* pe = (struct pythonmod_env*)qstate->env->modinfo[id]; 493 struct pythonmod_qstate* pq = (struct pythonmod_qstate*)qstate->minfo[id]; 494 PyObject* py_qstate, *res; 495 PyGILState_STATE gil = PyGILState_Ensure(); 496 497 if ( pq == NULL) 498 { 499 /* create qstate */ 500 pq = qstate->minfo[id] = malloc(sizeof(struct pythonmod_qstate)); 501 502 /* Initialize per query data */ 503 pq->data = Py_None; 504 Py_INCREF(pq->data); 505 } 506 507 /* Call operate */ 508 py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0); 509 res = PyObject_CallFunction(pe->func_operate, "iiOO", id, (int) event, 510 py_qstate, pq->data); 511 if (PyErr_Occurred()) 512 { 513 log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event)); 514 log_py_err(); 515 qstate->ext_state[id] = module_error; 516 } 517 else if ((res == NULL) || (!PyObject_IsTrue(res))) 518 { 519 log_err("pythonmod: python returned bad code, event: %s", strmodulevent(event)); 520 qstate->ext_state[id] = module_error; 521 } 522 Py_XDECREF(res); 523 Py_XDECREF(py_qstate); 524 525 PyGILState_Release(gil); 526 } 527 528 void pythonmod_clear(struct module_qstate* qstate, int id) 529 { 530 struct pythonmod_qstate* pq; 531 if (qstate == NULL) 532 return; 533 534 pq = (struct pythonmod_qstate*)qstate->minfo[id]; 535 verbose(VERB_ALGO, "pythonmod: clear, id: %d, pq:%p", id, pq); 536 if(pq != NULL) 537 { 538 PyGILState_STATE gil = PyGILState_Ensure(); 539 Py_DECREF(pq->data); 540 PyGILState_Release(gil); 541 /* Free qstate */ 542 free(pq); 543 } 544 545 qstate->minfo[id] = NULL; 546 } 547 548 size_t pythonmod_get_mem(struct module_env* env, int id) 549 { 550 struct pythonmod_env* pe = (struct pythonmod_env*)env->modinfo[id]; 551 verbose(VERB_ALGO, "pythonmod: get_mem, id: %d, pe:%p", id, pe); 552 if(!pe) 553 return 0; 554 return sizeof(*pe); 555 } 556 557 /** 558 * The module function block 559 */ 560 static struct module_func_block pythonmod_block = { 561 "python", 562 &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, 563 &pythonmod_clear, &pythonmod_get_mem 564 }; 565 566 struct module_func_block* pythonmod_get_funcblock(void) 567 { 568 return &pythonmod_block; 569 } 570