From: Wouter Wijngaards Date: Tue, 20 Nov 2018 12:24:40 +0000 (+0000) Subject: - Add patch from Jan Vcelak for pythonmod, X-Git-Tag: release-1.8.2rc1~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=04d73b9192da180b4ddd3c2d219a03b2198a4e55;p=thirdparty%2Funbound.git - Add patch from Jan Vcelak for pythonmod, add sockaddr_storage getters, add support for query callbacks, allow raw address access via comm_reply and update API documentation. git-svn-id: file:///svn/unbound/trunk@4962 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 2048dfa9b..ad30fb054 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,9 @@ 20 November 2018: Wouter - Scrub NS records from NXDOMAIN responses to stop fragmentation poisoning of the cache. + - Add patch from Jan Vcelak for pythonmod, + add sockaddr_storage getters, add support for query callbacks, + allow raw address access via comm_reply and update API documentation. 19 November 2018: Wouter - Support SO_REUSEPORT_LB in FreeBSD 12 with the so-reuseport: yes diff --git a/pythonmod/doc/modules/functions.rst b/pythonmod/doc/modules/functions.rst index 49ea7bfa4..43c66eb38 100644 --- a/pythonmod/doc/modules/functions.rst +++ b/pythonmod/doc/modules/functions.rst @@ -103,42 +103,67 @@ Inplace callbacks :param opt_list_out: :class:`edns_option`. EDNS option list to append options to. :param region: :class:`regional` -.. function:: register_inplace_cb_reply(py_cb, env) +.. function:: inplace_cb_query(qinfo, flags, qstate, addr, zone, region) + + Function prototype for callback functions used in + `register_inplace_cb_query`_. + + :param qinfo: :class:`query_info` + :param flags: query flags (integer) + :param qstate: :class:`module_qstate` + :param addr: :class:`sockaddr_storage` + :param zone: zone name in wire format (bytes) + :param region: :class:`regional` + +.. function:: register_inplace_cb_reply(py_cb, env, id) Register py_cb as an inplace reply callback function. :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable. :param env: :class:`module_env` + :param id: Module ID. :return: True on success, False otherwise :rtype: boolean -.. function:: register_inplace_cb_reply_cache(py_cb, env) +.. function:: register_inplace_cb_reply_cache(py_cb, env, id) Register py_cb as an inplace reply_cache callback function. :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable. :param env: :class:`module_env` + :param id: Module ID. :return: True on success, False otherwise :rtype: boolean -.. function:: register_inplace_cb_reply_local(py_cb, env) +.. function:: register_inplace_cb_reply_local(py_cb, env, id) Register py_cb as an inplace reply_local callback function. :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable. :param env: :class:`module_env` + :param id: Module ID. :return: True on success, False otherwise :rtype: boolean -.. function:: register_inplace_cb_reply_servfail(py_cb, env) +.. function:: register_inplace_cb_reply_servfail(py_cb, env, id) Register py_cb as an inplace reply_servfail callback function. :param py_cb: Python function that follows `inplace_cb_reply`_'s prototype. **Must** be callable. :param env: :class:`module_env` + :param id: Module ID. :return: True on success, False otherwise :rtype: boolean +.. function:: register_inplace_cb_query(py_cb, env, id) + + Register py_cb as an inplace query callback function. + + :param py_cb: Python function that follows `inplace_cb_query`_'s prototype. **Must** be callable. + :param env: :class:`module_env` + :param id: Module ID. + :return: True on success, False otherwise + :rtype: boolean Logging ------- diff --git a/pythonmod/doc/modules/struct.rst b/pythonmod/doc/modules/struct.rst index c74298b8b..de7c084e9 100644 --- a/pythonmod/doc/modules/struct.rst +++ b/pythonmod/doc/modules/struct.rst @@ -514,3 +514,33 @@ pythonmod_qstate Here you can keep your own private data (each thread has own data object). +sockaddr_storage +------------------------- + +.. class:: sockaddr_storage + + The :class:`sockaddr_storage` provides these data attributes: + + .. attribute:: family + + Address family name as a string. Possible values are `ip4`, `ip6`, and `unix`. + + .. attribute:: addr + + Address in presentation format. + + .. attribute:: raw_addr + + Address in network wire format. + + .. attribute:: port + + Port number. Invalid for Unix address. + + .. attribute:: flowinfo + + Flow info value. Valid only for IPv6 address. + + .. attribute:: scope_id + + Scope ID value. Valid only for IPv6 address. diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py index e47fc8292..776add8c2 100644 --- a/pythonmod/examples/inplace_callbacks.py +++ b/pythonmod/examples/inplace_callbacks.py @@ -247,6 +247,25 @@ def inplace_servfail_callback(qinfo, qstate, rep, rcode, edns, opt_list_out, return True +def inplace_query_callback(qinfo, flags, qstate, addr, zone, region, **kwargs): + """ + Function that will be registered as an inplace callback function. + It will be called before sending a query to a backend server. + + :param qinfo: query_info struct; + :param flags: flags of the query; + :param qstate: module qstate. opt_lists are available here; + :param addr: struct sockaddr_storage. Address of the backend server; + :param zone: zone name in binary; + :param region: region to allocate temporary data. Needs to be used when we + want to append a new option to opt_lists. + :param **kwargs: Dictionary that may contain parameters added in a future + release. + """ + log_info("python: outgoing query to {}@{}".format(addr.addr, addr.port)) + return True + + def init_standard(id, env): """ New version of the init function. @@ -281,6 +300,11 @@ def init_standard(id, env): if not register_inplace_cb_reply_servfail(inplace_servfail_callback, env, id): return False + # Register the inplace_query_callback function as an inplace callback + # before sending a query to a backend server. + if not register_inplace_cb_query(inplace_query_callback, env, id): + return False + return True diff --git a/pythonmod/interface.i b/pythonmod/interface.i index 96accb69c..6bb2b9edb 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -12,6 +12,8 @@ #include #include #include + #include + #include #include #include "config.h" #include "util/log.h" @@ -427,63 +429,182 @@ struct dns_msg { } /* ************************************************************************************ * - Structure mesh_state + Structure sockaddr_storage * ************************************************************************************ */ -struct mesh_state { - struct mesh_reply* reply_list; -}; -struct mesh_reply { - struct mesh_reply* next; - struct comm_reply query_reply; -}; +struct sockaddr_storage {}; -struct comm_reply { +%inline %{ + static size_t _sockaddr_storage_len(const struct sockaddr_storage *ss) { + if (ss == NULL) { + return 0; + } -}; + switch (ss->ss_family) { + case AF_INET: return sizeof(struct sockaddr_in); + case AF_INET6: return sizeof(struct sockaddr_in6); + case AF_UNIX: return sizeof(struct sockaddr_un); + default: + return 0; + } + } -%inline %{ + PyObject *_sockaddr_storage_family(const struct sockaddr_storage *ss) { + if (ss == NULL) { + return Py_None; + } + + switch (ss->ss_family) { + case AF_INET: return PyUnicode_FromString("ip4"); + case AF_INET6: return PyUnicode_FromString("ip6"); + case AF_UNIX: return PyUnicode_FromString("unix"); + default: + return Py_None; + } + } + + PyObject *_sockaddr_storage_addr(const struct sockaddr_storage *ss) { + if (ss == NULL) { + return Py_None; + } + + const struct sockaddr *sa = (struct sockaddr *)ss; + size_t sa_len = _sockaddr_storage_len(ss); + if (sa_len == 0) { + return Py_None; + } + + char name[NI_MAXHOST] = {0}; + if (getnameinfo(sa, sa_len, name, sizeof(name), NULL, 0, NI_NUMERICHOST) != 0) { + return Py_None; + } + + return PyUnicode_FromString(name); + } + + PyObject *_sockaddr_storage_raw_addr(const struct sockaddr_storage *ss) { + if (ss == NULL) { + return Py_None; + } + + size_t sa_len = _sockaddr_storage_len(ss); + if (sa_len == 0) { + return Py_None; + } + + if (ss->ss_family == AF_INET) { + const struct sockaddr_in *sa = (struct sockaddr_in *)ss; + const struct in_addr *raw = (struct in_addr *)&sa->sin_addr; + return PyBytes_FromStringAndSize((const char *)raw, sizeof(*raw)); + } + + if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ss; + const struct in6_addr *raw = (struct in6_addr *)&sa->sin6_addr; + return PyBytes_FromStringAndSize((const char *)raw, sizeof(*raw)); + } + + if (ss->ss_family == AF_UNIX) { + const struct sockaddr_un *sa = (struct sockaddr_un *)ss; + return PyBytes_FromString(sa->sun_path); + } - PyObject* _comm_reply_addr_get(struct comm_reply* reply) { - char dest[64]; - reply_addr2str(reply, dest, 64); - if (dest[0] == 0) return Py_None; - return PyBytes_FromString(dest); - } + } - PyObject* _comm_reply_family_get(struct comm_reply* reply) { + PyObject *_sockaddr_storage_port(const struct sockaddr_storage *ss) { + if (ss == NULL) { + return Py_None; + } - int af = (int)((struct sockaddr_in*) &(reply->addr))->sin_family; + if (ss->ss_family == AF_INET) { + const struct sockaddr_in *sa4 = (struct sockaddr_in *)ss; + return PyInt_FromLong(ntohs(sa4->sin_port)); + } - switch(af) { - case AF_INET: return PyBytes_FromString("ip4"); - case AF_INET6: return PyBytes_FromString("ip6"); - case AF_UNIX: return PyBytes_FromString("unix"); + if (ss->ss_family == AF_INET6) { + const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ss; + return PyInt_FromLong(ntohs(sa6->sin6_port)); } return Py_None; - } + } - PyObject* _comm_reply_port_get(struct comm_reply* reply) { - uint16_t port; - port = ntohs(((struct sockaddr_in*)&(reply->addr))->sin_port); - return PyInt_FromLong(port); - } + PyObject *_sockaddr_storage_flowinfo(const struct sockaddr_storage *ss) { + if (ss == NULL || ss->ss_family != AF_INET6) { + return Py_None; + } + + const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ss; + return PyInt_FromLong(ntohl(sa6->sin6_flowinfo)); + } + + PyObject *_sockaddr_storage_scope_id(const struct sockaddr_storage *ss) { + if (ss == NULL || ss->ss_family != AF_INET6) { + return Py_None; + } + const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ss; + return PyInt_FromLong(ntohl(sa6->sin6_scope_id)); + } %} +%extend sockaddr_storage { + %pythoncode %{ + def _family_get(self): return _sockaddr_storage_family(self) + __swig_getmethods__["family"] = _family_get + if _newclass: family = _swig_property(_family_get) + + def _addr_get(self): return _sockaddr_storage_addr(self) + __swig_getmethods__["addr"] = _addr_get + if _newclass: addr = _swig_property(_addr_get) + + def _raw_addr_get(self): return _sockaddr_storage_raw_addr(self) + __swig_getmethods__["raw_addr"] = _raw_addr_get + if _newclass: raw_addr = _swig_property(_raw_addr_get) + + def _port_get(self): return _sockaddr_storage_port(self) + __swig_getmethods__["port"] = _port_get + if _newclass: port = _swig_property(_port_get) + + def _flowinfo_get(self): return _sockaddr_storage_flowinfo(self) + __swig_getmethods__["flowinfo"] = _flowinfo_get + if _newclass: flowinfo = _swig_property(_flowinfo_get) + + def _scope_id_get(self): return _sockaddr_storage_scope_id(self) + __swig_getmethods__["scope_id"] = _scope_id_get + if _newclass: scope_id = _swig_property(_scope_id_get) + %} +} + +/* ************************************************************************************ * + Structure mesh_state + * ************************************************************************************ */ +struct mesh_state { + struct mesh_reply* reply_list; +}; + +struct mesh_reply { + struct mesh_reply* next; + struct comm_reply query_reply; +}; + +%rename(_addr) comm_reply::addr; +struct comm_reply { + struct sockaddr_storage addr; +}; + %extend comm_reply { %pythoncode %{ - def _addr_get(self): return _comm_reply_addr_get(self) + def _addr_get(self): return _sockaddr_storage_addr(self._addr) __swig_getmethods__["addr"] = _addr_get if _newclass:addr = _swig_property(_addr_get) - def _port_get(self): return _comm_reply_port_get(self) + def _port_get(self): return _sockaddr_storage_port(self._addr) __swig_getmethods__["port"] = _port_get if _newclass:port = _swig_property(_port_get) - def _family_get(self): return _comm_reply_family_get(self) + def _family_get(self): return _sockaddr_storage_family(self._addr) __swig_getmethods__["family"] = _family_get if _newclass:family = _swig_property(_family_get) %} @@ -1437,6 +1558,54 @@ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, return python_inplace_cb_register(inplace_cb_reply_servfail, py_cb, env, id); } + + int python_inplace_cb_query_generic( + struct query_info* qinfo, uint16_t flags, struct module_qstate* qstate, + struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, struct regional* region, int id, + void* python_callback) + { + int res = 0; + PyObject *func = python_callback; + + PyGILState_STATE gstate = PyGILState_Ensure(); + + PyObject *py_qinfo = SWIG_NewPointerObj((void*) qinfo, SWIGTYPE_p_query_info, 0); + PyObject *py_qstate = SWIG_NewPointerObj((void*) qstate, SWIGTYPE_p_module_qstate, 0); + PyObject *py_addr = SWIG_NewPointerObj((void *) addr, SWIGTYPE_p_sockaddr_storage, 0); + PyObject *py_zone = PyBytes_FromStringAndSize((const char *)zone, zonelen); + PyObject *py_region = SWIG_NewPointerObj((void*) region, SWIGTYPE_p_regional, 0); + + PyObject *py_args = Py_BuildValue("(OiOOOO)", py_qinfo, flags, py_qstate, py_addr, py_zone, py_region); + PyObject *py_kwargs = Py_BuildValue("{}"); + PyObject *result = PyObject_Call(func, py_args, py_kwargs); + if (result) { + res = PyInt_AsLong(result); + } + + Py_XDECREF(py_qinfo); + Py_XDECREF(py_qstate); + Py_XDECREF(py_addr); + Py_XDECREF(py_zone); + Py_XDECREF(py_region); + + Py_XDECREF(py_args); + Py_XDECREF(py_kwargs); + Py_XDECREF(result); + + PyGILState_Release(gstate); + + return res; + } + + static int register_inplace_cb_query(PyObject* py_cb, + struct module_env* env, int id) + { + int ret = inplace_cb_register(python_inplace_cb_query_generic, + inplace_cb_query, (void*) py_cb, env, id); + if (ret) Py_INCREF(py_cb); + return ret; + } %} /* C declarations */ int inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg, @@ -1451,3 +1620,5 @@ static int register_inplace_cb_reply_local(PyObject* py_cb, struct module_env* env, int id); static int register_inplace_cb_reply_servfail(PyObject* py_cb, struct module_env* env, int id); +static int register_inplace_cb_query(PyObject *py_cb, + struct module_env* env, int id); diff --git a/pythonmod/pythonmod.h b/pythonmod/pythonmod.h index 991ae51a2..ae8af27eb 100644 --- a/pythonmod/pythonmod.h +++ b/pythonmod/pythonmod.h @@ -74,4 +74,12 @@ int python_inplace_cb_reply_generic(struct query_info* qinfo, struct edns_data* edns, struct edns_option** opt_list_out, struct comm_reply* repinfo, struct regional* region, int id, void* python_callback); + +/** Declared here for fptr_wlist access. The definition is in interface.i. */ +int python_inplace_cb_query_generic( + struct query_info* qinfo, uint16_t flags, struct module_qstate* qstate, + struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, struct regional* region, int id, + void* python_callback); + #endif /* PYTHONMOD_H */ diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 271d86256..02f85e8dc 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -564,9 +564,12 @@ int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_type* fptr) #ifdef CLIENT_SUBNET if(fptr == &ecs_whitelist_check) return 1; -#else - (void)fptr; #endif +#ifdef WITH_PYTHONMODULE + if(fptr == &python_inplace_cb_query_generic) + return 1; +#endif + (void)fptr; return 0; }