Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pycares/__init__.py: 31%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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) or _ffi is None:
6 raise RuntimeError('Could not initialize c-ares')
8if not _lib.ares_threadsafety():
9 raise RuntimeError("c-ares is not built with thread safety")
11from . import errno
12from .utils import ascii_bytes, maybe_str, parse_name
13from ._version import __version__
15import contextlib
16import math
17import socket
18import threading
19from collections.abc import Callable, Iterable
20from dataclasses import dataclass
21from typing import Any, Callable, Literal, Optional, Dict, Union
22from queue import SimpleQueue
24IP4 = tuple[str, int]
25IP6 = tuple[str, int, int, int]
27# Flag values
28ARES_FLAG_USEVC = _lib.ARES_FLAG_USEVC
29ARES_FLAG_PRIMARY = _lib.ARES_FLAG_PRIMARY
30ARES_FLAG_IGNTC = _lib.ARES_FLAG_IGNTC
31ARES_FLAG_NORECURSE = _lib.ARES_FLAG_NORECURSE
32ARES_FLAG_STAYOPEN = _lib.ARES_FLAG_STAYOPEN
33ARES_FLAG_NOSEARCH = _lib.ARES_FLAG_NOSEARCH
34ARES_FLAG_NOALIASES = _lib.ARES_FLAG_NOALIASES
35ARES_FLAG_NOCHECKRESP = _lib.ARES_FLAG_NOCHECKRESP
36ARES_FLAG_EDNS = _lib.ARES_FLAG_EDNS
37ARES_FLAG_NO_DFLT_SVR = _lib.ARES_FLAG_NO_DFLT_SVR
39# Nameinfo flag values
40ARES_NI_NOFQDN = _lib.ARES_NI_NOFQDN
41ARES_NI_NUMERICHOST = _lib.ARES_NI_NUMERICHOST
42ARES_NI_NAMEREQD = _lib.ARES_NI_NAMEREQD
43ARES_NI_NUMERICSERV = _lib.ARES_NI_NUMERICSERV
44ARES_NI_DGRAM = _lib.ARES_NI_DGRAM
45ARES_NI_TCP = _lib.ARES_NI_TCP
46ARES_NI_UDP = _lib.ARES_NI_UDP
47ARES_NI_SCTP = _lib.ARES_NI_SCTP
48ARES_NI_DCCP = _lib.ARES_NI_DCCP
49ARES_NI_NUMERICSCOPE = _lib.ARES_NI_NUMERICSCOPE
50ARES_NI_LOOKUPHOST = _lib.ARES_NI_LOOKUPHOST
51ARES_NI_LOOKUPSERVICE = _lib.ARES_NI_LOOKUPSERVICE
52ARES_NI_IDN = _lib.ARES_NI_IDN
53ARES_NI_IDN_ALLOW_UNASSIGNED = _lib.ARES_NI_IDN_ALLOW_UNASSIGNED
54ARES_NI_IDN_USE_STD3_ASCII_RULES = _lib.ARES_NI_IDN_USE_STD3_ASCII_RULES
56# Bad socket
57ARES_SOCKET_BAD = _lib.ARES_SOCKET_BAD
59# Query types
60QUERY_TYPE_A = _lib.ARES_REC_TYPE_A
61QUERY_TYPE_AAAA = _lib.ARES_REC_TYPE_AAAA
62QUERY_TYPE_ANY = _lib.ARES_REC_TYPE_ANY
63QUERY_TYPE_CAA = _lib.ARES_REC_TYPE_CAA
64QUERY_TYPE_CNAME = _lib.ARES_REC_TYPE_CNAME
65QUERY_TYPE_MX = _lib.ARES_REC_TYPE_MX
66QUERY_TYPE_NAPTR = _lib.ARES_REC_TYPE_NAPTR
67QUERY_TYPE_NS = _lib.ARES_REC_TYPE_NS
68QUERY_TYPE_PTR = _lib.ARES_REC_TYPE_PTR
69QUERY_TYPE_SOA = _lib.ARES_REC_TYPE_SOA
70QUERY_TYPE_SRV = _lib.ARES_REC_TYPE_SRV
71QUERY_TYPE_TXT = _lib.ARES_REC_TYPE_TXT
72QUERY_TYPE_TLSA = _lib.ARES_REC_TYPE_TLSA
73QUERY_TYPE_HTTPS = _lib.ARES_REC_TYPE_HTTPS
74QUERY_TYPE_URI = _lib.ARES_REC_TYPE_URI
76# Query classes
77QUERY_CLASS_IN = _lib.ARES_CLASS_IN
78QUERY_CLASS_CHAOS = _lib.ARES_CLASS_CHAOS
79QUERY_CLASS_HS = _lib.ARES_CLASS_HESOID
80QUERY_CLASS_NONE = _lib.ARES_CLASS_NONE
81QUERY_CLASS_ANY = _lib.ARES_CLASS_ANY
83ARES_VERSION = maybe_str(_ffi.string(_lib.ares_version(_ffi.NULL)))
84PYCARES_ADDRTTL_SIZE = 256
87class AresError(Exception):
88 pass
91# callback helpers
93_handle_to_channel: Dict[Any, "Channel"] = {} # Maps handle to channel to prevent use-after-free
96@_ffi.def_extern()
97def _sock_state_cb(data, socket_fd, readable, writable):
98 # Note: sock_state_cb handle is not tracked in _handle_to_channel
99 # because it has a different lifecycle (tied to the channel, not individual queries)
100 sock_state_cb = _ffi.from_handle(data)
101 sock_state_cb(socket_fd, readable, writable)
103@_ffi.def_extern()
104def _host_cb(arg, status, timeouts, hostent):
105 if arg not in _handle_to_channel:
106 return
108 callback = _ffi.from_handle(arg)
110 try:
111 if status != _lib.ARES_SUCCESS:
112 result = None
113 else:
114 result = parse_hostent(hostent)
115 status = None
116 callback(result, status)
117 finally:
118 _handle_to_channel.pop(arg, None)
120@_ffi.def_extern()
121def _nameinfo_cb(arg, status, timeouts, node, service):
122 if arg not in _handle_to_channel:
123 return
125 callback = _ffi.from_handle(arg)
127 try:
128 if status != _lib.ARES_SUCCESS:
129 result = None
130 else:
131 result = parse_nameinfo(node, service)
132 status = None
133 callback(result, status)
134 finally:
135 _handle_to_channel.pop(arg, None)
137@_ffi.def_extern()
138def _query_dnsrec_cb(arg, status, timeouts, dnsrec):
139 """Callback for new DNS record API queries"""
141 if arg not in _handle_to_channel:
142 return
144 callback = _ffi.from_handle(arg)
146 try:
147 if status != _lib.ARES_SUCCESS:
148 result = None
149 else:
150 result, parse_status = parse_dnsrec(dnsrec)
151 if parse_status is not None:
152 status = parse_status
153 else:
154 # Success - set status to None
155 status = None
156 callback(result, status)
157 finally:
158 _handle_to_channel.pop(arg, None)
161@_ffi.def_extern()
162def _addrinfo_cb(arg, status, timeouts, res):
163 if arg not in _handle_to_channel:
164 return
166 callback = _ffi.from_handle(arg)
168 try:
169 if status != _lib.ARES_SUCCESS:
170 result = None
171 else:
172 result = parse_addrinfo(res)
173 status = None
174 callback(result, status)
175 finally:
176 _handle_to_channel.pop(arg, None)
179def _extract_opt_params(rr, key):
180 """Extract OPT params as list of (key, value) tuples for HTTPS/SVCB records."""
181 opt_cnt = _lib.ares_dns_rr_get_opt_cnt(rr, key)
182 if opt_cnt == 0:
183 return []
184 # Collect all options as a list of (key, value) tuples
185 params = []
186 for i in range(opt_cnt):
187 val_ptr = _ffi.new("unsigned char **")
188 val_len = _ffi.new("size_t *")
189 opt_key = _lib.ares_dns_rr_get_opt(rr, key, i, val_ptr, val_len)
190 if val_ptr[0] != _ffi.NULL:
191 val = bytes(_ffi.buffer(val_ptr[0], val_len[0]))
192 else:
193 val = b''
194 params.append((opt_key, val))
195 return params
197def _extract_str_data(rr, key):
198 """Retrieves a string"""
199 value = _lib.ares_dns_rr_get_str(rr, key)
200 return maybe_str(_ffi.string(value)) if value != _ffi.NULL else ""
202def _extract_bin_data_as_str(rr, key):
203 """Retrieves binary data as a string"""
204 length = _ffi.new("size_t *")
205 value = _lib.ares_dns_rr_get_bin(rr, key, length)
206 return maybe_str(_ffi.string(value)) if value != _ffi.NULL else ""
208def _extract_bin_data_as_bytes(rr, key):
209 """Retrieves binary data as bytes"""
210 length = _ffi.new("size_t *")
211 value = _lib.ares_dns_rr_get_bin(rr, key, length)
212 return bytes(_ffi.buffer(value, length[0])) if value != _ffi.NULL else b''
215def extract_record_data(rr, record_type):
216 """Extract type-specific data from a DNS resource record and return appropriate dataclass"""
217 if record_type == _lib.ARES_REC_TYPE_A:
218 addr = _lib.ares_dns_rr_get_addr(rr, _lib.ARES_RR_A_ADDR)
219 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
220 _lib.ares_inet_ntop(socket.AF_INET, addr, buf, _lib.INET6_ADDRSTRLEN)
221 return ARecordData(addr=maybe_str(_ffi.string(buf)))
223 elif record_type == _lib.ARES_REC_TYPE_AAAA:
224 addr = _lib.ares_dns_rr_get_addr6(rr, _lib.ARES_RR_AAAA_ADDR)
225 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
226 _lib.ares_inet_ntop(socket.AF_INET6, addr, buf, _lib.INET6_ADDRSTRLEN)
227 return AAAARecordData(addr=maybe_str(_ffi.string(buf)))
229 elif record_type == _lib.ARES_REC_TYPE_MX:
230 priority = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_MX_PREFERENCE)
231 exchange = _extract_str_data(rr, _lib.ARES_RR_MX_EXCHANGE)
232 return MXRecordData(priority=priority, exchange=exchange)
234 elif record_type == _lib.ARES_REC_TYPE_TXT:
235 # TXT records use ABIN (array of binary) for chunks
236 cnt = _lib.ares_dns_rr_get_abin_cnt(rr, _lib.ARES_RR_TXT_DATA)
237 chunks = []
238 for i in range(cnt):
239 length = _ffi.new("size_t *")
240 data = _lib.ares_dns_rr_get_abin(rr, _lib.ARES_RR_TXT_DATA, i, length)
241 if data != _ffi.NULL:
242 chunks.append(_ffi.buffer(data, length[0])[:])
243 return TXTRecordData(data=b''.join(chunks))
245 elif record_type == _lib.ARES_REC_TYPE_CAA:
246 critical = _lib.ares_dns_rr_get_u8(rr, _lib.ARES_RR_CAA_CRITICAL)
247 tag = _extract_str_data(rr, _lib.ARES_RR_CAA_TAG)
248 value_str = _extract_bin_data_as_str(rr, _lib.ARES_RR_CAA_VALUE)
249 return CAARecordData(critical=critical, tag=tag, value=value_str)
251 elif record_type == _lib.ARES_REC_TYPE_CNAME:
252 cname = _extract_str_data(rr, _lib.ARES_RR_CNAME_CNAME)
253 return CNAMERecordData(cname=cname)
255 elif record_type == _lib.ARES_REC_TYPE_NAPTR:
256 order = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_NAPTR_ORDER)
257 preference = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_NAPTR_PREFERENCE)
258 flags = _extract_str_data(rr, _lib.ARES_RR_NAPTR_FLAGS)
259 service = _extract_str_data(rr, _lib.ARES_RR_NAPTR_SERVICES)
260 regexp = _extract_str_data(rr, _lib.ARES_RR_NAPTR_REGEXP)
261 replacement = _extract_str_data(rr, _lib.ARES_RR_NAPTR_REPLACEMENT)
262 return NAPTRRecordData(
263 order=order,
264 preference=preference,
265 flags=flags,
266 service=service,
267 regexp=regexp,
268 replacement=replacement
269 )
271 elif record_type == _lib.ARES_REC_TYPE_NS:
272 nsdname = _extract_str_data(rr, _lib.ARES_RR_NS_NSDNAME)
273 return NSRecordData(nsdname=nsdname)
275 elif record_type == _lib.ARES_REC_TYPE_PTR:
276 dname = _extract_str_data(rr, _lib.ARES_RR_PTR_DNAME)
277 return PTRRecordData(dname=dname)
279 elif record_type == _lib.ARES_REC_TYPE_SOA:
280 mname = _extract_str_data(rr, _lib.ARES_RR_SOA_MNAME)
281 rname = _extract_str_data(rr, _lib.ARES_RR_SOA_RNAME)
282 serial = _lib.ares_dns_rr_get_u32(rr, _lib.ARES_RR_SOA_SERIAL)
283 refresh = _lib.ares_dns_rr_get_u32(rr, _lib.ARES_RR_SOA_REFRESH)
284 retry = _lib.ares_dns_rr_get_u32(rr, _lib.ARES_RR_SOA_RETRY)
285 expire = _lib.ares_dns_rr_get_u32(rr, _lib.ARES_RR_SOA_EXPIRE)
286 minimum = _lib.ares_dns_rr_get_u32(rr, _lib.ARES_RR_SOA_MINIMUM)
287 return SOARecordData(
288 mname=mname,
289 rname=rname,
290 serial=serial,
291 refresh=refresh,
292 retry=retry,
293 expire=expire,
294 minimum=minimum
295 )
297 elif record_type == _lib.ARES_REC_TYPE_SRV:
298 priority = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_SRV_PRIORITY)
299 weight = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_SRV_WEIGHT)
300 port = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_SRV_PORT)
301 target = _extract_str_data(rr, _lib.ARES_RR_SRV_TARGET)
302 return SRVRecordData(
303 priority=priority,
304 weight=weight,
305 port=port,
306 target=target
307 )
309 elif record_type == _lib.ARES_REC_TYPE_TLSA:
310 cert_usage = _lib.ares_dns_rr_get_u8(rr, _lib.ARES_RR_TLSA_CERT_USAGE)
311 selector = _lib.ares_dns_rr_get_u8(rr, _lib.ARES_RR_TLSA_SELECTOR)
312 matching_type = _lib.ares_dns_rr_get_u8(rr, _lib.ARES_RR_TLSA_MATCH)
313 cert_data = _extract_bin_data_as_bytes(rr, _lib.ARES_RR_TLSA_DATA)
314 return TLSARecordData(
315 cert_usage=cert_usage,
316 selector=selector,
317 matching_type=matching_type,
318 cert_association_data=cert_data
319 )
321 elif record_type == _lib.ARES_REC_TYPE_HTTPS:
322 priority = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_HTTPS_PRIORITY)
323 target = _extract_str_data(rr, _lib.ARES_RR_HTTPS_TARGET)
324 params = _extract_opt_params(rr, _lib.ARES_RR_HTTPS_PARAMS)
325 return HTTPSRecordData(
326 priority=priority,
327 target=target,
328 params=params
329 )
331 elif record_type == _lib.ARES_REC_TYPE_URI:
332 priority = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_URI_PRIORITY)
333 weight = _lib.ares_dns_rr_get_u16(rr, _lib.ARES_RR_URI_WEIGHT)
334 target = _extract_str_data(rr, _lib.ARES_RR_URI_TARGET)
335 return URIRecordData(
336 priority=priority,
337 weight=weight,
338 target=target
339 )
341 else:
342 # Unknown record type - return None or raise error
343 raise ValueError(f"Unsupported DNS record type: {record_type}")
346def parse_dnsrec(dnsrec):
347 """Parse ares_dns_record_t into DNSResult with all sections"""
348 if dnsrec == _ffi.NULL:
349 return None, _lib.ARES_EBADRESP
351 answer_records = []
352 authority_records = []
353 additional_records = []
355 # Parse answer section
356 answer_count = _lib.ares_dns_record_rr_cnt(dnsrec, _lib.ARES_SECTION_ANSWER)
357 for i in range(answer_count):
358 rr = _lib.ares_dns_record_rr_get_const(dnsrec, _lib.ARES_SECTION_ANSWER, i)
359 if rr != _ffi.NULL:
360 name = maybe_str(_ffi.string(_lib.ares_dns_rr_get_name(rr)))
361 rec_type = _lib.ares_dns_rr_get_type(rr)
362 rec_class = _lib.ares_dns_rr_get_class(rr)
363 ttl = _lib.ares_dns_rr_get_ttl(rr)
365 try:
366 data = extract_record_data(rr, rec_type)
367 answer_records.append(DNSRecord(
368 name=name,
369 type=rec_type,
370 record_class=rec_class,
371 ttl=ttl,
372 data=data
373 ))
374 except (ValueError, Exception):
375 # Skip unsupported record types
376 pass
378 # Parse authority section
379 authority_count = _lib.ares_dns_record_rr_cnt(dnsrec, _lib.ARES_SECTION_AUTHORITY)
380 for i in range(authority_count):
381 rr = _lib.ares_dns_record_rr_get_const(dnsrec, _lib.ARES_SECTION_AUTHORITY, i)
382 if rr != _ffi.NULL:
383 name = maybe_str(_ffi.string(_lib.ares_dns_rr_get_name(rr)))
384 rec_type = _lib.ares_dns_rr_get_type(rr)
385 rec_class = _lib.ares_dns_rr_get_class(rr)
386 ttl = _lib.ares_dns_rr_get_ttl(rr)
388 try:
389 data = extract_record_data(rr, rec_type)
390 authority_records.append(DNSRecord(
391 name=name,
392 type=rec_type,
393 record_class=rec_class,
394 ttl=ttl,
395 data=data
396 ))
397 except (ValueError, Exception):
398 # Skip unsupported record types
399 pass
401 # Parse additional section
402 additional_count = _lib.ares_dns_record_rr_cnt(dnsrec, _lib.ARES_SECTION_ADDITIONAL)
403 for i in range(additional_count):
404 rr = _lib.ares_dns_record_rr_get_const(dnsrec, _lib.ARES_SECTION_ADDITIONAL, i)
405 if rr != _ffi.NULL:
406 name = maybe_str(_ffi.string(_lib.ares_dns_rr_get_name(rr)))
407 rec_type = _lib.ares_dns_rr_get_type(rr)
408 rec_class = _lib.ares_dns_rr_get_class(rr)
409 ttl = _lib.ares_dns_rr_get_ttl(rr)
411 try:
412 data = extract_record_data(rr, rec_type)
413 additional_records.append(DNSRecord(
414 name=name,
415 type=rec_type,
416 record_class=rec_class,
417 ttl=ttl,
418 data=data
419 ))
420 except (ValueError, Exception):
421 # Skip unsupported record types
422 pass
424 result = DNSResult(
425 answer=answer_records,
426 authority=authority_records,
427 additional=additional_records
428 )
430 return result, None
433class _ChannelShutdownManager:
434 """Manages channel destruction in a single background thread using SimpleQueue."""
436 def __init__(self) -> None:
437 self._queue: SimpleQueue = SimpleQueue()
438 self._thread: Optional[threading.Thread] = None
439 self._start_lock = threading.Lock()
441 def _run_safe_shutdown_loop(self) -> None:
442 """Process channel destruction requests from the queue."""
443 while True:
444 # Block forever until we get a channel to destroy
445 ch, _ = self._queue.get()
446 channel = ch[0]
448 # Cancel all pending queries - this will trigger callbacks with ARES_ECANCELLED
449 _lib.ares_cancel(channel)
451 # Wait for all queries to finish
452 _lib.ares_queue_wait_empty(channel, -1)
454 # Destroy the channel
455 _lib.ares_destroy(channel)
457 def start(self) -> None:
458 """Start the background thread if not already started."""
459 if self._thread is not None:
460 return
461 with self._start_lock:
462 if self._thread is not None:
463 # Started by another thread while waiting for the lock
464 return
465 self._thread = threading.Thread(target=self._run_safe_shutdown_loop, daemon=True)
466 self._thread.start()
468 def destroy_channel(self, channel, sock_state_cb_handle) -> None:
469 """
470 Schedule channel destruction on the background thread.
472 The socket state callback handle is passed along to ensure it remains
473 alive until the channel is destroyed.
475 Thread Safety and Synchronization:
476 This method uses SimpleQueue which is thread-safe for putting items
477 from multiple threads. The background thread processes channels
478 sequentially waiting for queries to end before each destruction.
479 """
480 self._queue.put((channel, sock_state_cb_handle))
483# Global shutdown manager instance
484_shutdown_manager = _ChannelShutdownManager()
487class Channel:
488 __qtypes__ = (_lib.ARES_REC_TYPE_A, _lib.ARES_REC_TYPE_AAAA, _lib.ARES_REC_TYPE_ANY, _lib.ARES_REC_TYPE_CAA, _lib.ARES_REC_TYPE_CNAME, _lib.ARES_REC_TYPE_HTTPS, _lib.ARES_REC_TYPE_MX, _lib.ARES_REC_TYPE_NAPTR, _lib.ARES_REC_TYPE_NS, _lib.ARES_REC_TYPE_PTR, _lib.ARES_REC_TYPE_SOA, _lib.ARES_REC_TYPE_SRV, _lib.ARES_REC_TYPE_TLSA, _lib.ARES_REC_TYPE_TXT, _lib.ARES_REC_TYPE_URI)
489 __qclasses__ = (_lib.ARES_CLASS_IN, _lib.ARES_CLASS_CHAOS, _lib.ARES_CLASS_HESOID, _lib.ARES_CLASS_NONE, _lib.ARES_CLASS_ANY)
491 def __init__(self,
492 *,
493 flags: Optional[int] = None,
494 timeout: Optional[float] = None,
495 tries: Optional[int] = None,
496 ndots: Optional[int] = None,
497 tcp_port: Optional[int] = None,
498 udp_port: Optional[int] = None,
499 servers: Optional[Iterable[Union[str, bytes]]] = None,
500 domains: Optional[Iterable[Union[str, bytes]]] = None,
501 lookups: Union[str, bytes, None] = None,
502 sock_state_cb: Optional[Callable[[int, bool, bool], None]] = None,
503 socket_send_buffer_size: Optional[int] = None,
504 socket_receive_buffer_size: Optional[int] = None,
505 rotate: bool = False,
506 local_ip: Union[str, bytes, None] = None,
507 local_dev: Optional[str] = None,
508 resolvconf_path: Union[str, bytes, None] = None) -> None:
510 # Initialize _channel to None first to ensure __del__ doesn't fail
511 self._channel = None
512 self._lock = threading.RLock()
514 # Store flags for later use (default is 0 if not specified)
515 self._flags = flags if flags is not None else 0
517 channel = _ffi.new("ares_channel *")
518 options = _ffi.new("struct ares_options *")
519 optmask = 0
521 if flags is not None:
522 options.flags = flags
523 optmask = optmask | _lib.ARES_OPT_FLAGS
525 if timeout is not None:
526 options.timeout = int(timeout * 1000)
527 optmask = optmask | _lib.ARES_OPT_TIMEOUTMS
529 if tries is not None:
530 options.tries = tries
531 optmask = optmask | _lib.ARES_OPT_TRIES
533 if ndots is not None:
534 options.ndots = ndots
535 optmask = optmask | _lib.ARES_OPT_NDOTS
537 if tcp_port is not None:
538 options.tcp_port = tcp_port
539 optmask = optmask | _lib.ARES_OPT_TCP_PORT
541 if udp_port is not None:
542 options.udp_port = udp_port
543 optmask = optmask | _lib.ARES_OPT_UDP_PORT
545 if socket_send_buffer_size is not None:
546 options.socket_send_buffer_size = socket_send_buffer_size
547 optmask = optmask | _lib.ARES_OPT_SOCK_SNDBUF
549 if socket_receive_buffer_size is not None:
550 options.socket_receive_buffer_size = socket_receive_buffer_size
551 optmask = optmask | _lib.ARES_OPT_SOCK_RCVBUF
553 if sock_state_cb:
554 if not callable(sock_state_cb):
555 raise TypeError("sock_state_cb is not callable")
557 userdata = _ffi.new_handle(sock_state_cb)
559 # This must be kept alive while the channel is alive.
560 self._sock_state_cb_handle = userdata
562 options.sock_state_cb = _lib._sock_state_cb
563 options.sock_state_cb_data = userdata
564 optmask = optmask | _lib.ARES_OPT_SOCK_STATE_CB
565 else:
566 self._sock_state_cb_handle = None
567 optmask = optmask | _lib.ARES_OPT_EVENT_THREAD
568 options.evsys = _lib.ARES_EVSYS_DEFAULT
570 if lookups:
571 options.lookups = _ffi.new('char[]', ascii_bytes(lookups))
572 optmask = optmask | _lib.ARES_OPT_LOOKUPS
574 if domains:
575 strs = [_ffi.new("char[]", ascii_bytes(i)) for i in domains]
576 c = _ffi.new("char *[%d]" % (len(domains) + 1))
577 for i in range(len(domains)):
578 c[i] = strs[i]
580 options.domains = c
581 options.ndomains = len(domains)
582 optmask = optmask | _lib.ARES_OPT_DOMAINS
584 if rotate:
585 optmask = optmask | _lib.ARES_OPT_ROTATE
587 if resolvconf_path is not None:
588 optmask = optmask | _lib.ARES_OPT_RESOLVCONF
589 options.resolvconf_path = _ffi.new('char[]', ascii_bytes(resolvconf_path))
591 r = _lib.ares_init_options(channel, options, optmask)
592 if r != _lib.ARES_SUCCESS:
593 raise AresError('Failed to initialize c-ares channel')
595 self._channel = channel
596 if servers:
597 self.servers = servers
599 if local_ip:
600 self.set_local_ip(local_ip)
602 if local_dev:
603 self.set_local_dev(local_dev)
605 # Ensure the shutdown thread is started
606 _shutdown_manager.start()
608 def __del__(self) -> None:
609 # Deliberately lock-free: when __del__ runs, no submitter holds
610 # self and no callback is pending, so the read-and-null in
611 # _destroy_channel is uncontested.
612 self._destroy_channel()
614 @contextlib.contextmanager
615 def _capture_channel(self):
616 """
617 Yield the c-ares channel pointer while holding self._lock so that
618 a concurrent close() cannot destroy the channel mid-call.
620 Raises RuntimeError if the channel has been closed.
621 """
622 with self._lock:
623 channel = self._channel
624 if channel is None:
625 raise RuntimeError("Channel is destroyed, no new queries allowed")
626 yield channel[0]
628 def _register_callback_handle(self, callback_data):
629 """
630 Wrap callback_data in a cffi handle and register it so that it (and
631 the Channel) stay alive until the c-ares callback fires. Callers
632 MUST pop the handle from _handle_to_channel on any exception path
633 before the c-ares callback could ever observe it.
634 """
635 userdata = _ffi.new_handle(callback_data)
636 _handle_to_channel[userdata] = self
637 return userdata
639 def cancel(self) -> None:
640 with self._capture_channel() as channel:
641 _lib.ares_cancel(channel)
643 def reinit(self) -> None:
644 with self._capture_channel() as channel:
645 r = _lib.ares_reinit(channel)
646 if r != _lib.ARES_SUCCESS:
647 raise AresError(r, errno.strerror(r))
649 @property
650 def servers(self) -> list[str]:
651 with self._capture_channel() as channel:
652 csv_str = _lib.ares_get_servers_csv(channel)
654 if csv_str == _ffi.NULL:
655 raise AresError(_lib.ARES_ENOMEM, errno.strerror(_lib.ARES_ENOMEM))
657 server_list = []
658 csv_string = maybe_str(_ffi.string(csv_str))
659 _lib.ares_free_string(csv_str)
660 server_list = [s.strip() for s in csv_string.split(',')]
662 return server_list
664 @servers.setter
665 def servers(self, servers: Iterable[Union[str, bytes]]) -> None:
666 server_list = [ascii_bytes(s).decode('ascii') if isinstance(s, bytes) else s for s in servers]
667 csv_str = ','.join(server_list)
669 with self._capture_channel() as channel:
670 r = _lib.ares_set_servers_csv(channel, csv_str.encode('ascii'))
671 if r != _lib.ARES_SUCCESS:
672 raise AresError(r, errno.strerror(r))
674 def process_fd(self, read_fd: int, write_fd: int) -> None:
675 with self._capture_channel() as channel:
676 _lib.ares_process_fd(channel, _ffi.cast("ares_socket_t", read_fd), _ffi.cast("ares_socket_t", write_fd))
678 def process_read_fd(self, read_fd:int) -> None:
679 with self._capture_channel() as channel:
680 _lib.ares_process_fd(channel, _ffi.cast("ares_socket_t", read_fd), _ffi.cast("ares_socket_t", ARES_SOCKET_BAD))
682 def process_write_fd(self, write_fd:int) -> None:
683 with self._capture_channel() as channel:
684 _lib.ares_process_fd(channel, _ffi.cast("ares_socket_t", ARES_SOCKET_BAD), _ffi.cast("ares_socket_t", write_fd))
686 def timeout(self, t = None):
687 maxtv = _ffi.NULL
688 tv = _ffi.new("struct timeval*")
690 if t is not None:
691 if t >= 0.0:
692 maxtv = _ffi.new("struct timeval*")
693 maxtv.tv_sec = int(math.floor(t))
694 maxtv.tv_usec = int(math.fmod(t, 1.0) * 1000000)
695 else:
696 raise ValueError("timeout needs to be a positive number or None")
698 with self._capture_channel() as channel:
699 result = _lib.ares_timeout(channel, maxtv, tv)
701 if result == _ffi.NULL:
702 return 0.0
704 return (result.tv_sec + result.tv_usec / 1000000.0)
706 def gethostbyaddr(self, addr: str, *, callback: Callable[[Any, int], None]) -> None:
707 if not callable(callback):
708 raise TypeError("a callable is required")
710 addr4 = _ffi.new("struct in_addr*")
711 addr6 = _ffi.new("struct ares_in6_addr*")
712 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(addr), (addr4)) == 1:
713 address = addr4
714 family = socket.AF_INET
715 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(addr), (addr6)) == 1:
716 address = addr6
717 family = socket.AF_INET6
718 else:
719 raise ValueError("invalid IP address")
721 with self._capture_channel() as channel:
722 userdata = self._register_callback_handle(callback)
723 try:
724 _lib.ares_gethostbyaddr(channel, address, _ffi.sizeof(address[0]), family, _lib._host_cb, userdata)
725 except BaseException:
726 _handle_to_channel.pop(userdata, None)
727 raise
729 def getaddrinfo(
730 self,
731 host: str,
732 port: Optional[int],
733 *,
734 family: socket.AddressFamily = 0,
735 type: int = 0,
736 proto: int = 0,
737 flags: int = 0,
738 callback: Callable[[Any, int], None]
739 ) -> None:
740 if not callable(callback):
741 raise TypeError("a callable is required")
743 if port is None:
744 service = _ffi.NULL
745 elif isinstance(port, int):
746 service = str(port).encode('ascii')
747 else:
748 service = ascii_bytes(port)
750 with self._capture_channel() as channel:
751 userdata = self._register_callback_handle(callback)
752 try:
753 hints = _ffi.new('struct ares_addrinfo_hints*')
754 hints.ai_flags = flags
755 hints.ai_family = family
756 hints.ai_socktype = type
757 hints.ai_protocol = proto
758 _lib.ares_getaddrinfo(channel, parse_name(host), service, hints, _lib._addrinfo_cb, userdata)
759 except BaseException:
760 _handle_to_channel.pop(userdata, None)
761 raise
763 def query(self, name: str, query_type: int, *, query_class: int = QUERY_CLASS_IN, callback: Callable[[Any, int], None]) -> None:
764 """
765 Perform a DNS query.
767 Args:
768 name: Domain name to query
769 query_type: Type of query (e.g., QUERY_TYPE_A, QUERY_TYPE_AAAA, etc.)
770 query_class: Query class (default: QUERY_CLASS_IN)
771 callback: Callback function that receives (result, errno)
773 The callback will receive a DNSResult object containing answer, authority, and additional sections.
774 """
775 if not callable(callback):
776 raise TypeError('a callable is required')
778 if query_type not in self.__qtypes__:
779 raise ValueError('invalid query type specified')
781 if query_class not in self.__qclasses__:
782 raise ValueError('invalid query class specified')
784 with self._capture_channel() as channel:
785 userdata = self._register_callback_handle(callback)
786 try:
787 qid = _ffi.new("unsigned short *")
788 status = _lib.ares_query_dnsrec(
789 channel,
790 parse_name(name),
791 query_class,
792 query_type,
793 _lib._query_dnsrec_cb,
794 userdata,
795 qid
796 )
797 except BaseException:
798 _handle_to_channel.pop(userdata, None)
799 raise
800 if status != _lib.ARES_SUCCESS:
801 _handle_to_channel.pop(userdata, None)
802 raise AresError(status, errno.strerror(status))
804 def search(self, name: str, query_type: int, *, query_class: int = QUERY_CLASS_IN, callback: Callable[[Any, int], None]) -> None:
805 """
806 Perform a DNS search (honors resolv.conf search domains).
808 Args:
809 name: Domain name to search
810 query_type: Type of query (e.g., QUERY_TYPE_A, QUERY_TYPE_AAAA, etc.)
811 query_class: Query class (default: QUERY_CLASS_IN)
812 callback: Callback function that receives (result, errno)
814 The callback will receive a DNSResult object containing answer, authority, and additional sections.
815 """
816 if not callable(callback):
817 raise TypeError('a callable is required')
819 if query_type not in self.__qtypes__:
820 raise ValueError('invalid query type specified')
822 if query_class not in self.__qclasses__:
823 raise ValueError('invalid query class specified')
825 # Create a DNS record for the search query
826 # Set RD (Recursion Desired) flag unless ARES_FLAG_NORECURSE is set
827 dns_flags = 0 if (self._flags & _lib.ARES_FLAG_NORECURSE) else _lib.ARES_FLAG_RD
829 dnsrec_p = _ffi.new("ares_dns_record_t **")
830 status = _lib.ares_dns_record_create(
831 dnsrec_p,
832 0, # id (will be set by c-ares)
833 dns_flags, # flags - include RD for recursive queries
834 _lib.ARES_OPCODE_QUERY,
835 _lib.ARES_RCODE_NOERROR
836 )
837 if status != _lib.ARES_SUCCESS:
838 raise AresError(status, errno.strerror(status))
840 dnsrec = dnsrec_p[0]
842 # Add the query to the DNS record
843 status = _lib.ares_dns_record_query_add(
844 dnsrec,
845 parse_name(name),
846 query_type,
847 query_class
848 )
849 if status != _lib.ARES_SUCCESS:
850 _lib.ares_dns_record_destroy(dnsrec)
851 raise AresError(status, errno.strerror(status))
853 # dnsrec is only needed for this call (it's a const parameter); free
854 # it once ares_search_dnsrec() returns, regardless of outcome.
855 try:
856 with self._capture_channel() as channel:
857 userdata = self._register_callback_handle(callback)
858 try:
859 status = _lib.ares_search_dnsrec(
860 channel,
861 dnsrec,
862 _lib._query_dnsrec_cb,
863 userdata
864 )
865 except BaseException:
866 _handle_to_channel.pop(userdata, None)
867 raise
868 if status != _lib.ARES_SUCCESS:
869 raise AresError(status, errno.strerror(status))
870 finally:
871 _lib.ares_dns_record_destroy(dnsrec)
873 def set_local_ip(self, ip):
874 addr4 = _ffi.new("struct in_addr*")
875 addr6 = _ffi.new("struct ares_in6_addr*")
877 with self._capture_channel() as channel:
878 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), addr4) == 1:
879 _lib.ares_set_local_ip4(channel, socket.ntohl(addr4.s_addr))
880 elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), addr6) == 1:
881 _lib.ares_set_local_ip6(
882 channel,
883 _ffi.cast("unsigned char *", addr6),
884 )
885 else:
886 raise ValueError("invalid IP address")
888 def getnameinfo(self, address: Union[IP4, IP6], flags: int, *, callback: Callable[[Any, int], None]) -> None:
889 if not callable(callback):
890 raise TypeError("a callable is required")
892 if len(address) == 2:
893 (ip, port) = address
894 sa4 = _ffi.new("struct sockaddr_in*")
895 if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), _ffi.addressof(sa4.sin_addr)) != 1:
896 raise ValueError("Invalid IPv4 address %r" % ip)
897 sa4.sin_family = socket.AF_INET
898 sa4.sin_port = socket.htons(port)
899 sa = sa4
900 elif len(address) == 4:
901 (ip, port, flowinfo, scope_id) = address
902 sa6 = _ffi.new("struct sockaddr_in6*")
903 if _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), _ffi.addressof(sa6.sin6_addr)) != 1:
904 raise ValueError("Invalid IPv6 address %r" % ip)
905 sa6.sin6_family = socket.AF_INET6
906 sa6.sin6_port = socket.htons(port)
907 sa6.sin6_flowinfo = socket.htonl(flowinfo) # I'm unsure about byteorder here.
908 sa6.sin6_scope_id = scope_id # Yes, without htonl.
909 sa = sa6
910 else:
911 raise ValueError("Invalid address argument")
913 with self._capture_channel() as channel:
914 userdata = self._register_callback_handle(callback)
915 try:
916 _lib.ares_getnameinfo(channel, _ffi.cast("struct sockaddr*", sa), _ffi.sizeof(sa[0]), flags, _lib._nameinfo_cb, userdata)
917 except BaseException:
918 _handle_to_channel.pop(userdata, None)
919 raise
921 def set_local_dev(self, dev):
922 with self._capture_channel() as channel:
923 _lib.ares_set_local_dev(channel, ascii_bytes(dev))
925 def close(self) -> None:
926 """
927 Close the channel as soon as it's safe to do so.
929 This method can be called from any thread. The channel will be destroyed
930 safely using a background thread, after any in-flight submission has
931 returned, to ensure c-ares has completed its cleanup.
933 Note: Once close() is called, no new queries can be started. Any pending
934 queries will be cancelled and their callbacks will receive ARES_ECANCELLED.
936 """
937 with self._lock:
938 self._destroy_channel()
940 def _destroy_channel(self) -> None:
941 channel, self._channel = self._channel, None
942 if channel is None:
943 # Already destroyed
944 return
946 # NB: don't cancel queries here, it may lead to problem if done from a
947 # query callback.
949 # Schedule channel destruction
950 _shutdown_manager.destroy_channel(channel, self._sock_state_cb_handle)
952 def wait(self, timeout: float=None) -> bool:
953 """
954 Wait until all pending queries are complete or timeout occurs.
956 Args:
957 timeout: Maximum time to wait in seconds. Use -1 for infinite wait.
958 """
959 with self._capture_channel() as channel:
960 r = _lib.ares_queue_wait_empty(channel, int(timeout * 1000) if timeout is not None and timeout >= 0 else -1)
961 if r == _lib.ARES_SUCCESS:
962 return True
963 elif r == _lib.ARES_ETIMEOUT:
964 return False
965 else:
966 raise AresError(r, errno.strerror(r))
969# DNS query result types - New dataclass-based API
970#
972@dataclass
973class ARecordData:
974 """Data for A (IPv4 address) record"""
975 addr: str
977@dataclass
978class AAAARecordData:
979 """Data for AAAA (IPv6 address) record"""
980 addr: str
982@dataclass
983class MXRecordData:
984 """Data for MX (mail exchange) record"""
985 priority: int
986 exchange: str
988@dataclass
989class TXTRecordData:
990 """Data for TXT (text) record"""
991 data: bytes
993@dataclass
994class CAARecordData:
995 """Data for CAA (certification authority authorization) record"""
996 critical: int
997 tag: str
998 value: str
1000@dataclass
1001class CNAMERecordData:
1002 """Data for CNAME (canonical name) record"""
1003 cname: str
1005@dataclass
1006class NAPTRRecordData:
1007 """Data for NAPTR (naming authority pointer) record"""
1008 order: int
1009 preference: int
1010 flags: str
1011 service: str
1012 regexp: str
1013 replacement: str
1015@dataclass
1016class NSRecordData:
1017 """Data for NS (name server) record"""
1018 nsdname: str
1020@dataclass
1021class PTRRecordData:
1022 """Data for PTR (pointer) record"""
1023 dname: str
1025@dataclass
1026class SOARecordData:
1027 """Data for SOA (start of authority) record"""
1028 mname: str
1029 rname: str
1030 serial: int
1031 refresh: int
1032 retry: int
1033 expire: int
1034 minimum: int
1036@dataclass
1037class SRVRecordData:
1038 """Data for SRV (service) record"""
1039 priority: int
1040 weight: int
1041 port: int
1042 target: str
1044@dataclass
1045class TLSARecordData:
1046 """Data for TLSA (DANE TLS authentication) record - RFC 6698"""
1047 cert_usage: int
1048 selector: int
1049 matching_type: int
1050 cert_association_data: bytes
1052@dataclass
1053class HTTPSRecordData:
1054 """Data for HTTPS (service binding) record - RFC 9460"""
1055 priority: int
1056 target: str
1057 params: list # List of (key: int, value: bytes) tuples
1059@dataclass
1060class URIRecordData:
1061 """Data for URI (Uniform Resource Identifier) record - RFC 7553"""
1062 priority: int
1063 weight: int
1064 target: str
1066@dataclass
1067class DNSRecord:
1068 """Represents a single DNS resource record"""
1069 name: str
1070 type: int
1071 record_class: int
1072 ttl: int
1073 data: Union[ARecordData, AAAARecordData, MXRecordData, TXTRecordData,
1074 CAARecordData, CNAMERecordData, HTTPSRecordData, NAPTRRecordData,
1075 NSRecordData, PTRRecordData, SOARecordData, SRVRecordData,
1076 TLSARecordData, URIRecordData]
1078@dataclass
1079class DNSResult:
1080 """Represents a complete DNS query result with all sections"""
1081 answer: list[DNSRecord]
1082 authority: list[DNSRecord]
1083 additional: list[DNSRecord]
1086# Host/AddrInfo result types
1088@dataclass
1089class HostResult:
1090 """Result from gethostbyaddr() operation"""
1091 name: str
1092 aliases: list[str]
1093 addresses: list[str]
1095@dataclass
1096class NameInfoResult:
1097 """Result from getnameinfo() operation"""
1098 node: str
1099 service: Optional[str]
1101@dataclass
1102class AddrInfoNode:
1103 """Single address node from getaddrinfo() result"""
1104 ttl: int
1105 flags: int
1106 family: int
1107 socktype: int
1108 protocol: int
1109 addr: tuple # (ip, port) or (ip, port, flowinfo, scope_id)
1111@dataclass
1112class AddrInfoCname:
1113 """CNAME information from getaddrinfo() result"""
1114 ttl: int
1115 alias: str
1116 name: str
1118@dataclass
1119class AddrInfoResult:
1120 """Complete result from getaddrinfo() operation"""
1121 cnames: list[AddrInfoCname]
1122 nodes: list[AddrInfoNode]
1125# Parser functions for Host/AddrInfo results
1127def parse_hostent(hostent) -> HostResult:
1128 """Parse c-ares hostent structure into HostResult"""
1129 name = maybe_str(_ffi.string(hostent.h_name))
1130 aliases = []
1131 addresses = []
1133 i = 0
1134 while hostent.h_aliases[i] != _ffi.NULL:
1135 aliases.append(maybe_str(_ffi.string(hostent.h_aliases[i])))
1136 i += 1
1138 i = 0
1139 while hostent.h_addr_list[i] != _ffi.NULL:
1140 buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
1141 if _ffi.NULL != _lib.ares_inet_ntop(hostent.h_addrtype, hostent.h_addr_list[i], buf, _lib.INET6_ADDRSTRLEN):
1142 addresses.append(maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN)))
1143 i += 1
1145 return HostResult(name=name, aliases=aliases, addresses=addresses)
1148def parse_nameinfo(node, service) -> NameInfoResult:
1149 """Parse c-ares nameinfo into NameInfoResult"""
1150 node_str = maybe_str(_ffi.string(node))
1151 service_str = maybe_str(_ffi.string(service)) if service != _ffi.NULL else None
1152 return NameInfoResult(node=node_str, service=service_str)
1155def parse_addrinfo_node(ares_node) -> AddrInfoNode:
1156 """Parse a single c-ares addrinfo node into AddrInfoNode"""
1157 ttl = ares_node.ai_ttl
1158 flags = ares_node.ai_flags
1159 socktype = ares_node.ai_socktype
1160 protocol = ares_node.ai_protocol
1162 addr_struct = ares_node.ai_addr
1163 assert addr_struct.sa_family == ares_node.ai_family
1164 ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN)
1166 if addr_struct.sa_family == socket.AF_INET:
1167 family = socket.AF_INET
1168 s = _ffi.cast("struct sockaddr_in*", addr_struct)
1169 if _ffi.NULL != _lib.ares_inet_ntop(s.sin_family, _ffi.addressof(s.sin_addr), ip, _lib.INET6_ADDRSTRLEN):
1170 # (address, port) 2-tuple for AF_INET
1171 addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin_port))
1172 else:
1173 raise ValueError("failed to convert IPv4 address")
1174 elif addr_struct.sa_family == socket.AF_INET6:
1175 family = socket.AF_INET6
1176 s = _ffi.cast("struct sockaddr_in6*", addr_struct)
1177 if _ffi.NULL != _lib.ares_inet_ntop(s.sin6_family, _ffi.addressof(s.sin6_addr), ip, _lib.INET6_ADDRSTRLEN):
1178 # (address, port, flow info, scope id) 4-tuple for AF_INET6
1179 addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin6_port), s.sin6_flowinfo, s.sin6_scope_id)
1180 else:
1181 raise ValueError("failed to convert IPv6 address")
1182 else:
1183 raise ValueError("invalid sockaddr family")
1185 return AddrInfoNode(ttl=ttl, flags=flags, family=family, socktype=socktype, protocol=protocol, addr=addr)
1188def parse_addrinfo_cname(ares_cname) -> AddrInfoCname:
1189 """Parse a single c-ares addrinfo cname into AddrInfoCname"""
1190 return AddrInfoCname(
1191 ttl=ares_cname.ttl,
1192 alias=maybe_str(_ffi.string(ares_cname.alias)),
1193 name=maybe_str(_ffi.string(ares_cname.name))
1194 )
1197def parse_addrinfo(ares_addrinfo) -> AddrInfoResult:
1198 """Parse c-ares addrinfo structure into AddrInfoResult"""
1199 cnames = []
1200 nodes = []
1202 cname_ptr = ares_addrinfo.cnames
1203 while cname_ptr != _ffi.NULL:
1204 cnames.append(parse_addrinfo_cname(cname_ptr))
1205 cname_ptr = cname_ptr.next
1207 node_ptr = ares_addrinfo.nodes
1208 while node_ptr != _ffi.NULL:
1209 nodes.append(parse_addrinfo_node(node_ptr))
1210 node_ptr = node_ptr.ai_next
1212 _lib.ares_freeaddrinfo(ares_addrinfo)
1214 return AddrInfoResult(cnames=cnames, nodes=nodes)
1217__all__ = (
1218 # Channel flags
1219 "ARES_FLAG_USEVC",
1220 "ARES_FLAG_PRIMARY",
1221 "ARES_FLAG_IGNTC",
1222 "ARES_FLAG_NORECURSE",
1223 "ARES_FLAG_STAYOPEN",
1224 "ARES_FLAG_NOSEARCH",
1225 "ARES_FLAG_NOALIASES",
1226 "ARES_FLAG_NOCHECKRESP",
1227 "ARES_FLAG_EDNS",
1228 "ARES_FLAG_NO_DFLT_SVR",
1230 # Nameinfo flag values
1231 "ARES_NI_NOFQDN",
1232 "ARES_NI_NUMERICHOST",
1233 "ARES_NI_NAMEREQD",
1234 "ARES_NI_NUMERICSERV",
1235 "ARES_NI_DGRAM",
1236 "ARES_NI_TCP",
1237 "ARES_NI_UDP",
1238 "ARES_NI_SCTP",
1239 "ARES_NI_DCCP",
1240 "ARES_NI_NUMERICSCOPE",
1241 "ARES_NI_LOOKUPHOST",
1242 "ARES_NI_LOOKUPSERVICE",
1243 "ARES_NI_IDN",
1244 "ARES_NI_IDN_ALLOW_UNASSIGNED",
1245 "ARES_NI_IDN_USE_STD3_ASCII_RULES",
1247 # Bad socket
1248 "ARES_SOCKET_BAD",
1250 # Query types
1251 "QUERY_TYPE_A",
1252 "QUERY_TYPE_AAAA",
1253 "QUERY_TYPE_ANY",
1254 "QUERY_TYPE_CAA",
1255 "QUERY_TYPE_CNAME",
1256 "QUERY_TYPE_HTTPS",
1257 "QUERY_TYPE_MX",
1258 "QUERY_TYPE_NAPTR",
1259 "QUERY_TYPE_NS",
1260 "QUERY_TYPE_PTR",
1261 "QUERY_TYPE_SOA",
1262 "QUERY_TYPE_SRV",
1263 "QUERY_TYPE_TLSA",
1264 "QUERY_TYPE_TXT",
1265 "QUERY_TYPE_URI",
1267 # Query classes
1268 "QUERY_CLASS_IN",
1269 "QUERY_CLASS_CHAOS",
1270 "QUERY_CLASS_HS",
1271 "QUERY_CLASS_NONE",
1272 "QUERY_CLASS_ANY",
1274 # Core stuff
1275 "ARES_VERSION",
1276 "AresError",
1277 "Channel",
1278 "errno",
1279 "__version__",
1281 # DNS record result types
1282 "DNSResult",
1283 "DNSRecord",
1284 "ARecordData",
1285 "AAAARecordData",
1286 "MXRecordData",
1287 "TXTRecordData",
1288 "CAARecordData",
1289 "CNAMERecordData",
1290 "HTTPSRecordData",
1291 "NAPTRRecordData",
1292 "NSRecordData",
1293 "PTRRecordData",
1294 "SOARecordData",
1295 "SRVRecordData",
1296 "TLSARecordData",
1297 "URIRecordData",
1299 # Host/AddrInfo result types
1300 "HostResult",
1301 "NameInfoResult",
1302 "AddrInfoResult",
1303 "AddrInfoNode",
1304 "AddrInfoCname",
1305)