Skip to content

Commit

Permalink
core: add support for CAA queries
Browse files Browse the repository at this point in the history
  • Loading branch information
saghul committed May 13, 2021
1 parent dfe00f3 commit 922b95a
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 17 deletions.
8 changes: 8 additions & 0 deletions docs/channel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand All @@ -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
Expand Down
15 changes: 14 additions & 1 deletion src/_cffi_src/build_cares.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ...
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -589,4 +603,3 @@
ffi = cffi.FFI()
ffi.cdef(PLATFORM_TYPES + TYPES + FUNCTIONS + CALLBACKS)
ffi.set_source('_cares', INCLUDES)

57 changes: 41 additions & 16 deletions src/pycares/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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

2 changes: 2 additions & 0 deletions src/pycares/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down
11 changes: 11 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 922b95a

Please sign in to comment.