if test -n "$(doxygen)"; then \
$(doxygen) $(srcdir)/doc/unbound.doxygen; fi
if test "$(WITH_PYUNBOUND)" = "yes" -o "$(WITH_PYTHONMODULE)" = "yes"; \
- then if test -x "`which sphinx-build 2>&1`"; then \
- sphinx-build -b html pythonmod/doc doc/html/pythonmod; \
- sphinx-build -b html libunbound/python/doc doc/html/pyunbound;\
+ then if test -x "`which sphinx-build-$(PY_MAJOR_VERSION) 2>&1`"; then \
+ sphinx-build-$(PY_MAJOR_VERSION) -b html pythonmod/doc doc/html/pythonmod; \
+ sphinx-build-$(PY_MAJOR_VERSION) -b html libunbound/python/doc doc/html/pyunbound;\
fi ;\
fi
+22 November 2018: Wouter
+ - With ./configure --with-pyunbound --with-pythonmodule
+ PYTHON_VERSION=3.6 or with 2.7 unbound can compile and unit tests
+ succeed for the python module.
+ - pythonmod logs the python error and traceback on failure.
+
21 November 2018: Wouter
- Scrub NS records from NODATA responses as well.
EXCLUDE = ./build \
./compat \
+ ./contrib \
util/configparser.c \
util/configparser.h \
util/configlexer.c \
util/locks.h \
+ pythonmod/doc \
+ pythonmod/examples \
pythonmod/unboundmodule.py \
pythonmod/interface.h \
- pythonmod/examples/resgen.py \
- pythonmod/examples/resmod.py \
- pythonmod/examples/resip.py \
+ pythonmod/ubmodule-msg.py \
+ pythonmod/ubmodule-tst.py \
libunbound/python/unbound.py \
libunbound/python/libunbound_wrap.c \
+ libunbound/python/doc \
+ libunbound/python/examples \
./ldns-src \
doc/control_proto_spec.txt \
doc/requirements.txt
i = 0;
while (i < len) {
- i += name[i] + 1;
+ i += ((unsigned int)name[i]) + 1;
cnt++;
}
list = PyList_New(cnt);
i = 0; cnt = 0;
while (i < len) {
- PyList_SetItem(list, cnt, PyBytes_FromStringAndSize(name + i + 1, name[i]));
- i += name[i] + 1;
+ char buf[LDNS_MAX_LABELLEN+1];
+ if(((unsigned int)name[i])+1 <= (int)sizeof(buf)) {
+ memmove(buf, name + i + 1, (unsigned int)name[i]);
+ buf[(unsigned int)name[i]] = 0;
+ PyList_SetItem(list, cnt, PyString_FromString(buf));
+ }
+ i += ((unsigned int)name[i]) + 1;
cnt++;
}
return list;
%}
%inline %{
- PyObject* dnameAsStr(const char* dname) {
+ PyObject* dnameAsStr(PyObject* dname) {
char buf[LDNS_MAX_DOMAINLEN+1];
buf[0] = '\0';
- dname_str((uint8_t*)dname, buf);
- return PyBytes_FromString(buf);
+ dname_str((uint8_t*)PyBytes_AsString(dname), buf);
+ return PyString_FromString(buf);
}
%}
for (i=0; i < PyList_Size(l); i++)
{
item = PyList_GetItem(l, i);
- if (!PyBytes_Check(item))
+ if (!PyBytes_Check(item) && !PyUnicode_Check(item))
return 0;
}
return 1;
PyObject* item;
int i;
size_t len;
+ char* s;
+ PyObject* ascstr;
for (i=0; i < PyList_Size(l); i++)
{
+ ascstr = NULL;
item = PyList_GetItem(l, i);
+ if(PyObject_TypeCheck(item, &PyBytes_Type)) {
+ s = PyBytes_AsString(item);
+ } else {
+ ascstr = PyUnicode_AsASCIIString(item);
+ s = PyBytes_AsString(ascstr);
+ }
len = sldns_buffer_remaining(qb);
if(qsec) {
- if(sldns_str2wire_rr_question_buf(PyBytes_AsString(item),
+ if(sldns_str2wire_rr_question_buf(s,
sldns_buffer_current(qb), &len, NULL, NULL, 0, NULL, 0)
- != 0)
+ != 0) {
+ if(ascstr)
+ Py_DECREF(ascstr);
return 0;
+ }
} else {
- if(sldns_str2wire_rr_buf(PyBytes_AsString(item),
+ if(sldns_str2wire_rr_buf(s,
sldns_buffer_current(qb), &len, NULL, default_ttl,
- NULL, 0, NULL, 0) != 0)
+ NULL, 0, NULL, 0) != 0) {
+ if(ascstr)
+ Py_DECREF(ascstr);
return 0;
+ }
}
+ if(ascstr)
+ Py_DECREF(ascstr);
sldns_buffer_skip(qb, len);
sldns_buffer_write_u16_at(qb, count_offset,
#include "pythonmod/interface.h"
#endif
+/** log python error */
+static void
+log_py_err(void)
+{
+ char *result = NULL;
+ PyObject *modStringIO = NULL;
+ PyObject *modTB = NULL;
+ PyObject *obFuncStringIO = NULL;
+ PyObject *obStringIO = NULL;
+ PyObject *obFuncTB = NULL;
+ PyObject *argsTB = NULL;
+ PyObject *obResult = NULL;
+ PyObject *ascstr = NULL;
+ PyObject *exc_typ, *exc_val, *exc_tb;
+
+ /* Fetch the error state now before we cruch it */
+ /* exc val contains the error message
+ * exc tb contains stack traceback and other info. */
+ PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
+ PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb);
+
+ /* Import the modules we need - cStringIO and traceback */
+ modStringIO = PyImport_ImportModule("cStringIO");
+ if (modStringIO==NULL) /* python 1.4 and before */
+ modStringIO = PyImport_ImportModule("StringIO");
+ if (modStringIO==NULL) /* python 3 */
+ modStringIO = PyImport_ImportModule("io");
+ if (modStringIO==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot ImportModule cStringIO or StringIO");
+ goto cleanup;
+ }
+ modTB = PyImport_ImportModule("traceback");
+ if (modTB==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot ImportModule traceback");
+ goto cleanup;
+ }
+
+ /* Construct a cStringIO object */
+ obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
+ if (obFuncStringIO==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot GetAttrString cStringIO.StringIO");
+ goto cleanup;
+ }
+ obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
+ if (obStringIO==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot cStringIO.StringIO()");
+ goto cleanup;
+ }
+
+ /* Get the traceback.print_exception function, and call it. */
+ obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
+ if (obFuncTB==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot GetAttrString traceback.print_exception");
+ goto cleanup;
+ }
+ argsTB = Py_BuildValue("OOOOO", (exc_typ ? exc_typ : Py_None),
+ (exc_val ? exc_val : Py_None), (exc_tb ? exc_tb : Py_None),
+ Py_None, obStringIO);
+ if (argsTB==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot BuildValue for print_exception");
+ goto cleanup;
+ }
+
+ obResult = PyObject_CallObject(obFuncTB, argsTB);
+ if (obResult==NULL) {
+ PyErr_Print();
+ log_err("pythonmod: cannot print exception, "
+ "call traceback.print_exception() failed");
+ goto cleanup;
+ }
+
+ /* Now call the getvalue() method in the StringIO instance */
+ Py_DECREF(obFuncStringIO);
+ obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
+ if (obFuncStringIO==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "cannot GetAttrString cStringIO.getvalue");
+ goto cleanup;
+ }
+ Py_DECREF(obResult);
+ obResult = PyObject_CallObject(obFuncStringIO, NULL);
+ if (obResult==NULL) {
+ log_err("pythonmod: cannot print exception, "
+ "call cStringIO.getvalue() failed");
+ goto cleanup;
+ }
+
+ /* And it should be a string all ready to go - duplicate it. */
+ if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
+ log_err("pythonmod: cannot print exception, "
+ "cStringIO.getvalue() result did not String_Check");
+ goto cleanup;
+ }
+ if(PyString_Check(obResult)) {
+ result = PyString_AsString(obResult);
+ } else {
+ ascstr = PyUnicode_AsASCIIString(obResult);
+ result = PyBytes_AsString(ascstr);
+ }
+ log_err("pythonmod: python error: %s", result);
+
+cleanup:
+ Py_XDECREF(modStringIO);
+ Py_XDECREF(modTB);
+ Py_XDECREF(obFuncStringIO);
+ Py_XDECREF(obStringIO);
+ Py_XDECREF(obFuncTB);
+ Py_XDECREF(argsTB);
+ Py_XDECREF(obResult);
+ Py_XDECREF(ascstr);
+
+ /* clear the exception, by not restoring it */
+ /* Restore the exception state */
+ /* PyErr_Restore(exc_typ, exc_val, exc_tb); */
+}
+
int pythonmod_init(struct module_env* env, int id)
{
/* Initialize module */
/* TODO: deallocation of pe->... if an error occurs */
- if (PyRun_SimpleFile(script_py, pe->fname) < 0)
- {
+ if (PyRun_SimpleFile(script_py, pe->fname) < 0) {
log_err("pythonmod: can't parse Python script %s", pe->fname);
+ /* print the error to logs too, run it again */
+ fseek(script_py, 0, SEEK_SET);
+ /* we don't run the file, like this, because then side-effects
+ * s = PyRun_File(script_py, pe->fname, Py_file_input,
+ * PyModule_GetDict(PyImport_AddModule("__main__")), pe->dict);
+ * could happen (again). Instead we parse the file again to get
+ * the error string in the logs, for when the daemon has stderr
+ * removed. SimpleFile run already printed to stderr, for then
+ * this is called from unbound-checkconf or unbound -dd the user
+ * has a nice formatted error.
+ */
+ /* ignore the NULL return of _node, it is NULL due to the parse failure
+ * that we are expecting */
+ (void)PyParser_SimpleParseFile(script_py, pe->fname, Py_file_input);
+ log_py_err();
PyGILState_Release(gil);
return 0;
}
-
fclose(script_py);
if ((pe->func_init = PyDict_GetItemString(pe->dict, "init_standard")) == NULL)
if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function init");
- PyErr_Print();
+ log_py_err();
Py_XDECREF(res);
Py_XDECREF(py_init_arg);
PyGILState_Release(gil);
res = PyObject_CallFunction(pe->func_deinit, "i", id);
if (PyErr_Occurred()) {
log_err("pythonmod: Exception occurred in function deinit");
- PyErr_Print();
+ log_py_err();
}
/* Free result if any */
Py_XDECREF(res);
if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function inform_super");
- PyErr_Print();
+ log_py_err();
qstate->ext_state[id] = module_error;
}
else if ((res == NULL) || (!PyObject_IsTrue(res)))
if (PyErr_Occurred())
{
log_err("pythonmod: Exception occurred in function operate, event: %s", strmodulevent(event));
- PyErr_Print();
+ log_py_err();
qstate->ext_state[id] = module_error;
}
else if ((res == NULL) || (!PyObject_IsTrue(res)))
#echo export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../.libs:."
#export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../.libs:."
+if grep "PY_MAJOR_VERSION=3" $PRE/Makefile; then
+ PYTHON="python3"; else PYTHON="python2"; fi
+if test ! -x `which $PYTHON` 2>&1; then PYTHON="python"; fi
+
# do the test
echo "> pylib.lookup.py www.example.com."
-./pylib.lookup.py www.example.com. | tee outfile
+$PYTHON pylib.lookup.py www.example.com. | tee outfile
echo "> cat logfiles"
cat fwd.log
def dataHex(data, prefix=""):
res = ""
- for i in range(0, (len(data)+15)/16):
+ for i in range(0, int((len(data)+15)/16)):
res += "%s0x%02X | " % (prefix, i*16)
- d = map(lambda x:ord(x), data[i*16:i*16+17])
+ if type(data[0]) == type(1):
+ d = map(lambda x:int(x), data[i*16:i*16+17])
+ else:
+ d = map(lambda x:ord(x), data[i*16:i*16+17])
for ch in d:
res += "%02X " % ch
- for i in range(0,17-len(d)):
+ for i in range(0,17-len(data[i*16:i*16+17])):
res += " "
res += "| "
for ch in d:
return res
def printReturnMsg(qstate):
- print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
- print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
+ print ("Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount, qstate.return_msg.rep.security, qstate.return_msg.rep.ttl))
+ print (" qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str)
if (qstate.return_msg.rep):
- print "RRSets:",qstate.return_msg.rep.rrset_count
+ print ("RRSets:",qstate.return_msg.rep.rrset_count)
prevkey = None
for i in range(0,qstate.return_msg.rep.rrset_count):
r = qstate.return_msg.rep.rrsets[i]
rk = r.rk
- print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
- print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
+ print (i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags)
+ print ("type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class))
d = r.entry.data
- print " RRDatas:",d.count+d.rrsig_count
+ print (" RRDatas:",d.count+d.rrsig_count)
for j in range(0,d.count+d.rrsig_count):
- print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
- print dataHex(d.rr_data[j]," ")
+ print (" ",j,":","TTL=",d.rr_ttl[j],"RR data:")
+ print (dataHex(d.rr_data[j]," "))
def operate(id, event, qstate, qdata):
log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
- #print "pythonmod: per query data", qdata
+ #print ("pythonmod: per query data", qdata)
- print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, qstate.qinfo.qname_str,
- print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
- print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
- print
+ print ("Query:", qstate.qinfo.qname, qstate.qinfo.qname_list, qstate.qinfo.qname_str)
+ print ("Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype)
+ print ("Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass)
+ print ()
if (event == MODULE_EVENT_NEW or event == MODULE_EVENT_PASS) and (qstate.qinfo.qname_str.endswith("www2.example.com.")):
- print qstate.qinfo.qname_str
+ print (qstate.qinfo.qname_str)
qstate.ext_state[id] = MODULE_FINISHED
if (qstate.qinfo.qtype == RR_TYPE_TXT) or (qstate.qinfo.qtype == RR_TYPE_ANY):
msg.answer.append("%s 10 IN TXT path=/" % qstate.qinfo.qname_str)
+ print(msg.answer)
if not msg.set_return_msg(qstate):
qstate.ext_state[id] = MODULE_ERROR
return True
def dataHex(data, prefix=""):
res = ""
- for i in range(0, (len(data)+15)/16):
+ for i in range(0, int((len(data)+15)/16)):
res += "%s0x%02X | " % (prefix, i*16)
- d = map(lambda x:ord(x), data[i*16:i*16+17])
+ if type(data[0]) == type(1):
+ d = map(lambda x:int(x), data[i*16:i*16+17])
+ else:
+ d = map(lambda x:ord(x), data[i*16:i*16+17])
for ch in d:
- res += "%02X " % ch
- for i in range(0,17-len(d)):
+ res += "%02X " % int(ch)
+ for i in range(0,17-len(data[i*16:i*16+17])):
res += " "
res += "| "
for ch in d:
return res
def printReturnMsg(qstate):
- print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)
- print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str
+ print ("Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount, qstate.return_msg.rep.security, qstate.return_msg.rep.ttl))
+ print (" qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str)
if (qstate.return_msg.rep):
- print "RRSets:",qstate.return_msg.rep.rrset_count
+ print ("RRSets:",qstate.return_msg.rep.rrset_count)
prevkey = None
for i in range(0,qstate.return_msg.rep.rrset_count):
r = qstate.return_msg.rep.rrsets[i]
rk = r.rk
- print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags,
- print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)
+ print (i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags)
+ print ("type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class))
d = r.entry.data
- print " RRDatas:",d.count+d.rrsig_count
+ print (" RRDatas:",d.count+d.rrsig_count)
for j in range(0,d.count+d.rrsig_count):
- print " ",j,":","TTL=",d.rr_ttl[j],"RR data:"
- print dataHex(d.rr_data[j]," ")
+ print (" ",j,":","TTL=",d.rr_ttl[j],"RR data:")
+ print (dataHex(d.rr_data[j]," "))
def operate(id, event, qstate, qdata):
log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event)))
- #print "pythonmod: per query data", qdata
+ #print ("pythonmod: per query data", qdata)
- print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, qstate.qinfo.qname_str,
- print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype,
- print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass
- print
+ print ("Query:", qstate.qinfo.qname, qstate.qinfo.qname_list, qstate.qinfo.qname_str)
+ print ("Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype)
+ print ("Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass)
+ print ()
if (event == MODULE_EVENT_NEW or event == MODULE_EVENT_PASS) and (qstate.qinfo.qname_str.endswith("example.com.")):
- print qstate.qinfo.qname_str
+ print (qstate.qinfo.qname_str)
qstate.ext_state[id] = MODULE_FINISHED
- # eat time
- y = 20
- for z in range(2, 10000):
- y = y*2 - z/2
- y = y/2 + z
+ # eat time
+ y = 20
+ for z in range(2, 10000):
+ y = y*2 - z/2
+ y = y/2 + z
msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) #, 300)
#msg.authority.append("xxx.seznam.cz. 10 IN A 192.168.1.1")
www2 IN A 10.20.30.40
ENTRY_END
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www3 IN A
+SECTION ANSWER
+www3 IN A 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www4 IN A
+SECTION ANSWER
+www4 IN A 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NOERROR
+ADJUST copy_id
+SECTION QUESTION
+www5 IN A
+SECTION ANSWER
+www5 IN A 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NXDOMAIN
+ADJUST copy_id
+SECTION QUESTION
+www6 IN A
+SECTION AUTHORITY
+example.com. 3600 IN SOA a. b. 2018100719 7200 3600 1209600 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NXDOMAIN
+ADJUST copy_id
+SECTION QUESTION
+www7 IN A
+SECTION AUTHORITY
+example.com. 3600 IN SOA a. b. 2018100719 7200 3600 1209600 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NXDOMAIN
+ADJUST copy_id
+SECTION QUESTION
+www8 IN A
+SECTION AUTHORITY
+example.com. 3600 IN SOA a. b. 2018100719 7200 3600 1209600 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NXDOMAIN
+ADJUST copy_id
+SECTION QUESTION
+www9 IN A
+SECTION AUTHORITY
+example.com. 3600 IN SOA a. b. 2018100719 7200 3600 1209600 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR AA NXDOMAIN
+ADJUST copy_id
+SECTION QUESTION
+www10 IN A
+SECTION AUTHORITY
+example.com. 3600 IN SOA a. b. 2018100719 7200 3600 1209600 3600
+ENTRY_END
+