]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Add patch from Jan Vcelak for pythonmod,
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 20 Nov 2018 12:24:40 +0000 (12:24 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 20 Nov 2018 12:24:40 +0000 (12:24 +0000)
  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

doc/Changelog
pythonmod/doc/modules/functions.rst
pythonmod/doc/modules/struct.rst
pythonmod/examples/inplace_callbacks.py
pythonmod/interface.i
pythonmod/pythonmod.h
util/fptr_wlist.c

index 2048dfa9b253f4e992d85a36fdd3ecb555dc048f..ad30fb054d7523f67ebcf209dacccfc830b8bdfc 100644 (file)
@@ -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
index 49ea7bfa45f0910364bdc7e1af496788b7e124e6..43c66eb380dd702bef05dc5008f8c8ec457969bd 100644 (file)
@@ -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
 -------
index c74298b8b75d222117cfd510440c5414fea30362..de7c084e9008ca008a7a23595fe4f9ddb66df98e 100644 (file)
@@ -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.
index e47fc8292be27e13ba41aecc2938f9d8bf473a98..776add8c29adf84b7f71734b4d6c22447a3a8c61 100644 (file)
@@ -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
 
 
index 96accb69cca6e48e7405204f818f5a46cd796ab6..6bb2b9edb240593661f796b96f8ea364548031e6 100644 (file)
@@ -12,6 +12,8 @@
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
+   #include <netdb.h>
+   #include <sys/un.h>
    #include <stdarg.h>
    #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);
index 991ae51a20a74751041aed84a6020294b097c4e9..ae8af27eb22b4d6342a5a4836e128cc39b37d186 100644 (file)
@@ -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 */
index 271d862569835378dc216a7582014b30a8069ba8..02f85e8dc4ab756a1f1bc539323411e11ee5d23e 100644 (file)
@@ -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;
 }