Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions aiodnsresolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class DnsRecordDoesNotExist(DnsError):
pass


class DnsNoMatchingAnswers(DnsRecordDoesNotExist):
pass


class DnsPointerLoop(DnsError):
pass

Expand Down Expand Up @@ -153,7 +157,7 @@ def pack_name(name):
return b''.join(
bytes((len(part),)) + part
for part in name.split(b'.')
) + b'\0'
) + b'\0' if name else b'\0'

def pack_resource(record):
rdata = \
Expand Down Expand Up @@ -589,11 +593,12 @@ async def req():
last_exception = DnsResponseCode(res.rcode)
set_timeout_cause(last_exception)
logger.debug('Error from %s', addr_port)
elif name_error or (not cname_answers and not qtype_answers):
# a name error can be returned by some non-authoritative
# servers on not-existing, contradicting RFC 1035
elif name_error:
logger.debug('Record not found from %s', addr_port)
raise DnsRecordDoesNotExist()
elif not cname_answers and not qtype_answers:
logger.debug('No answers from %s', addr_port)
raise DnsNoMatchingAnswers()
else:
return cname_answers, qtype_answers

Expand Down
19 changes: 16 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
TYPES,
DnsCnameChainTooLong,
DnsError,
DnsNoMatchingAnswers,
DnsRecordDoesNotExist,
DnsSocketError,
DnsTimeout,
Expand Down Expand Up @@ -1772,6 +1773,18 @@ async def test_localhost_aaaa(self):
self.assertEqual(str(res[0]), '::1')
self.assertEqual(res[0].expires_at, loop.time())

@async_test
async def test_root_servers_queries(self):
# Upstream DNS server should return NOERROR status with an empty `A` records set.
# The default Github's does not do this, so we need to switch to Google's server.
self.addCleanup(patch_open(upstream_ip='8.8.8.8'))

resolve, _ = Resolver()
with self.assertRaises(DnsNoMatchingAnswers):
await resolve('', TYPES.A)
with self.assertRaises(DnsNoMatchingAnswers):
await resolve('root-servers.net', TYPES.A)


class TestMemoizedMutex(unittest.TestCase):
@async_test
Expand Down Expand Up @@ -1808,11 +1821,11 @@ async def func():
self.assertEqual(await task3, 42)


def patch_open():
def patch_open(upstream_ip='127.0.0.1'):
def mock_open(file_name, _):
lines = \
['127.0.0.1 localhost'] if file_name == '/etc/hosts' else \
['nameserver 127.0.0.1']
[f'{upstream_ip} localhost'] if file_name == '/etc/hosts' else \
[f'nameserver {upstream_ip}']

context_manager = MagicMock()
context_manager.__enter__.return_value = lines
Expand Down