././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1697440773.6330059 aiodns-3.1.1/0000755000175100001770000000000000000000000013577 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/ChangeLog0000644000175100001770000000344600000000000015360 0ustar00runnerdocker000000000000003.0.0 ===== - Release wheels and source to PyPI with GH actions - Try to make tests more resilient - Don't build universal wheels - Migrate CI to GH Actions - Fix TXT CHAOS test - Add support for CAA queries - Support Python >= 3.6 - Bump pycares dependency - Drop tasks.py - Allow specifying dnsclass for queries - Set URL to https - Add license args in setup.py - Converted Type Annotations to Py3 syntax Closes - Only run mypy on cpython versions - Also fix all type errors with latest mypy - pycares seems to have no typing / stubs so lets ignore it via `mypy.ini` - setup: typing exists since Python 3.5 - Fix type annotation of gethostbyname() - Updated README 2.0.0 ===== (changes since version 1.x) - Drop support for Python < 3.5 - Add support for ANY queries - Raise pycares dependency 2.0.0b2 ======= - Raise pycares dependency 2.0.0b1 ======= - Fix using typing on Python 3.7 2.0.0b0 ======= - Drop support for Python < 3.5 - Add support for ANY queries - Raise pycares dependency 1.2.0 ===== - Add support for Python 3.7 - Fix CNAME test - Add examples with `async` and `await` - Fix Python version check - Add gethostbyaddr 1.1.1 ===== - Use per-version requires for wheels 1.1.0 ===== - Add DNSResolver.gethostbyname() - Build universal wheels 1.0.1 ===== - Fix including tests and ChangeLog in source distributions 1.0.0 ===== - Use pycares >= 1.0.0 - Fix tests 0.3.2 ===== - setup: Fix decoding in non-UTF-8 environments 0.3.1 ===== - Adapt to Trollius package rename - Fixed stopping watching file descriptor 0.3.0 ===== - Add DNSResolver.cancel method - Handle case when the Future returned by query() is cancelled 0.2.0 ===== - Add support for Trollius - Don't make query() a coroutine, just return the future - Raise ValueError if specified query type is invalid 0.1.0 ===== - Initial release ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/LICENSE0000644000175100001770000000205600000000000014607 0ustar00runnerdocker00000000000000Copyright (C) 2014 by Saúl Ibarra Corretgé Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/MANIFEST.in0000644000175100001770000000022500000000000015334 0ustar00runnerdocker00000000000000include README.rst LICENSE ChangeLog include setup.py tests.py include aiodns/py.typed recursive-exclude * __pycache__ recursive-exclude * *.py[co] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1697440773.6330059 aiodns-3.1.1/PKG-INFO0000644000175100001770000001126600000000000014702 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: aiodns Version: 3.1.1 Summary: Simple DNS resolver for asyncio Home-page: https://github.com/saghul/aiodns Author: Saúl Ibarra Corretgé Author-email: s@saghul.net License: MIT Description: =============================== Simple DNS resolver for asyncio =============================== .. image:: https://badge.fury.io/py/aiodns.png :target: https://pypi.org/project/aiodns/ .. image:: https://github.com/saghul/aiodns/workflows/CI/badge.svg :target: https://github.com/saghul/aiodns/actions aiodns provides a simple way for doing asynchronous DNS resolutions using `pycares `_. Example ======= .. code:: python import asyncio import aiodns loop = asyncio.get_event_loop() resolver = aiodns.DNSResolver(loop=loop) async def query(name, query_type): return await resolver.query(name, query_type) coro = query('google.com', 'A') result = loop.run_until_complete(coro) The following query types are supported: A, AAAA, ANY, CAA, CNAME, MX, NAPTR, NS, PTR, SOA, SRV, TXT. API === The API is pretty simple, three functions are provided in the ``DNSResolver`` class: * ``query(host, type)``: Do a DNS resolution of the given type for the given hostname. It returns an instance of ``asyncio.Future``. The actual result of the DNS query is taken directly from pycares. As of version 1.0.0 of aiodns (and pycares, for that matter) results are always namedtuple-like objects with different attributes. Please check the `documentation `_ for the result fields. * ``gethostbyname(host, socket_family)``: Do a DNS resolution for the given hostname and the desired type of address family (i.e. ``socket.AF_INET``). While ``query()`` always performs a request to a DNS server, ``gethostbyname()`` first looks into ``/etc/hosts`` and thus can resolve local hostnames (such as ``localhost``). Please check `the documentation `_ for the result fields. The actual result of the call is a ``asyncio.Future``. * ``gethostbyaddr(name)``: Make a reverse lookup for an address. * ``cancel()``: Cancel all pending DNS queries. All futures will get ``DNSError`` exception set, with ``ARES_ECANCELLED`` errno. Note for Windows users ====================== This library requires the asyncio loop to be a `SelectorEventLoop`, which is not the default on Windows since Python 3.8. The default can be changed as follows (do this very early in your application): .. code:: python asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) This may have other implications for the rest of your codebase, so make sure to test thoroughly. Running the test suite ====================== To run the test suite: ``python tests.py`` Author ====== Saúl Ibarra Corretgé License ======= aiodns uses the MIT license, check LICENSE file. Python versions =============== Python >= 3.6 are supported. Contributing ============ If you'd like to contribute, fork the project, make a patch and send a pull request. Have a look at the surrounding code and please, make yours look alike :-) Platform: POSIX Platform: Microsoft Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Description-Content-Type: text/x-rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/README.rst0000644000175100001770000000601700000000000015272 0ustar00runnerdocker00000000000000=============================== Simple DNS resolver for asyncio =============================== .. image:: https://badge.fury.io/py/aiodns.png :target: https://pypi.org/project/aiodns/ .. image:: https://github.com/saghul/aiodns/workflows/CI/badge.svg :target: https://github.com/saghul/aiodns/actions aiodns provides a simple way for doing asynchronous DNS resolutions using `pycares `_. Example ======= .. code:: python import asyncio import aiodns loop = asyncio.get_event_loop() resolver = aiodns.DNSResolver(loop=loop) async def query(name, query_type): return await resolver.query(name, query_type) coro = query('google.com', 'A') result = loop.run_until_complete(coro) The following query types are supported: A, AAAA, ANY, CAA, CNAME, MX, NAPTR, NS, PTR, SOA, SRV, TXT. API === The API is pretty simple, three functions are provided in the ``DNSResolver`` class: * ``query(host, type)``: Do a DNS resolution of the given type for the given hostname. It returns an instance of ``asyncio.Future``. The actual result of the DNS query is taken directly from pycares. As of version 1.0.0 of aiodns (and pycares, for that matter) results are always namedtuple-like objects with different attributes. Please check the `documentation `_ for the result fields. * ``gethostbyname(host, socket_family)``: Do a DNS resolution for the given hostname and the desired type of address family (i.e. ``socket.AF_INET``). While ``query()`` always performs a request to a DNS server, ``gethostbyname()`` first looks into ``/etc/hosts`` and thus can resolve local hostnames (such as ``localhost``). Please check `the documentation `_ for the result fields. The actual result of the call is a ``asyncio.Future``. * ``gethostbyaddr(name)``: Make a reverse lookup for an address. * ``cancel()``: Cancel all pending DNS queries. All futures will get ``DNSError`` exception set, with ``ARES_ECANCELLED`` errno. Note for Windows users ====================== This library requires the asyncio loop to be a `SelectorEventLoop`, which is not the default on Windows since Python 3.8. The default can be changed as follows (do this very early in your application): .. code:: python asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) This may have other implications for the rest of your codebase, so make sure to test thoroughly. Running the test suite ====================== To run the test suite: ``python tests.py`` Author ====== Saúl Ibarra Corretgé License ======= aiodns uses the MIT license, check LICENSE file. Python versions =============== Python >= 3.6 are supported. Contributing ============ If you'd like to contribute, fork the project, make a patch and send a pull request. Have a look at the surrounding code and please, make yours look alike :-) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1697440773.6330059 aiodns-3.1.1/aiodns/0000755000175100001770000000000000000000000015054 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/aiodns/__init__.py0000644000175100001770000001315600000000000017173 0ustar00runnerdocker00000000000000 import asyncio import functools import pycares import socket import sys from typing import ( Any, Optional, Set, Sequence ) from . import error __version__ = '3.1.1' __all__ = ('DNSResolver', 'error') READ = 1 WRITE = 2 query_type_map = {'A' : pycares.QUERY_TYPE_A, 'AAAA' : pycares.QUERY_TYPE_AAAA, 'ANY' : pycares.QUERY_TYPE_ANY, 'CAA' : pycares.QUERY_TYPE_CAA, 'CNAME' : pycares.QUERY_TYPE_CNAME, 'MX' : pycares.QUERY_TYPE_MX, 'NAPTR' : pycares.QUERY_TYPE_NAPTR, 'NS' : pycares.QUERY_TYPE_NS, 'PTR' : pycares.QUERY_TYPE_PTR, 'SOA' : pycares.QUERY_TYPE_SOA, 'SRV' : pycares.QUERY_TYPE_SRV, 'TXT' : pycares.QUERY_TYPE_TXT } query_class_map = {'IN' : pycares.QUERY_CLASS_IN, 'CHAOS' : pycares.QUERY_CLASS_CHAOS, 'HS' : pycares.QUERY_CLASS_HS, 'NONE' : pycares.QUERY_CLASS_NONE, 'ANY' : pycares.QUERY_CLASS_ANY } class DNSResolver: def __init__(self, nameservers: Optional[Sequence[str]] = None, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any) -> None: self.loop = loop or asyncio.get_event_loop() assert self.loop is not None if sys.platform == 'win32': if not isinstance(self.loop, asyncio.SelectorEventLoop): raise RuntimeError( 'aiodns needs a SelectorEventLoop on Windows. See more: https://github.com/saghul/aiodns/issues/86') kwargs.pop('sock_state_cb', None) timeout = kwargs.pop('timeout', None) self._timeout = timeout self._channel = pycares.Channel(sock_state_cb=self._sock_state_cb, timeout=timeout, **kwargs) if nameservers: self.nameservers = nameservers self._read_fds = set() # type: Set[int] self._write_fds = set() # type: Set[int] self._timer = None # type: Optional[asyncio.TimerHandle] @property def nameservers(self) -> Sequence[str]: return self._channel.servers @nameservers.setter def nameservers(self, value: Sequence[str]) -> None: self._channel.servers = value @staticmethod def _callback(fut: asyncio.Future, result: Any, errorno: int) -> None: if fut.cancelled(): return if errorno is not None: fut.set_exception(error.DNSError(errorno, pycares.errno.strerror(errorno))) else: fut.set_result(result) def query(self, host: str, qtype: str, qclass: Optional[str]=None) -> asyncio.Future: try: qtype = query_type_map[qtype] except KeyError: raise ValueError('invalid query type: {}'.format(qtype)) if qclass is not None: try: qclass = query_class_map[qclass] except KeyError: raise ValueError('invalid query class: {}'.format(qclass)) fut = asyncio.Future(loop=self.loop) # type: asyncio.Future cb = functools.partial(self._callback, fut) self._channel.query(host, qtype, cb, query_class=qclass) return fut def gethostbyname(self, host: str, family: socket.AddressFamily) -> asyncio.Future: fut = asyncio.Future(loop=self.loop) # type: asyncio.Future cb = functools.partial(self._callback, fut) self._channel.gethostbyname(host, family, cb) return fut def gethostbyaddr(self, name: str) -> asyncio.Future: fut = asyncio.Future(loop=self.loop) # type: asyncio.Future cb = functools.partial(self._callback, fut) self._channel.gethostbyaddr(name, cb) return fut def cancel(self) -> None: self._channel.cancel() def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None: if readable or writable: if readable: self.loop.add_reader(fd, self._handle_event, fd, READ) self._read_fds.add(fd) if writable: self.loop.add_writer(fd, self._handle_event, fd, WRITE) self._write_fds.add(fd) if self._timer is None: self._start_timer() else: # socket is now closed if fd in self._read_fds: self._read_fds.discard(fd) self.loop.remove_reader(fd) if fd in self._write_fds: self._write_fds.discard(fd) self.loop.remove_writer(fd) if not self._read_fds and not self._write_fds and self._timer is not None: self._timer.cancel() self._timer = None def _handle_event(self, fd: int, event: Any) -> None: read_fd = pycares.ARES_SOCKET_BAD write_fd = pycares.ARES_SOCKET_BAD if event == READ: read_fd = fd elif event == WRITE: write_fd = fd self._channel.process_fd(read_fd, write_fd) def _timer_cb(self) -> None: if self._read_fds or self._write_fds: self._channel.process_fd(pycares.ARES_SOCKET_BAD, pycares.ARES_SOCKET_BAD) self._start_timer() else: self._timer = None def _start_timer(self): timeout = self._timeout if timeout is None or timeout < 0 or timeout > 1: timeout = 1 elif timeout == 0: timeout = 0.1 self._timer = self.loop.call_later(timeout, self._timer_cb) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/aiodns/error.py0000644000175100001770000000020600000000000016555 0ustar00runnerdocker00000000000000 import pycares for code, name in pycares.errno.errorcode.items(): globals()[name] = code class DNSError(Exception): pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/aiodns/py.typed0000644000175100001770000000000000000000000016541 0ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1697440773.6330059 aiodns-3.1.1/aiodns.egg-info/0000755000175100001770000000000000000000000016546 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440773.0 aiodns-3.1.1/aiodns.egg-info/PKG-INFO0000644000175100001770000001126600000000000017651 0ustar00runnerdocker00000000000000Metadata-Version: 2.1 Name: aiodns Version: 3.1.1 Summary: Simple DNS resolver for asyncio Home-page: https://github.com/saghul/aiodns Author: Saúl Ibarra Corretgé Author-email: s@saghul.net License: MIT Description: =============================== Simple DNS resolver for asyncio =============================== .. image:: https://badge.fury.io/py/aiodns.png :target: https://pypi.org/project/aiodns/ .. image:: https://github.com/saghul/aiodns/workflows/CI/badge.svg :target: https://github.com/saghul/aiodns/actions aiodns provides a simple way for doing asynchronous DNS resolutions using `pycares `_. Example ======= .. code:: python import asyncio import aiodns loop = asyncio.get_event_loop() resolver = aiodns.DNSResolver(loop=loop) async def query(name, query_type): return await resolver.query(name, query_type) coro = query('google.com', 'A') result = loop.run_until_complete(coro) The following query types are supported: A, AAAA, ANY, CAA, CNAME, MX, NAPTR, NS, PTR, SOA, SRV, TXT. API === The API is pretty simple, three functions are provided in the ``DNSResolver`` class: * ``query(host, type)``: Do a DNS resolution of the given type for the given hostname. It returns an instance of ``asyncio.Future``. The actual result of the DNS query is taken directly from pycares. As of version 1.0.0 of aiodns (and pycares, for that matter) results are always namedtuple-like objects with different attributes. Please check the `documentation `_ for the result fields. * ``gethostbyname(host, socket_family)``: Do a DNS resolution for the given hostname and the desired type of address family (i.e. ``socket.AF_INET``). While ``query()`` always performs a request to a DNS server, ``gethostbyname()`` first looks into ``/etc/hosts`` and thus can resolve local hostnames (such as ``localhost``). Please check `the documentation `_ for the result fields. The actual result of the call is a ``asyncio.Future``. * ``gethostbyaddr(name)``: Make a reverse lookup for an address. * ``cancel()``: Cancel all pending DNS queries. All futures will get ``DNSError`` exception set, with ``ARES_ECANCELLED`` errno. Note for Windows users ====================== This library requires the asyncio loop to be a `SelectorEventLoop`, which is not the default on Windows since Python 3.8. The default can be changed as follows (do this very early in your application): .. code:: python asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) This may have other implications for the rest of your codebase, so make sure to test thoroughly. Running the test suite ====================== To run the test suite: ``python tests.py`` Author ====== Saúl Ibarra Corretgé License ======= aiodns uses the MIT license, check LICENSE file. Python versions =============== Python >= 3.6 are supported. Contributing ============ If you'd like to contribute, fork the project, make a patch and send a pull request. Have a look at the surrounding code and please, make yours look alike :-) Platform: POSIX Platform: Microsoft Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Description-Content-Type: text/x-rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440773.0 aiodns-3.1.1/aiodns.egg-info/SOURCES.txt0000644000175100001770000000041400000000000020431 0ustar00runnerdocker00000000000000ChangeLog LICENSE MANIFEST.in README.rst setup.cfg setup.py tests.py aiodns/__init__.py aiodns/error.py aiodns/py.typed aiodns.egg-info/PKG-INFO aiodns.egg-info/SOURCES.txt aiodns.egg-info/dependency_links.txt aiodns.egg-info/requires.txt aiodns.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440773.0 aiodns-3.1.1/aiodns.egg-info/dependency_links.txt0000644000175100001770000000000100000000000022614 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440773.0 aiodns-3.1.1/aiodns.egg-info/requires.txt0000644000175100001770000000001700000000000021144 0ustar00runnerdocker00000000000000pycares>=4.0.0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440773.0 aiodns-3.1.1/aiodns.egg-info/top_level.txt0000644000175100001770000000000700000000000021275 0ustar00runnerdocker00000000000000aiodns ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1697440773.6330059 aiodns-3.1.1/setup.cfg0000644000175100001770000000006500000000000015421 0ustar00runnerdocker00000000000000[bdist_wheel] [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/setup.py0000644000175100001770000000270400000000000015314 0ustar00runnerdocker00000000000000# -*- coding: utf-8 -*- import codecs import re import sys from setuptools import setup def get_version(): return re.search(r"""__version__\s+=\s+(?P['"])(?P.+?)(?P=quote)""", open('aiodns/__init__.py').read()).group('version') setup(name = "aiodns", version = get_version(), author = "Saúl Ibarra Corretgé", author_email = "s@saghul.net", url = "https://github.com/saghul/aiodns", description = "Simple DNS resolver for asyncio", license = "MIT", long_description = codecs.open("README.rst", encoding="utf-8").read(), long_description_content_type = "text/x-rst", install_requires = ['pycares>=4.0.0'], packages = ['aiodns'], platforms = ["POSIX", "Microsoft Windows"], classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12" ] ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1697440771.0 aiodns-3.1.1/tests.py0000755000175100001770000001324400000000000015322 0ustar00runnerdocker00000000000000#!/usr/bin/env python import asyncio import ipaddress import unittest import socket import sys import time import aiodns class DNSTest(unittest.TestCase): def setUp(self): if sys.platform == 'win32': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) self.loop = asyncio.new_event_loop() self.addCleanup(self.loop.close) self.resolver = aiodns.DNSResolver(loop=self.loop, timeout=5.0) self.resolver.nameservers = ['8.8.8.8'] def tearDown(self): self.resolver = None def test_query_a(self): f = self.resolver.query('google.com', 'A') result = self.loop.run_until_complete(f) def test_query_async_await(self): async def f(): return await self.resolver.query('google.com', 'A') result = self.loop.run_until_complete(f()) self.assertTrue(result) def test_query_a_bad(self): f = self.resolver.query('hgf8g2od29hdohid.com', 'A') try: self.loop.run_until_complete(f) except aiodns.error.DNSError as e: self.assertEqual(e.args[0], aiodns.error.ARES_ENOTFOUND) def test_query_aaaa(self): f = self.resolver.query('ipv6.google.com', 'AAAA') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_cname(self): f = self.resolver.query('www.amazon.com', 'CNAME') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_mx(self): f = self.resolver.query('google.com', 'MX') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_ns(self): f = self.resolver.query('google.com', 'NS') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_txt(self): f = self.resolver.query('google.com', 'TXT') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_soa(self): f = self.resolver.query('google.com', 'SOA') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_srv(self): f = self.resolver.query('_xmpp-server._tcp.jabber.org', 'SRV') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_naptr(self): f = self.resolver.query('sip2sip.info', 'NAPTR') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_ptr(self): ip = '8.8.8.8' f = self.resolver.query(ipaddress.ip_address(ip).reverse_pointer, 'PTR') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_bad_type(self): self.assertRaises(ValueError, self.resolver.query, 'google.com', 'XXX') def test_query_txt_chaos(self): self.resolver = aiodns.DNSResolver(loop=self.loop) self.resolver.nameservers = ['1.1.1.1'] f = self.resolver.query('id.server', 'TXT', 'CHAOS') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_query_bad_class(self): self.assertRaises(ValueError, self.resolver.query, 'google.com', 'A', "INVALIDCLASS") def test_query_timeout(self): self.resolver = aiodns.DNSResolver(timeout=0.1, tries=1, loop=self.loop) self.resolver.nameservers = ['1.2.3.4'] f = self.resolver.query('google.com', 'A') started = time.monotonic() try: self.loop.run_until_complete(f) except aiodns.error.DNSError as e: self.assertEqual(e.args[0], aiodns.error.ARES_ETIMEOUT) # Ensure timeout really cuts time deadline. Limit duration to one second self.assertLess(time.monotonic() - started, 1) def test_query_cancel(self): f = self.resolver.query('google.com', 'A') self.resolver.cancel() try: self.loop.run_until_complete(f) except aiodns.error.DNSError as e: self.assertEqual(e.args[0], aiodns.error.ARES_ECANCELLED) def test_future_cancel(self): f = self.resolver.query('google.com', 'A') f.cancel() async def coro(): await asyncio.sleep(0.1) await f try: self.loop.run_until_complete(coro()) except asyncio.CancelledError as e: self.assertTrue(e) def test_query_twice(self): async def coro(self, host, qtype, n=2): for i in range(n): result = await self.resolver.query(host, qtype) self.assertTrue(result) self.loop.run_until_complete(coro(self, 'gmail.com', 'MX')) def test_gethostbyname(self): f = self.resolver.gethostbyname('google.com', socket.AF_INET) result = self.loop.run_until_complete(f) self.assertTrue(result) @unittest.skipIf(sys.platform == 'win32', 'skipped on Windows') def test_gethostbyaddr(self): f = self.resolver.gethostbyaddr('127.0.0.1') result = self.loop.run_until_complete(f) self.assertTrue(result) def test_gethostbyname_ipv6(self): f = self.resolver.gethostbyname('ipv6.google.com', socket.AF_INET6) result = self.loop.run_until_complete(f) self.assertTrue(result) def test_gethostbyname_bad_family(self): f = self.resolver.gethostbyname('ipv6.google.com', -1) with self.assertRaises(aiodns.error.DNSError): self.loop.run_until_complete(f) # def test_query_bad_chars(self): # f = self.resolver.query('xn--cardeosapeluqueros-r0b.com', 'MX') # result = self.loop.run_until_complete(f) # self.assertTrue(result) if __name__ == '__main__': unittest.main(verbosity=2)