Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pycares/__init__.py: 21%
590 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
2from ._cares import ffi as _ffi, lib as _lib
3import _cffi_backend # hint for bundler tools
5if _lib.ARES_SUCCESS != _lib.ares_library_init(_lib.ARES_LIB_INIT_ALL):
6 raise RuntimeError('Could not initialize c-ares')
8from . import errno
9from .utils import ascii_bytes, maybe_str, parse_name
10from ._version import __version__
12import collections.abc
13import socket
14import math
15import functools
16import sys
19exported_pycares_symbols = [
20 # Flag values
21 'ARES_FLAG_USEVC',
22 'ARES_FLAG_PRIMARY',
23 'ARES_FLAG_IGNTC',
24 'ARES_FLAG_NORECURSE',
25 'ARES_FLAG_STAYOPEN',
26 'ARES_FLAG_NOSEARCH',
27 'ARES_FLAG_NOALIASES',
28 'ARES_FLAG_NOCHECKRESP',
30 # Nameinfo flag values
31 'ARES_NI_NOFQDN',
32 'ARES_NI_NUMERICHOST',
33 'ARES_NI_NAMEREQD',
34 'ARES_NI_NUMERICSERV',
35 'ARES_NI_DGRAM',
36 'ARES_NI_TCP',
37 'ARES_NI_UDP',
38 'ARES_NI_SCTP',
39 'ARES_NI_DCCP',
40 'ARES_NI_NUMERICSCOPE',
41 'ARES_NI_LOOKUPHOST',
42 'ARES_NI_LOOKUPSERVICE',
43 'ARES_NI_IDN',
44 'ARES_NI_IDN_ALLOW_UNASSIGNED',
45 'ARES_NI_IDN_USE_STD3_ASCII_RULES',
47 # Bad socket
48 'ARES_SOCKET_BAD',
49]
51for symbol in exported_pycares_symbols:
52 globals()[symbol] = getattr(_lib, symbol)
55exported_pycares_symbols_map = {
56 # Query types
57 "QUERY_TYPE_A" : "T_A",
58 "QUERY_TYPE_AAAA" : "T_AAAA",
59 "QUERY_TYPE_ANY" : "T_ANY",
60 "QUERY_TYPE_CAA" : "T_CAA",
61 "QUERY_TYPE_CNAME" : "T_CNAME",
62 "QUERY_TYPE_MX" : "T_MX",
63 "QUERY_TYPE_NAPTR" : "T_NAPTR",
64 "QUERY_TYPE_NS" : "T_NS",
65 "QUERY_TYPE_PTR" : "T_PTR",
66 "QUERY_TYPE_SOA" : "T_SOA",
67 "QUERY_TYPE_SRV" : "T_SRV",
68 "QUERY_TYPE_TXT" : "T_TXT",
70 # Query classes
71 "QUERY_CLASS_IN" : "C_IN",
72 "QUERY_CLASS_CHAOS": "C_CHAOS",
73 "QUERY_CLASS_HS" : "C_HS",
74 "QUERY_CLASS_NONE" :"C_NONE",
75 "QUERY_CLASS_ANY" : "C_ANY",
76}
78for k, v in exported_pycares_symbols_map.items():
79 globals()[k] = getattr(_lib, v)
82globals()['ARES_VERSION'] = maybe_str(_ffi.string(_lib.ares_version(_ffi.NULL)))
85PYCARES_ADDRTTL_SIZE = 256
88class AresError(Exception):
89 pass
92# callback helpers
94_global_set = set()
96@_ffi.def_extern()
97def _sock_state_cb(data, socket_fd, readable, writable):
98 sock_state_cb = _ffi.from_handle(data)
99 sock_state_cb(socket_fd, readable, writable)
101@_ffi.def_extern()
102def _host_cb(arg, status, timeouts, hostent):
103 callback = _ffi.from_handle(arg)
104 _global_set.discard(arg)
106 if status != _lib.ARES_SUCCESS:
107 result = None
108 else:
109 result = ares_host_result(hostent)
110 status = None
112 callback(result, status)
114@_ffi.def_extern()
115def _nameinfo_cb(arg, status, timeouts, node, service):
116 callback = _ffi.from_handle(arg)
117 _global_set.discard(arg)
119 if status != _lib.ARES_SUCCESS:
120 result = None
121 else:
122 result = ares_nameinfo_result(node, service)
123 status = None
125 callback(result, status)
127@_ffi.def_extern()
128def _query_cb(arg, status, timeouts, abuf, alen):
129 callback, query_type = _ffi.from_handle(arg)
130 _global_set.discard(arg)
132 if status == _lib.ARES_SUCCESS:
133 if query_type == _lib.T_ANY:
134 result = []
135 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):
136 r, status = parse_result(qtype, abuf, alen)
137 if status not in (None, _lib.ARES_ENODATA, _lib.ARES_EBADRESP):
138 result = None
139 break
140 if r is not None:
141 if isinstance(r, collections.abc.Iterable):
142 result.extend(r)
143 else:
144 result.append(r)
145 else:
146 status = None
147 else:
148 result, status = parse_result(query_type, abuf, alen)
149 else:
150 result = None
152 callback(result, status)
154@_ffi.def_extern()
155def _addrinfo_cb(arg, status, timeouts, res):
156 callback = _ffi.from_handle(arg)
157 _global_set.discard(arg)
159 if status != _lib.ARES_SUCCESS:
160 result = None
161 else:
162 result = ares_addrinfo_result(res)
163 status = None
165 callback(result, status)
167def parse_result(query_type, abuf, alen):
168 if query_type == _lib.T_A:
169 addrttls = _ffi.new("struct ares_addrttl[]", PYCARES_ADDRTTL_SIZE)
170 naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE)
171 parse_status = _lib.ares_parse_a_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls)
172 if parse_status != _lib.ARES_SUCCESS:
173 result = None
174 status = parse_status
175 else:
176 result = [ares_query_a_result(addrttls[i]) for i in range(naddrttls[0])]
177 status = None
178 elif query_type == _lib.T_AAAA:
179 addrttls = _ffi.new("struct ares_addr6ttl[]", PYCARES_ADDRTTL_SIZE)
180 naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE)
181 parse_status = _lib.ares_parse_aaaa_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls)
182 if parse_status != _lib.ARES_SUCCESS:
183 result = None
184 status = parse_status
185 else:
186 result = [ares_query_aaaa_result(addrttls[i]) for i in range(naddrttls[0])]
187 status = None
188 elif query_type == _lib.T_CAA:
189 caa_reply = _ffi.new("struct ares_caa_reply **")
190 parse_status = _lib.ares_parse_caa_reply(abuf, alen, caa_reply)
191 if parse_status != _lib.ARES_SUCCESS:
192 result = None
193 status = parse_status
194 else:
195 result = []
196 caa_reply_ptr = caa_reply[0]
197 while caa_reply_ptr != _ffi.NULL:
198 result.append(ares_query_caa_result(caa_reply_ptr))
199 caa_reply_ptr = caa_reply_ptr.next
200 _lib.ares_free_data(caa_reply[0])
201 status = None
202 elif query_type == _lib.T_CNAME:
203 host = _ffi.new("struct hostent **")
204 parse_status = _lib.ares_parse_a_reply(abuf, alen, host, _ffi.NULL, _ffi.NULL)
205 if parse_status != _lib.ARES_SUCCESS:
206 result = None
207 status = parse_status
208 else:
209 result = ares_query_cname_result(host[0])
210 _lib.ares_free_hostent(host[0])
211 status = None
212 elif query_type == _lib.T_MX:
213 mx_reply = _ffi.new("struct ares_mx_reply **")
214 parse_status = _lib.ares_parse_mx_reply(abuf, alen, mx_reply)
215 if parse_status != _lib.ARES_SUCCESS:
216 result = None
217 status = parse_status
218 else:
219 result = []
220 mx_reply_ptr = mx_reply[0]
221 while mx_reply_ptr != _ffi.NULL:
222 result.append(ares_query_mx_result(mx_reply_ptr))
223 mx_reply_ptr = mx_reply_ptr.next
224 _lib.ares_free_data(mx_reply[0])
225 status = None
226 elif query_type == _lib.T_NAPTR:
227 naptr_reply = _ffi.new("struct ares_naptr_reply **")
228 parse_status = _lib.ares_parse_naptr_reply(abuf, alen, naptr_reply)
229 if parse_status != _lib.ARES_SUCCESS:
230 result = None
231 status = parse_status
232 else:
233 result = []
234 naptr_reply_ptr = naptr_reply[0]
235 while naptr_reply_ptr != _ffi.NULL:
236 result.append(ares_query_naptr_result(naptr_reply_ptr))
237 naptr_reply_ptr = naptr_reply_ptr.next
238 _lib.ares_free_data(naptr_reply[0])
239 status = None
240 elif query_type == _lib.T_NS:
241 hostent = _ffi.new("struct hostent **")
242 parse_status = _lib.ares_parse_ns_reply(abuf, alen, hostent)
243 if parse_status != _lib.ARES_SUCCESS:
244 result = None
245 status = parse_status
246 else:
247 result = []
248 host = hostent[0]
249 i = 0
250 while host.h_aliases[i] != _ffi.NULL:
251 result.append(ares_query_ns_result(host.h_aliases[i]))
252 i += 1
253 _lib.ares_free_hostent(host)
254 status = None
255 elif query_type == _lib.T_PTR:
256 hostent = _ffi.new("struct hostent **")
257 parse_status = _lib.ares_parse_ptr_reply(abuf, alen, _ffi.NULL, 0, socket.AF_UNSPEC, hostent)
258 if parse_status != _lib.ARES_SUCCESS:
259 result = None
260 status = parse_status
261 else:
262 aliases = []
263 host = hostent[0]
264 i = 0
265 while host.h_aliases[i] != _ffi.NULL:
266 aliases.append(maybe_str(_ffi.string(host.h_aliases[i])))
267 i += 1
268 result = ares_query_ptr_result(host, aliases)
269 _lib.ares_free_hostent(host)
270 status = None
271 elif query_type == _lib.T_SOA:
272 soa_reply = _ffi.new("struct ares_soa_reply **")
273 parse_status = _lib.ares_parse_soa_reply(abuf, alen, soa_reply)
274 if parse_status != _lib.ARES_SUCCESS:
275 result = None
276 status = parse_status
277 else:
278 result = ares_query_soa_result(soa_reply[0])
279 _lib.ares_free_data(soa_reply[0])
280 status = None
281 elif query_type == _lib.T_SRV:
282 srv_reply = _ffi.new("struct ares_srv_reply **")
283 parse_status = _lib.ares_parse_srv_reply(abuf, alen, srv_reply)
284 if parse_status != _lib.ARES_SUCCESS:
285 result = None
286 status = parse_status
287 else:
288 result = []
289 srv_reply_ptr = srv_reply[0]
290 while srv_reply_ptr != _ffi.NULL:
291 result.append(ares_query_srv_result(srv_reply_ptr))
292 srv_reply_ptr = srv_reply_ptr.next
293 _lib.ares_free_data(srv_reply[0])
294 status = None
295 elif query_type == _lib.T_TXT:
296 txt_reply = _ffi.new("struct ares_txt_ext **")
297 parse_status = _lib.ares_parse_txt_reply_ext(abuf, alen, txt_reply)
298 if parse_status != _lib.ARES_SUCCESS:
299 result = None
300 status = parse_status
301 else:
302 result = []
303 txt_reply_ptr = txt_reply[0]
304 tmp_obj = None
305 while True:
306 if txt_reply_ptr == _ffi.NULL:
307 if tmp_obj is not None:
308 result.append(ares_query_txt_result(tmp_obj))
309 break
310 if txt_reply_ptr.record_start == 1:
311 if tmp_obj is not None:
312 result.append(ares_query_txt_result(tmp_obj))
313 tmp_obj = ares_query_txt_result_chunk(txt_reply_ptr)
314 else:
315 new_chunk = ares_query_txt_result_chunk(txt_reply_ptr)
316 tmp_obj.text += new_chunk.text
317 txt_reply_ptr = txt_reply_ptr.next
318 _lib.ares_free_data(txt_reply[0])
319 status = None
320 else:
321 raise ValueError("invalid query type specified")
323 return result, status
326class Channel:
327 __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)
328 __qclasses__ = (_lib.C_IN, _lib.C_CHAOS, _lib.C_HS, _lib.C_NONE, _lib.C_ANY)
330 def __init__(self,
331 flags = None,
332 timeout = None,
333 tries = None,
334 ndots = None,
335 tcp_port = None,
336 udp_port = None,
337 servers = None,
338 domains = None,
339 lookups = None,
340 sock_state_cb = None,
341 socket_send_buffer_size = None,
342 socket_receive_buffer_size = None,
343 rotate = False,
344 local_ip = None,
345 local_dev = None,
346 resolvconf_path = None):
348 channel = _ffi.new("ares_channel *")
349 options = _ffi.new("struct ares_options *")
350 optmask = 0
352 if flags is not None:
353 options.flags = flags
354 optmask = optmask | _lib.ARES_OPT_FLAGS
356 if timeout is not None:
357 options.timeout = int(timeout * 1000)
358 optmask = optmask | _lib.ARES_OPT_TIMEOUTMS
360 if tries is not None:
361 options.tries = tries
362 optmask = optmask | _lib.ARES_OPT_TRIES
364 if ndots is not None:
365 options.ndots = ndots
366 optmask = optmask | _lib.ARES_OPT_NDOTS
368 if tcp_port is not None:
369 options.tcp_port = tcp_port
370 optmask = optmask | _lib.ARES_OPT_TCP_PORT
372 if udp_port is not None:
373 options.udp_port = udp_port
374 optmask = optmask | _lib.ARES_OPT_UDP_PORT
376 if socket_send_buffer_size is not None:
377 options.socket_send_buffer_size = socket_send_buffer_size
378 optmask = optmask | _lib.ARES_OPT_SOCK_SNDBUF
380 if socket_receive_buffer_size is not None:
381 options.socket_receive_buffer_size = socket_receive_buffer_size
382 optmask = optmask | _lib.ARES_OPT_SOCK_RCVBUF
384 if sock_state_cb:
385 if not callable(sock_state_cb):
386 raise TypeError("sock_state_cb is not callable")
388 userdata = _ffi.new_handle(sock_state_cb)
390 # This must be kept alive while the channel is alive.
391 self._sock_state_cb_handle = userdata
393 options.sock_state_cb = _lib._sock_state_cb
394 options.sock_state_cb_data = userdata
395 optmask = optmask | _lib.ARES_OPT_SOCK_STATE_CB
397 if lookups:
398 options.lookups = _ffi.new('char[]', ascii_bytes(lookups))
399 optmask = optmask | _lib.ARES_OPT_LOOKUPS
401 if domains:
402 strs = [_ffi.new("char[]", ascii_bytes(i)) for i in domains]
403 c = _ffi.new("char *[%d]" % (len(domains) + 1))
404 for i in range(len(domains)):
405 c[i] = strs[i]
407 options.domains = c
408 options.ndomains = len(domains)
409 optmask = optmask | _lib.ARES_OPT_DOMAINS
411 if rotate:
412 optmask = optmask | _lib.ARES_OPT_ROTATE
414 if resolvconf_path is not None:
415 optmask = optmask | _lib.ARES_OPT_RESOLVCONF
416 options.resolvconf_path = _ffi.new('char[]', ascii_bytes(resolvconf_path))
418 r = _lib.ares_init_options(channel, options, optmask)
419 if r != _lib.ARES_SUCCESS:
420 raise AresError('Failed to initialize c-ares channel')
422 self._channel = _ffi.gc(channel, lambda x: _lib.ares_destroy(x[0]))
424 if servers:
425 self.servers = servers
427 if local_ip:
428 self.set_local_ip(local_ip)
430 if local_dev:
431 self.set_local_dev(local_dev)
433 def cancel(self):
434 _lib.ares_cancel(self._channel[0])
436 @property
437 def servers(self):
438 servers = _ffi.new("struct ares_addr_node **")
440 r = _lib.ares_get_servers(self._channel[0], servers)
441 if r != _lib.ARES_SUCCESS:
442 raise AresError(r, errno.strerror(r))
444 server_list = []
445 server = _ffi.new("struct ares_addr_node **", servers[0])
446 while True:
447 if server == _ffi.NULL:
448 break
450 ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN)
451 s = server[0]
452 if _ffi.NULL != _lib.ares_inet_ntop(s.family, _ffi.addressof(s.addr), ip, _lib.INET6_ADDRSTRLEN):
453 server_list.append(maybe_str(_ffi.string(ip, _lib.INET6_ADDRSTRLEN)))
455 server = s.next
457 return server_list
459 @servers.setter
460 def servers(self, servers):
461 c = _ffi.new("struct ares_addr_node[%d]" % len(servers))
462 for i, server in enumerate(servers):
463 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(server), _ffi.addressof(c[i].addr.addr4)) == 1:
464 c[i].family = socket.AF_INET
465 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(server), _ffi.addressof(c[i].addr.addr6)) == 1:
466 c[i].family = socket.AF_INET6
467 else:
468 raise ValueError("invalid IP address")
470 if i > 0:
471 c[i - 1].next = _ffi.addressof(c[i])
473 r = _lib.ares_set_servers(self._channel[0], c)
474 if r != _lib.ARES_SUCCESS:
475 raise AresError(r, errno.strerror(r))
477 def getsock(self):
478 rfds = []
479 wfds = []
480 socks = _ffi.new("ares_socket_t [%d]" % _lib.ARES_GETSOCK_MAXNUM)
481 bitmask = _lib.ares_getsock(self._channel[0], socks, _lib.ARES_GETSOCK_MAXNUM)
482 for i in range(_lib.ARES_GETSOCK_MAXNUM):
483 if _lib.ARES_GETSOCK_READABLE(bitmask, i):
484 rfds.append(socks[i])
485 if _lib.ARES_GETSOCK_WRITABLE(bitmask, i):
486 wfds.append(socks[i])
488 return rfds, wfds
490 def process_fd(self, read_fd, write_fd):
491 _lib.ares_process_fd(self._channel[0], _ffi.cast("ares_socket_t", read_fd), _ffi.cast("ares_socket_t", write_fd))
493 def timeout(self, t = None):
494 maxtv = _ffi.NULL
495 tv = _ffi.new("struct timeval*")
497 if t is not None:
498 if t >= 0.0:
499 maxtv = _ffi.new("struct timeval*")
500 maxtv.tv_sec = int(math.floor(t))
501 maxtv.tv_usec = int(math.fmod(t, 1.0) * 1000000)
502 else:
503 raise ValueError("timeout needs to be a positive number or None")
505 _lib.ares_timeout(self._channel[0], maxtv, tv)
507 if tv == _ffi.NULL:
508 return 0.0
510 return (tv.tv_sec + tv.tv_usec / 1000000.0)
512 def gethostbyaddr(self, addr, callback):
513 if not callable(callback):
514 raise TypeError("a callable is required")
516 addr4 = _ffi.new("struct in_addr*")
517 addr6 = _ffi.new("struct ares_in6_addr*")
518 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(addr), (addr4)) == 1:
519 address = addr4
520 family = socket.AF_INET
521 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(addr), (addr6)) == 1:
522 address = addr6
523 family = socket.AF_INET6
524 else:
525 raise ValueError("invalid IP address")
527 userdata = _ffi.new_handle(callback)
528 _global_set.add(userdata)
529 _lib.ares_gethostbyaddr(self._channel[0], address, _ffi.sizeof(address[0]), family, _lib._host_cb, userdata)
531 def gethostbyname(self, name, family, callback):
532 if not callable(callback):
533 raise TypeError("a callable is required")
535 userdata = _ffi.new_handle(callback)
536 _global_set.add(userdata)
537 _lib.ares_gethostbyname(self._channel[0], parse_name(name), family, _lib._host_cb, userdata)
539 def getaddrinfo(self, host, port, callback, family=0, type=0, proto=0, flags=0):
540 if not callable(callback):
541 raise TypeError("a callable is required")
543 if port is None:
544 service = _ffi.NULL
545 elif isinstance(port, int):
546 service = str(port).encode('ascii')
547 else:
548 service = ascii_bytes(port)
550 userdata = _ffi.new_handle(callback)
551 _global_set.add(userdata)
553 hints = _ffi.new('struct ares_addrinfo_hints*')
554 hints.ai_flags = flags
555 hints.ai_family = family
556 hints.ai_socktype = type
557 hints.ai_protocol = proto
558 _lib.ares_getaddrinfo(self._channel[0], parse_name(host), service, hints, _lib._addrinfo_cb, userdata)
560 def query(self, name, query_type, callback, query_class=None):
561 self._do_query(_lib.ares_query, name, query_type, callback, query_class=query_class)
563 def search(self, name, query_type, callback, query_class=None):
564 self._do_query(_lib.ares_search, name, query_type, callback, query_class=query_class)
566 def _do_query(self, func, name, query_type, callback, query_class=None):
567 if not callable(callback):
568 raise TypeError('a callable is required')
570 if query_type not in self.__qtypes__:
571 raise ValueError('invalid query type specified')
573 if query_class is None:
574 query_class = _lib.C_IN
576 if query_class not in self.__qclasses__:
577 raise ValueError('invalid query class specified')
579 userdata = _ffi.new_handle((callback, query_type))
580 _global_set.add(userdata)
581 func(self._channel[0], parse_name(name), query_class, query_type, _lib._query_cb, userdata)
583 def set_local_ip(self, ip):
584 addr4 = _ffi.new("struct in_addr*")
585 addr6 = _ffi.new("struct ares_in6_addr*")
586 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), addr4) == 1:
587 _lib.ares_set_local_ip4(self._channel[0], socket.ntohl(addr4.s_addr))
588 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), addr6) == 1:
589 _lib.ares_set_local_ip6(self._channel[0], addr6)
590 else:
591 raise ValueError("invalid IP address")
593 def getnameinfo(self, address, flags, callback):
594 if not callable(callback):
595 raise TypeError("a callable is required")
597 if len(address) == 2:
598 (ip, port) = address
599 sa4 = _ffi.new("struct sockaddr_in*")
600 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), _ffi.addressof(sa4.sin_addr)) != 1:
601 raise ValueError("Invalid IPv4 address %r" % ip)
602 sa4.sin_family = socket.AF_INET
603 sa4.sin_port = socket.htons(port)
604 sa = sa4
605 elif len(address) == 4:
606 (ip, port, flowinfo, scope_id) = address
607 sa6 = _ffi.new("struct sockaddr_in6*")
608 if _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), _ffi.addressof(sa6.sin6_addr)) != 1:
609 raise ValueError("Invalid IPv6 address %r" % ip)
610 sa6.sin6_family = socket.AF_INET6
611 sa6.sin6_port = socket.htons(port)
612 sa6.sin6_flowinfo = socket.htonl(flowinfo) # I'm unsure about byteorder here.
613 sa6.sin6_scope_id = scope_id # Yes, without htonl.
614 sa = sa6
615 else:
616 raise ValueError("Invalid address argument")
618 userdata = _ffi.new_handle(callback)
619 _global_set.add(userdata)
620 _lib.ares_getnameinfo(self._channel[0], _ffi.cast("struct sockaddr*", sa), _ffi.sizeof(sa[0]), flags, _lib._nameinfo_cb, userdata)
622 def set_local_dev(self, dev):
623 _lib.ares_set_local_dev(self._channel[0], dev)
626class AresResult:
627 __slots__ = ()
629 def __repr__(self):
630 attrs = ['%s=%s' % (a, getattr(self, a)) for a in self.__slots__]
631 return '<%s> %s' % (self.__class__.__name__, ', '.join(attrs))
634# DNS query result types
635#
637class ares_query_a_result(AresResult):
638 __slots__ = ('host', 'ttl')
639 type = 'A'
641 def __init__(self, ares_addrttl):
642 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
643 _lib.ares_inet_ntop(socket.AF_INET, _ffi.addressof(ares_addrttl.ipaddr), buf, _lib.INET6_ADDRSTRLEN)
644 self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN))
645 self.ttl = ares_addrttl.ttl
648class ares_query_aaaa_result(AresResult):
649 __slots__ = ('host', 'ttl')
650 type = 'AAAA'
652 def __init__(self, ares_addrttl):
653 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
654 _lib.ares_inet_ntop(socket.AF_INET6, _ffi.addressof(ares_addrttl.ip6addr), buf, _lib.INET6_ADDRSTRLEN)
655 self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN))
656 self.ttl = ares_addrttl.ttl
659class ares_query_caa_result(AresResult):
660 __slots__ = ('critical', 'property', 'value', 'ttl')
661 type = 'CAA'
663 def __init__(self, caa):
664 self.critical = caa.critical
665 self.property = maybe_str(_ffi.string(caa.property, caa.plength))
666 self.value = maybe_str(_ffi.string(caa.value, caa.length))
667 self.ttl = -1
670class ares_query_cname_result(AresResult):
671 __slots__ = ('cname', 'ttl')
672 type = 'CNAME'
674 def __init__(self, host):
675 self.cname = maybe_str(_ffi.string(host.h_name))
676 self.ttl = -1
679class ares_query_mx_result(AresResult):
680 __slots__ = ('host', 'priority', 'ttl')
681 type = 'MX'
683 def __init__(self, mx):
684 self.host = maybe_str(_ffi.string(mx.host))
685 self.priority = mx.priority
686 self.ttl = -1
689class ares_query_naptr_result(AresResult):
690 __slots__ = ('order', 'preference', 'flags', 'service', 'regex', 'replacement', 'ttl')
691 type = 'NAPTR'
693 def __init__(self, naptr):
694 self.order = naptr.order
695 self.preference = naptr.preference
696 self.flags = maybe_str(_ffi.string(naptr.flags))
697 self.service = maybe_str(_ffi.string(naptr.service))
698 self.regex = maybe_str(_ffi.string(naptr.regexp))
699 self.replacement = maybe_str(_ffi.string(naptr.replacement))
700 self.ttl = -1
703class ares_query_ns_result(AresResult):
704 __slots__ = ('host', 'ttl')
705 type = 'NS'
707 def __init__(self, ns):
708 self.host = maybe_str(_ffi.string(ns))
709 self.ttl = -1
712class ares_query_ptr_result(AresResult):
713 __slots__ = ('name', 'ttl', 'aliases')
714 type = 'PTR'
716 def __init__(self, hostent, aliases):
717 self.name = maybe_str(_ffi.string(hostent.h_name))
718 self.aliases = aliases
719 self.ttl = -1
722class ares_query_soa_result(AresResult):
723 __slots__ = ('nsname', 'hostmaster', 'serial', 'refresh', 'retry', 'expires', 'minttl', 'ttl')
724 type = 'SOA'
726 def __init__(self, soa):
727 self.nsname = maybe_str(_ffi.string(soa.nsname))
728 self.hostmaster = maybe_str(_ffi.string(soa.hostmaster))
729 self.serial = soa.serial
730 self.refresh = soa.refresh
731 self.retry = soa.retry
732 self.expires = soa.expire
733 self.minttl = soa.minttl
734 self.ttl = -1
737class ares_query_srv_result(AresResult):
738 __slots__ = ('host', 'port', 'priority', 'weight', 'ttl')
739 type = 'SRV'
741 def __init__(self, srv):
742 self.host = maybe_str(_ffi.string(srv.host))
743 self.port = srv.port
744 self.priority = srv.priority
745 self.weight = srv.weight
746 self.ttl = -1
749class ares_query_txt_result(AresResult):
750 __slots__ = ('text', 'ttl')
751 type = 'TXT'
753 def __init__(self, txt_chunk):
754 self.text = maybe_str(txt_chunk.text)
755 self.ttl = -1
758class ares_query_txt_result_chunk(AresResult):
759 __slots__ = ('text', 'ttl')
760 type = 'TXT'
762 def __init__(self, txt):
763 self.text = _ffi.string(txt.txt)
764 self.ttl = -1
767# Other result types
768#
770class ares_host_result(AresResult):
771 __slots__ = ('name', 'aliases', 'addresses')
773 def __init__(self, hostent):
774 self.name = maybe_str(_ffi.string(hostent.h_name))
775 self.aliases = []
776 self.addresses = []
777 i = 0
778 while hostent.h_aliases[i] != _ffi.NULL:
779 self.aliases.append(maybe_str(_ffi.string(hostent.h_aliases[i])))
780 i += 1
782 i = 0
783 while hostent.h_addr_list[i] != _ffi.NULL:
784 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
785 if _ffi.NULL != _lib.ares_inet_ntop(hostent.h_addrtype, hostent.h_addr_list[i], buf, _lib.INET6_ADDRSTRLEN):
786 self.addresses.append(maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN)))
787 i += 1
790class ares_nameinfo_result(AresResult):
791 __slots__ = ('node', 'service')
793 def __init__(self, node, service):
794 self.node = maybe_str(_ffi.string(node))
795 self.service = maybe_str(_ffi.string(service)) if service != _ffi.NULL else None
798class ares_addrinfo_node_result(AresResult):
799 __slots__ = ('ttl', 'flags', 'family', 'socktype', 'protocol', 'addr')
801 def __init__(self, ares_node):
802 self.ttl = ares_node.ai_ttl
803 self.flags = ares_node.ai_flags
804 self.socktype = ares_node.ai_socktype
805 self.protocol = ares_node.ai_protocol
807 addr = ares_node.ai_addr
808 assert addr.sa_family == ares_node.ai_family
809 ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN)
810 if addr.sa_family == socket.AF_INET:
811 self.family = socket.AF_INET
812 s = _ffi.cast("struct sockaddr_in*", addr)
813 if _ffi.NULL != _lib.ares_inet_ntop(s.sin_family, _ffi.addressof(s.sin_addr), ip, _lib.INET6_ADDRSTRLEN):
814 # (address, port) 2-tuple for AF_INET
815 self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin_port))
816 elif addr.sa_family == socket.AF_INET6:
817 self.family = socket.AF_INET6
818 s = _ffi.cast("struct sockaddr_in6*", addr)
819 if _ffi.NULL != _lib.ares_inet_ntop(s.sin6_family, _ffi.addressof(s.sin6_addr), ip, _lib.INET6_ADDRSTRLEN):
820 # (address, port, flow info, scope id) 4-tuple for AF_INET6
821 self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin6_port), s.sin6_flowinfo, s.sin6_scope_id)
822 else:
823 raise ValueError("invalid sockaddr family")
826class ares_addrinfo_cname_result(AresResult):
827 __slots__ = ('ttl', 'alias', 'name')
829 def __init__(self, ares_cname):
830 self.ttl = ares_cname.ttl
831 self.alias = maybe_str(_ffi.string(ares_cname.alias))
832 self.name = maybe_str(_ffi.string(ares_cname.name))
835class ares_addrinfo_result(AresResult):
836 __slots__ = ('cnames', 'nodes')
838 def __init__(self, ares_addrinfo):
839 self.cnames = []
840 self.nodes = []
841 cname_ptr = ares_addrinfo.cnames
842 while cname_ptr != _ffi.NULL:
843 self.cnames.append(ares_addrinfo_cname_result(cname_ptr))
844 cname_ptr = cname_ptr.next
845 node_ptr = ares_addrinfo.nodes
846 while node_ptr != _ffi.NULL:
847 self.nodes.append(ares_addrinfo_node_result(node_ptr))
848 node_ptr = node_ptr.ai_next
849 _lib.ares_freeaddrinfo(ares_addrinfo)
853__all__ = exported_pycares_symbols + list(exported_pycares_symbols_map.keys()) + ['AresError', 'Channel', 'errno', '__version__']
855del exported_pycares_symbols, exported_pycares_symbols_map