diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index 53d4a2f3669..7d24f88c231 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -60,31 +60,42 @@ def __init__(self, loop=None, *args, **kwargs): if not hasattr(self._resolver, 'gethostbyname'): # aiodns 1.1 is not available, fallback to DNSResolver.query - self.resolve = self.resolve_with_query + self.resolve = self._resolve_with_query @asyncio.coroutine def resolve(self, host, port=0, family=socket.AF_INET): + try: + resp = yield from self._resolver.gethostbyname(host, family) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(msg) from exc hosts = [] - resp = yield from self._resolver.gethostbyname(host, family) - for address in resp.addresses: hosts.append( {'hostname': host, 'host': address, 'port': port, 'family': family, 'proto': 0, 'flags': socket.AI_NUMERICHOST}) + + if not hosts: + raise OSError("DNS lookup failed") + return hosts @asyncio.coroutine - def resolve_with_query(self, host, port=0, family=socket.AF_INET): + def _resolve_with_query(self, host, port=0, family=socket.AF_INET): if family == socket.AF_INET6: qtype = 'AAAA' else: qtype = 'A' - hosts = [] - resp = yield from self._resolver.query(host, qtype) + try: + resp = yield from self._resolver.query(host, qtype) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(msg) from exc + hosts = [] for rr in resp: hosts.append( {'hostname': host, @@ -92,6 +103,9 @@ def resolve_with_query(self, host, port=0, family=socket.AF_INET): 'family': family, 'proto': 0, 'flags': socket.AI_NUMERICHOST}) + if not hosts: + raise OSError("DNS lookup failed") + return hosts @asyncio.coroutine diff --git a/changes/2231.bugfix b/changes/2231.bugfix new file mode 100644 index 00000000000..f4fd216958c --- /dev/null +++ b/changes/2231.bugfix @@ -0,0 +1,2 @@ +Raise OSError on async DNS lookup if resolved domain is an alias for +another one, which does not have an A or CNAME record. diff --git a/tests/test_resolver.py b/tests/test_resolver.py index b824bdbb412..826397b083e 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -103,7 +103,7 @@ def test_async_resolver_negative_lookup(loop): with patch('aiodns.DNSResolver') as mock: mock().gethostbyname.side_effect = aiodns.error.DNSError() resolver = AsyncResolver(loop=loop) - with pytest.raises(aiodns.error.DNSError): + with pytest.raises(OSError): yield from resolver.resolve('doesnotexist.bla') @@ -114,7 +114,28 @@ def test_async_resolver_query_negative_lookup(loop): del mock().gethostbyname mock().query.side_effect = aiodns.error.DNSError() resolver = AsyncResolver(loop=loop) - with pytest.raises(aiodns.error.DNSError): + with pytest.raises(OSError): + yield from resolver.resolve('doesnotexist.bla') + + +@pytest.mark.skipif(aiodns is None, reason="aiodns required") +@asyncio.coroutine +def test_async_resolver_no_hosts_in_query(loop): + with patch('aiodns.DNSResolver') as mock: + del mock().gethostbyname + mock().query.return_value = fake_query_result([]) + resolver = AsyncResolver(loop=loop) + with pytest.raises(OSError): + yield from resolver.resolve('doesnotexist.bla') + + +@pytest.mark.skipif(not gethostbyname, reason="aiodns 1.1 required") +@asyncio.coroutine +def test_async_resolver_no_hosts_in_gethostbyname(loop): + with patch('aiodns.DNSResolver') as mock: + mock().gethostbyname.return_value = fake_result([]) + resolver = AsyncResolver(loop=loop) + with pytest.raises(OSError): yield from resolver.resolve('doesnotexist.bla')