Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/aiodns/__init__.py: 29%
94 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:52 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:52 +0000
2import asyncio
3import functools
4import pycares
5import socket
7from typing import (
8 Any,
9 List,
10 Optional,
11 Set
12)
14from . import error
17__version__ = '3.0.0'
19__all__ = ('DNSResolver', 'error')
22READ = 1
23WRITE = 2
25query_type_map = {'A' : pycares.QUERY_TYPE_A,
26 'AAAA' : pycares.QUERY_TYPE_AAAA,
27 'ANY' : pycares.QUERY_TYPE_ANY,
28 'CAA' : pycares.QUERY_TYPE_CAA,
29 'CNAME' : pycares.QUERY_TYPE_CNAME,
30 'MX' : pycares.QUERY_TYPE_MX,
31 'NAPTR' : pycares.QUERY_TYPE_NAPTR,
32 'NS' : pycares.QUERY_TYPE_NS,
33 'PTR' : pycares.QUERY_TYPE_PTR,
34 'SOA' : pycares.QUERY_TYPE_SOA,
35 'SRV' : pycares.QUERY_TYPE_SRV,
36 'TXT' : pycares.QUERY_TYPE_TXT
37 }
39query_class_map = {'IN' : pycares.QUERY_CLASS_IN,
40 'CHAOS' : pycares.QUERY_CLASS_CHAOS,
41 'HS' : pycares.QUERY_CLASS_HS,
42 'NONE' : pycares.QUERY_CLASS_NONE,
43 'ANY' : pycares.QUERY_CLASS_ANY
44 }
46class DNSResolver:
47 def __init__(self, nameservers: Optional[List[str]] = None,
48 loop: Optional[asyncio.AbstractEventLoop] = None,
49 **kwargs: Any) -> None:
50 self.loop = loop or asyncio.get_event_loop()
51 assert self.loop is not None
52 kwargs.pop('sock_state_cb', None)
53 self._channel = pycares.Channel(sock_state_cb=self._sock_state_cb, **kwargs)
54 if nameservers:
55 self.nameservers = nameservers
56 self._read_fds = set() # type: Set[int]
57 self._write_fds = set() # type: Set[int]
58 self._timer = None # type: Optional[asyncio.TimerHandle]
60 @property
61 def nameservers(self) -> pycares.Channel:
62 return self._channel.servers
64 @nameservers.setter
65 def nameservers(self, value: List[str]) -> None:
66 self._channel.servers = value
68 @staticmethod
69 def _callback(fut: asyncio.Future, result: Any, errorno: int) -> None:
70 if fut.cancelled():
71 return
72 if errorno is not None:
73 fut.set_exception(error.DNSError(errorno, pycares.errno.strerror(errorno)))
74 else:
75 fut.set_result(result)
77 def query(self, host: str, qtype: str, qclass: str=None) -> asyncio.Future:
78 try:
79 qtype = query_type_map[qtype]
80 except KeyError:
81 raise ValueError('invalid query type: {}'.format(qtype))
82 if qclass is not None:
83 try:
84 qclass = query_class_map[qclass]
85 except KeyError:
86 raise ValueError('invalid query class: {}'.format(qclass))
88 fut = asyncio.Future(loop=self.loop) # type: asyncio.Future
89 cb = functools.partial(self._callback, fut)
90 self._channel.query(host, qtype, cb, query_class=qclass)
91 return fut
93 def gethostbyname(self, host: str, family: socket.AddressFamily) -> asyncio.Future:
94 fut = asyncio.Future(loop=self.loop) # type: asyncio.Future
95 cb = functools.partial(self._callback, fut)
96 self._channel.gethostbyname(host, family, cb)
97 return fut
99 def gethostbyaddr(self, name: str) -> asyncio.Future:
100 fut = asyncio.Future(loop=self.loop) # type: asyncio.Future
101 cb = functools.partial(self._callback, fut)
102 self._channel.gethostbyaddr(name, cb)
103 return fut
105 def cancel(self) -> None:
106 self._channel.cancel()
108 def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None:
109 if readable or writable:
110 if readable:
111 self.loop.add_reader(fd, self._handle_event, fd, READ)
112 self._read_fds.add(fd)
113 if writable:
114 self.loop.add_writer(fd, self._handle_event, fd, WRITE)
115 self._write_fds.add(fd)
116 if self._timer is None:
117 self._timer = self.loop.call_later(1.0, self._timer_cb)
118 else:
119 # socket is now closed
120 if fd in self._read_fds:
121 self._read_fds.discard(fd)
122 self.loop.remove_reader(fd)
124 if fd in self._write_fds:
125 self._write_fds.discard(fd)
126 self.loop.remove_writer(fd)
128 if not self._read_fds and not self._write_fds and self._timer is not None:
129 self._timer.cancel()
130 self._timer = None
132 def _handle_event(self, fd: int, event: Any) -> None:
133 read_fd = pycares.ARES_SOCKET_BAD
134 write_fd = pycares.ARES_SOCKET_BAD
135 if event == READ:
136 read_fd = fd
137 elif event == WRITE:
138 write_fd = fd
139 self._channel.process_fd(read_fd, write_fd)
141 def _timer_cb(self) -> None:
142 if self._read_fds or self._write_fds:
143 self._channel.process_fd(pycares.ARES_SOCKET_BAD, pycares.ARES_SOCKET_BAD)
144 self._timer = self.loop.call_later(1.0, self._timer_cb)
145 else:
146 self._timer = None