From 922b95a406a84d6cba6b17618e3b7f65f5fa11d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Thu, 13 May 2021 22:53:12 +0200 Subject: [PATCH] core: add support for CAA queries --- docs/channel.rst | 8 +++++ src/_cffi_src/build_cares.py | 15 +++++++++- src/pycares/__init__.py | 57 ++++++++++++++++++++++++++---------- src/pycares/__main__.py | 2 ++ tests/tests.py | 11 +++++++ 5 files changed, 76 insertions(+), 17 deletions(-) diff --git a/docs/channel.rst b/docs/channel.rst index 07ab006..3934041 100644 --- a/docs/channel.rst +++ b/docs/channel.rst @@ -127,6 +127,7 @@ - ``QUERY_TYPE_A`` - ``QUERY_TYPE_AAAA`` - ``QUERY_TYPE_ANY`` + - ``QUERY_TYPE_CAA`` - ``QUERY_TYPE_CNAME`` - ``QUERY_TYPE_MX`` - ``QUERY_TYPE_NAPTR`` @@ -149,6 +150,13 @@ - host - ttl + - CAA: (list of) ``ares_query_caa_result``, fields: + + - critical + - property + - value + - ttl + - CNAME: ``ares_query_cname_result``, fields: - cname diff --git a/src/_cffi_src/build_cares.py b/src/_cffi_src/build_cares.py index ace96b8..77f03aa 100644 --- a/src/_cffi_src/build_cares.py +++ b/src/_cffi_src/build_cares.py @@ -77,6 +77,7 @@ #define T_A ... #define T_AAAA ... #define T_ANY ... +#define T_CAA ... #define T_CNAME ... #define T_MX ... #define T_NAPTR ... @@ -252,6 +253,15 @@ int ttl; }; +struct ares_caa_reply { + struct ares_caa_reply *next; + int critical; + unsigned char *property; + size_t plength; + unsigned char *value; + size_t length; +}; + struct ares_srv_reply { struct ares_srv_reply *next; char *host; @@ -492,6 +502,10 @@ struct ares_addr6ttl *addrttls, int *naddrttls); +int ares_parse_caa_reply(const unsigned char* abuf, + int alen, + struct ares_caa_reply** caa_out); + int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, @@ -589,4 +603,3 @@ ffi = cffi.FFI() ffi.cdef(PLATFORM_TYPES + TYPES + FUNCTIONS + CALLBACKS) ffi.set_source('_cares', INCLUDES) - diff --git a/src/pycares/__init__.py b/src/pycares/__init__.py index befcd15..b228829 100644 --- a/src/pycares/__init__.py +++ b/src/pycares/__init__.py @@ -54,24 +54,25 @@ exported_pycares_symbols_map = { # Query types - "QUERY_TYPE_A" : "T_A", - "QUERY_TYPE_AAAA" : "T_AAAA", - "QUERY_TYPE_ANY" : "T_ANY", + "QUERY_TYPE_A" : "T_A", + "QUERY_TYPE_AAAA" : "T_AAAA", + "QUERY_TYPE_ANY" : "T_ANY", + "QUERY_TYPE_CAA" : "T_CAA", "QUERY_TYPE_CNAME" : "T_CNAME", - "QUERY_TYPE_MX" : "T_MX", + "QUERY_TYPE_MX" : "T_MX", "QUERY_TYPE_NAPTR" : "T_NAPTR", - "QUERY_TYPE_NS" : "T_NS", - "QUERY_TYPE_PTR" : "T_PTR", - "QUERY_TYPE_SOA" : "T_SOA", - "QUERY_TYPE_SRV" : "T_SRV", - "QUERY_TYPE_TXT" : "T_TXT", + "QUERY_TYPE_NS" : "T_NS", + "QUERY_TYPE_PTR" : "T_PTR", + "QUERY_TYPE_SOA" : "T_SOA", + "QUERY_TYPE_SRV" : "T_SRV", + "QUERY_TYPE_TXT" : "T_TXT", # Query classes - "QUERY_CLASS_IN": "C_IN", + "QUERY_CLASS_IN" : "C_IN", "QUERY_CLASS_CHAOS": "C_CHAOS", - "QUERY_CLASS_HS": "C_HS", - "QUERY_CLASS_NONE":"C_NONE", - "QUERY_CLASS_ANY": "C_ANY", + "QUERY_CLASS_HS" : "C_HS", + "QUERY_CLASS_NONE" :"C_NONE", + "QUERY_CLASS_ANY" : "C_ANY", } for k, v in exported_pycares_symbols_map.items(): @@ -131,7 +132,7 @@ def _query_cb(arg, status, timeouts, abuf, alen): if status == _lib.ARES_SUCCESS: if query_type == _lib.T_ANY: result = [] - for qtype in (_lib.T_A, _lib.T_AAAA, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT): + for qtype in (_lib.T_A, _lib.T_AAAA, _lib.T_CAA, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT): r, status = parse_result(qtype, abuf, alen) if status not in (None, _lib.ARES_ENODATA, _lib.ARES_EBADRESP): result = None @@ -184,6 +185,20 @@ def parse_result(query_type, abuf, alen): else: result = [ares_query_aaaa_result(addrttls[i]) for i in range(naddrttls[0])] status = None + elif query_type == _lib.T_CAA: + caa_reply = _ffi.new("struct ares_caa_reply **") + parse_status = _lib.ares_parse_caa_reply(abuf, alen, caa_reply) + if parse_status != _lib.ARES_SUCCESS: + result = None + status = parse_status + else: + result = [] + caa_reply_ptr = caa_reply[0] + while caa_reply_ptr != _ffi.NULL: + result.append(ares_query_caa_result(caa_reply_ptr)) + caa_reply_ptr = caa_reply_ptr.next + _lib.ares_free_data(caa_reply[0]) + status = None elif query_type == _lib.T_CNAME: host = _ffi.new("struct hostent **") parse_status = _lib.ares_parse_a_reply(abuf, alen, host, _ffi.NULL, _ffi.NULL) @@ -309,7 +324,7 @@ def parse_result(query_type, abuf, alen): class Channel: - __qtypes__ = (_lib.T_A, _lib.T_AAAA, _lib.T_ANY, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT) + __qtypes__ = (_lib.T_A, _lib.T_AAAA, _lib.T_ANY, _lib.T_CAA, _lib.T_CNAME, _lib.T_MX, _lib.T_NAPTR, _lib.T_NS, _lib.T_PTR, _lib.T_SOA, _lib.T_SRV, _lib.T_TXT) __qclasses__ = (_lib.C_IN, _lib.C_CHAOS, _lib.C_HS, _lib.C_NONE, _lib.C_ANY) def __init__(self, @@ -641,6 +656,17 @@ def __init__(self, ares_addrttl): self.ttl = ares_addrttl.ttl +class ares_query_caa_result(AresResult): + __slots__ = ('critical', 'property', 'value', 'ttl') + type = 'CAA' + + def __init__(self, caa): + self.critical = caa.critical + self.property = maybe_str(_ffi.string(caa.property, caa.plength)) + self.value = maybe_str(_ffi.string(caa.value, caa.length)) + self.ttl = -1 + + class ares_query_cname_result(AresResult): __slots__ = ('cname', 'ttl') type = 'CNAME' @@ -827,4 +853,3 @@ def __init__(self, ares_addrinfo): __all__ = exported_pycares_symbols + list(exported_pycares_symbols_map.keys()) + ['AresError', 'Channel', 'errno', '__version__'] del exported_pycares_symbols, exported_pycares_symbols_map - diff --git a/src/pycares/__main__.py b/src/pycares/__main__.py index 5fa6895..e2adce8 100644 --- a/src/pycares/__main__.py +++ b/src/pycares/__main__.py @@ -40,6 +40,8 @@ def cb(result, error): txt = '%s\t\t%d\tIN\t%s' % (hostname, r.ttl, r.type) if r.type in ('A', 'AAAA'): parts.append('%s\t%s' % (txt, r.host)) + elif r.type == 'CAA': + parts.append('%s\t%d %s "%s"' % (txt, r.critical, r.property, r.value)) elif r.type == 'CNAME': parts.append('%s\t%s' % (txt, r.cname)) elif r.type == 'MX': diff --git a/tests/tests.py b/tests/tests.py index 254e1ce..164f1d0 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -230,6 +230,17 @@ def cb(result, errorno): self.assertEqual(type(r), pycares.ares_query_aaaa_result) self.assertNotEqual(r.host, None) + def test_query_caa(self): + self.result, self.errorno = None, None + def cb(result, errorno): + self.result, self.errorno = result, errorno + self.channel.query('wikipedia.org', pycares.QUERY_TYPE_CAA, cb) + self.wait() + self.assertNoError(self.errorno) + self.assertTrue(len(self.result) > 0) + for r in self.result: + self.assertEqual(type(r), pycares.ares_query_caa_result) + def test_query_cname(self): self.result, self.errorno = None, None def cb(result, errorno):