From 5fe01bc3b8932b5dffa88578663727c3df694bc4 Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Tue, 14 Apr 2015 14:01:02 +0200 Subject: [PATCH 4/4] Resolve Python 3 incompatibilities in libunbound and pythonmod wrappers Signed-off-by: Tomas Hozza Fix issue with Python 3 mapping of FILE* using file_py3.i from ldns. --- Makefile.in | 3 +- configure.ac | 2 + libunbound/python/file_py3.i | 155 +++++++++++++++++++++++++++++++++++++++++ libunbound/python/libunbound.i | 4 ++ pythonmod/interface.i | 28 ++++---- pythonmod/pythonmod.c | 8 ++- 6 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 libunbound/python/file_py3.i diff --git a/Makefile.in b/Makefile.in index 94b126d..a84b303 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,6 +25,7 @@ DNSTAP_SRC=@DNSTAP_SRC@ DNSTAP_OBJ=@DNSTAP_OBJ@ WITH_PYTHONMODULE=@WITH_PYTHONMODULE@ WITH_PYUNBOUND=@WITH_PYUNBOUND@ +PY_MAJOR_VERSION=@PY_MAJOR_VERSION@ PYTHON_SITE_PKG=@PYTHON_SITE_PKG@ PYTHONMOD_INSTALL=@PYTHONMOD_INSTALL@ PYTHONMOD_UNINSTALL=@PYTHONMOD_UNINSTALL@ @@ -393,7 +394,7 @@ libunbound_wrap.lo libunbound_wrap.o: libunbound/python/libunbound_wrap.c \ unbound.h libunbound/python/libunbound_wrap.c: $(srcdir)/libunbound/python/libunbound.i unbound.h @-if test ! -d libunbound/python; then $(INSTALL) -d libunbound/python; fi - $(SWIG) -python -o $@ $(CPPFLAGS) $(srcdir)/libunbound/python/libunbound.i + $(SWIG) -python -o $@ $(CPPFLAGS) -DPY_MAJOR_VERSION=$(PY_MAJOR_VERSION) $(srcdir)/libunbound/python/libunbound.i # Pyunbound python unbound wrapper _unbound.la: libunbound_wrap.lo libunbound.la diff --git a/configure.ac b/configure.ac index 6028138..de809af 100644 --- a/configure.ac +++ b/configure.ac @@ -475,6 +475,8 @@ if test x_$ub_test_python != x_no; then AC_ERROR([Python version >= 2.4.0 is required]) fi + PY_MAJOR_VERSION="`$PYTHON -c "import sys; print(sys.version_info.major)"`" + AC_SUBST(PY_MAJOR_VERSION) # Have Python AC_DEFINE(HAVE_PYTHON,1,[Define if you have Python libraries and header files.]) LIBS="$PYTHON_LDFLAGS $LIBS" diff --git a/libunbound/python/file_py3.i b/libunbound/python/file_py3.i new file mode 100644 index 0000000..5d8b5a2 --- /dev/null +++ b/libunbound/python/file_py3.i @@ -0,0 +1,155 @@ +/* + * file_py3.i: Typemaps for FILE* for Python 3 + * + * Copyright (c) 2011, Karel Slany (karel.slany AT nic.cz) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +%{ +#include +#include +%} + +%types(FILE *); + +//#define SWIG_FILE3_DEBUG + +/* converts basic file descriptor flags onto a string */ +%fragment("fdfl_to_str", "header") { +const char * +fdfl_to_str(int fdfl) { + + static const char * const file_mode[] = {"w+", "w", "r"}; + + if (fdfl & O_RDWR) { + return file_mode[0]; + } else if (fdfl & O_WRONLY) { + return file_mode[1]; + } else { + return file_mode[2]; + } +} +} + +%fragment("is_obj_file", "header") { +int +is_obj_file(PyObject *obj) { + int fd, fdfl; + if (!PyLong_Check(obj) && /* is not an integer */ + PyObject_HasAttrString(obj, "fileno") && /* has fileno method */ + (PyObject_CallMethod(obj, "flush", NULL) != NULL) && /* flush() succeeded */ + ((fd = PyObject_AsFileDescriptor(obj)) != -1) && /* got file descriptor */ + ((fdfl = fcntl(fd, F_GETFL)) != -1) /* got descriptor flags */ + ) { + return 1; + } + else { + return 0; + } +} +} + +%fragment("obj_to_file","header", fragment="fdfl_to_str,is_obj_file") { +FILE * +obj_to_file(PyObject *obj) { + int fd, fdfl; + FILE *fp; + if (is_obj_file(obj)) { + fd = PyObject_AsFileDescriptor(obj); + fdfl = fcntl(fd, F_GETFL); + fp = fdopen(dup(fd), fdfl_to_str(fdfl)); /* the FILE* must be flushed + and closed after being used */ +#ifdef SWIG_FILE3_DEBUG + fprintf(stderr, "opening fd %d (fl %d \"%s\") as FILE %p\n", + fd, fdfl, fdfl_to_str(fdfl), (void *)fp); +#endif + return fp; + } + return NULL; +} +} + +/* returns -1 if error occurred */ +/* caused magic SWIG Syntax errors when was commented out */ +#if 0 +%fragment("dispose_file", "header") { +int +dispose_file(FILE **fp) { +#ifdef SWIG_FILE3_DEBUG + fprintf(stderr, "flushing FILE %p\n", (void *)fp); +#endif + if (*fp == NULL) { + return 0; + } + if ((fflush(*fp) == 0) && /* flush file */ + (fclose(*fp) == 0)) { /* close file */ + *fp = NULL; + return 0; + } + return -1; +} +} +#endif + +%typemap(arginit, noblock = 1) FILE* { + $1 = NULL; +} + +/* + * added due to ub_ctx_debugout since since it is overloaded: + * takes void* and FILE*. In reality only FILE* but the wrapper + * and the function is declared in such way. + */ +%typemap(typecheck, noblock = 1, fragment = "is_obj_file", precedence = SWIG_TYPECHECK_POINTER) FILE* { + $1 = is_obj_file($input); +} + +%typemap(check, noblock = 1) FILE* { + if ($1 == NULL) { + /* The generated wrapper function raises TypeError on mismatching types. */ + SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " + "$argnum"" of type '" "$type""'"); + } +} + +%typemap(in, noblock = 1, fragment = "obj_to_file") FILE* { + $1 = obj_to_file($input); +} + +/* + * Commented out due the way how ub_ctx_debugout() uses the parameter. + * This typemap would cause the FILE* to be closed after return from + * the function. This caused Python interpreter to crash, since the + * function just stores the FILE* internally in ctx and use it for + * logging. So we'll leave the closing of the file on the OS. + */ +/*%typemap(freearg, noblock = 1, fragment = "dispose_file") FILE* { + if (dispose_file(&$1) == -1) { + SWIG_exception_fail(SWIG_IOError, "closing file in method '" "$symname" "', argument " + "$argnum"" of type '" "$type""'"); + } +}*/ diff --git a/libunbound/python/libunbound.i b/libunbound/python/libunbound.i index 1bef79f..3c0e45b 100644 --- a/libunbound/python/libunbound.i +++ b/libunbound/python/libunbound.i @@ -60,7 +60,11 @@ %} //%include "doc.i" +#if PY_MAJOR_VERSION >= 3 +%include "file_py3.i" // python 3 FILE * +#else %include "file.i" +#endif %feature("docstring") strerror "Convert error value to a human readable string." diff --git a/pythonmod/interface.i b/pythonmod/interface.i index cfaabb3..4b20c6e 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -37,7 +37,7 @@ %include "stdint.i" // uint_16_t can be known type now %inline %{ - //converts [len][data][len][data][0] string to a List of labels (PyStrings) + //converts [len][data][len][data][0] string to a List of labels (PyBytes) PyObject* GetNameAsLabelList(const char* name, int len) { PyObject* list; int cnt=0, i; @@ -79,8 +79,8 @@ struct query_info { %inline %{ enum enum_rr_class { RR_CLASS_IN = 1, - RR_CLASS_CH = 3, - RR_CLASS_HS = 4, + RR_CLASS_CH = 3, + RR_CLASS_HS = 4, RR_CLASS_NONE = 254, RR_CLASS_ANY = 255, }; @@ -164,7 +164,7 @@ struct query_info { char buf[LDNS_MAX_DOMAINLEN+1]; buf[0] = '\0'; dname_str((uint8_t*)dname, buf); - return PyString_FromString(buf); + return PyBytes_FromString(buf); } %} @@ -440,7 +440,7 @@ struct comm_reply { reply_addr2str(reply, dest, 64); if (dest[0] == 0) return Py_None; - return PyString_FromString(dest); + return PyBytes_FromString(dest); } PyObject* _comm_reply_family_get(struct comm_reply* reply) { @@ -448,9 +448,9 @@ struct comm_reply { int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family; switch(af) { - case AF_INET: return PyString_FromString("ip4"); - case AF_INET6: return PyString_FromString("ip6"); - case AF_UNIX: return PyString_FromString("unix"); + case AF_INET: return PyBytes_FromString("ip4"); + case AF_INET6: return PyBytes_FromString("ip6"); + case AF_UNIX: return PyBytes_FromString("unix"); } return Py_None; @@ -711,13 +711,13 @@ struct delegpt { %inline %{ PyObject* _get_dp_dname(struct delegpt* dp) { - return PyString_FromStringAndSize((char*)dp->name, dp->namelen); + return PyBytes_FromStringAndSize((char*)dp->name, dp->namelen); } PyObject* _get_dp_dname_components(struct delegpt* dp) { return GetNameAsLabelList((char*)dp->name, dp->namelen); } PyObject* _get_dpns_dname(struct delegpt_ns* dpns) { - return PyString_FromStringAndSize((char*)dpns->name, dpns->namelen); + return PyBytes_FromStringAndSize((char*)dpns->name, dpns->namelen); } PyObject* _get_dpns_dname_components(struct delegpt_ns* dpns) { return GetNameAsLabelList((char*)dpns->name, dpns->namelen); @@ -728,7 +728,7 @@ struct delegpt { delegpt_addr_addr2str(target, dest, 64); if (dest[0] == 0) return Py_None; - return PyString_FromString(dest); + return PyBytes_FromString(dest); } %} @@ -842,7 +842,7 @@ int checkList(PyObject *l) for (i=0; i < PyList_Size(l); i++) { item = PyList_GetItem(l, i); - if (!PyString_Check(item)) + if (!PyBytes_Check(item)) return 0; } return 1; @@ -864,12 +864,12 @@ int pushRRList(sldns_buffer* qb, PyObject *l, uint32_t default_ttl, int qsec, len = sldns_buffer_remaining(qb); if(qsec) { - if(sldns_str2wire_rr_question_buf(PyString_AsString(item), + if(sldns_str2wire_rr_question_buf(PyBytes_AsString(item), sldns_buffer_current(qb), &len, NULL, NULL, 0, NULL, 0) != 0) return 0; } else { - if(sldns_str2wire_rr_buf(PyString_AsString(item), + if(sldns_str2wire_rr_buf(PyBytes_AsString(item), sldns_buffer_current(qb), &len, NULL, default_ttl, NULL, 0, NULL, 0) != 0) return 0; diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index 190d41a..48dbc01 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -133,7 +133,13 @@ int pythonmod_init(struct module_env* env, int id) /* Initialize Python libraries */ if (!Py_IsInitialized()) { - Py_SetProgramName("unbound"); +#if PY_MAJOR_VERSION >= 3 + wchar_t progname[8]; + mbstowcs(progname, "unbound", 8); +#else + char *progname = "unbound"; +#endif + Py_SetProgramName(progname); Py_NoSiteFlag = 1; Py_Initialize(); PyEval_InitThreads(); -- 2.1.0