pycares 4.9.0__cp313-cp313-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pycares/__init__.py ADDED
@@ -0,0 +1,1083 @@
1
+
2
+ from ._cares import ffi as _ffi, lib as _lib
3
+ import _cffi_backend # hint for bundler tools
4
+
5
+ if _lib.ARES_SUCCESS != _lib.ares_library_init(_lib.ARES_LIB_INIT_ALL):
6
+ raise RuntimeError('Could not initialize c-ares')
7
+
8
+ from . import errno
9
+ from .utils import ascii_bytes, maybe_str, parse_name
10
+ from ._version import __version__
11
+
12
+ import socket
13
+ import math
14
+ import threading
15
+ import time
16
+ import weakref
17
+ from collections.abc import Callable, Iterable
18
+ from contextlib import suppress
19
+ from typing import Any, Callable, Optional, Dict, Union
20
+ from queue import SimpleQueue
21
+
22
+ IP4 = tuple[str, int]
23
+ IP6 = tuple[str, int, int, int]
24
+
25
+ # Flag values
26
+ ARES_FLAG_USEVC = _lib.ARES_FLAG_USEVC
27
+ ARES_FLAG_PRIMARY = _lib.ARES_FLAG_PRIMARY
28
+ ARES_FLAG_IGNTC = _lib.ARES_FLAG_IGNTC
29
+ ARES_FLAG_NORECURSE = _lib.ARES_FLAG_NORECURSE
30
+ ARES_FLAG_STAYOPEN = _lib.ARES_FLAG_STAYOPEN
31
+ ARES_FLAG_NOSEARCH = _lib.ARES_FLAG_NOSEARCH
32
+ ARES_FLAG_NOALIASES = _lib.ARES_FLAG_NOALIASES
33
+ ARES_FLAG_NOCHECKRESP = _lib.ARES_FLAG_NOCHECKRESP
34
+ ARES_FLAG_EDNS = _lib.ARES_FLAG_EDNS
35
+ ARES_FLAG_NO_DFLT_SVR = _lib.ARES_FLAG_NO_DFLT_SVR
36
+
37
+ # Nameinfo flag values
38
+ ARES_NI_NOFQDN = _lib.ARES_NI_NOFQDN
39
+ ARES_NI_NUMERICHOST = _lib.ARES_NI_NUMERICHOST
40
+ ARES_NI_NAMEREQD = _lib.ARES_NI_NAMEREQD
41
+ ARES_NI_NUMERICSERV = _lib.ARES_NI_NUMERICSERV
42
+ ARES_NI_DGRAM = _lib.ARES_NI_DGRAM
43
+ ARES_NI_TCP = _lib.ARES_NI_TCP
44
+ ARES_NI_UDP = _lib.ARES_NI_UDP
45
+ ARES_NI_SCTP = _lib.ARES_NI_SCTP
46
+ ARES_NI_DCCP = _lib.ARES_NI_DCCP
47
+ ARES_NI_NUMERICSCOPE = _lib.ARES_NI_NUMERICSCOPE
48
+ ARES_NI_LOOKUPHOST = _lib.ARES_NI_LOOKUPHOST
49
+ ARES_NI_LOOKUPSERVICE = _lib.ARES_NI_LOOKUPSERVICE
50
+ ARES_NI_IDN = _lib.ARES_NI_IDN
51
+ ARES_NI_IDN_ALLOW_UNASSIGNED = _lib.ARES_NI_IDN_ALLOW_UNASSIGNED
52
+ ARES_NI_IDN_USE_STD3_ASCII_RULES = _lib.ARES_NI_IDN_USE_STD3_ASCII_RULES
53
+
54
+ # Bad socket
55
+ ARES_SOCKET_BAD = _lib.ARES_SOCKET_BAD
56
+
57
+ # Query types
58
+ QUERY_TYPE_A = _lib.T_A
59
+ QUERY_TYPE_AAAA = _lib.T_AAAA
60
+ QUERY_TYPE_ANY = _lib.T_ANY
61
+ QUERY_TYPE_CAA = _lib.T_CAA
62
+ QUERY_TYPE_CNAME = _lib.T_CNAME
63
+ QUERY_TYPE_MX = _lib.T_MX
64
+ QUERY_TYPE_NAPTR = _lib.T_NAPTR
65
+ QUERY_TYPE_NS = _lib.T_NS
66
+ QUERY_TYPE_PTR = _lib.T_PTR
67
+ QUERY_TYPE_SOA = _lib.T_SOA
68
+ QUERY_TYPE_SRV = _lib.T_SRV
69
+ QUERY_TYPE_TXT = _lib.T_TXT
70
+
71
+ # Query classes
72
+ QUERY_CLASS_IN = _lib.C_IN
73
+ QUERY_CLASS_CHAOS = _lib.C_CHAOS
74
+ QUERY_CLASS_HS = _lib.C_HS
75
+ QUERY_CLASS_NONE = _lib.C_NONE
76
+ QUERY_CLASS_ANY = _lib.C_ANY
77
+
78
+ ARES_VERSION = maybe_str(_ffi.string(_lib.ares_version(_ffi.NULL)))
79
+ PYCARES_ADDRTTL_SIZE = 256
80
+
81
+
82
+ class AresError(Exception):
83
+ pass
84
+
85
+
86
+ # callback helpers
87
+
88
+ _handle_to_channel: Dict[Any, "Channel"] = {} # Maps handle to channel to prevent use-after-free
89
+
90
+
91
+ @_ffi.def_extern()
92
+ def _sock_state_cb(data, socket_fd, readable, writable):
93
+ # Note: sock_state_cb handle is not tracked in _handle_to_channel
94
+ # because it has a different lifecycle (tied to the channel, not individual queries)
95
+ if _ffi is None:
96
+ return
97
+ sock_state_cb = _ffi.from_handle(data)
98
+ sock_state_cb(socket_fd, readable, writable)
99
+
100
+ @_ffi.def_extern()
101
+ def _host_cb(arg, status, timeouts, hostent):
102
+ # Get callback data without removing the reference yet
103
+ if _ffi is None or arg not in _handle_to_channel:
104
+ return
105
+
106
+ callback = _ffi.from_handle(arg)
107
+
108
+ if status != _lib.ARES_SUCCESS:
109
+ result = None
110
+ else:
111
+ result = ares_host_result(hostent)
112
+ status = None
113
+
114
+ callback(result, status)
115
+ _handle_to_channel.pop(arg, None)
116
+
117
+ @_ffi.def_extern()
118
+ def _nameinfo_cb(arg, status, timeouts, node, service):
119
+ # Get callback data without removing the reference yet
120
+ if _ffi is None or arg not in _handle_to_channel:
121
+ return
122
+
123
+ callback = _ffi.from_handle(arg)
124
+
125
+ if status != _lib.ARES_SUCCESS:
126
+ result = None
127
+ else:
128
+ result = ares_nameinfo_result(node, service)
129
+ status = None
130
+
131
+ callback(result, status)
132
+ _handle_to_channel.pop(arg, None)
133
+
134
+ @_ffi.def_extern()
135
+ def _query_cb(arg, status, timeouts, abuf, alen):
136
+ # Get callback data without removing the reference yet
137
+ if _ffi is None or arg not in _handle_to_channel:
138
+ return
139
+
140
+ callback, query_type = _ffi.from_handle(arg)
141
+
142
+ if status == _lib.ARES_SUCCESS:
143
+ if query_type == _lib.T_ANY:
144
+ result = []
145
+ 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):
146
+ r, status = parse_result(qtype, abuf, alen)
147
+ if status not in (None, _lib.ARES_ENODATA, _lib.ARES_EBADRESP):
148
+ result = None
149
+ break
150
+ if r is not None:
151
+ if isinstance(r, Iterable):
152
+ result.extend(r)
153
+ else:
154
+ result.append(r)
155
+ else:
156
+ status = None
157
+ else:
158
+ result, status = parse_result(query_type, abuf, alen)
159
+ else:
160
+ result = None
161
+
162
+ callback(result, status)
163
+ _handle_to_channel.pop(arg, None)
164
+
165
+ @_ffi.def_extern()
166
+ def _addrinfo_cb(arg, status, timeouts, res):
167
+ # Get callback data without removing the reference yet
168
+ if _ffi is None or arg not in _handle_to_channel:
169
+ return
170
+
171
+ callback = _ffi.from_handle(arg)
172
+
173
+ if status != _lib.ARES_SUCCESS:
174
+ result = None
175
+ else:
176
+ result = ares_addrinfo_result(res)
177
+ status = None
178
+
179
+ callback(result, status)
180
+ _handle_to_channel.pop(arg, None)
181
+
182
+ def parse_result(query_type, abuf, alen):
183
+ if query_type == _lib.T_A:
184
+ addrttls = _ffi.new("struct ares_addrttl[]", PYCARES_ADDRTTL_SIZE)
185
+ naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE)
186
+ parse_status = _lib.ares_parse_a_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls)
187
+ if parse_status != _lib.ARES_SUCCESS:
188
+ result = None
189
+ status = parse_status
190
+ else:
191
+ result = [ares_query_a_result(addrttls[i]) for i in range(naddrttls[0])]
192
+ status = None
193
+ elif query_type == _lib.T_AAAA:
194
+ addrttls = _ffi.new("struct ares_addr6ttl[]", PYCARES_ADDRTTL_SIZE)
195
+ naddrttls = _ffi.new("int*", PYCARES_ADDRTTL_SIZE)
196
+ parse_status = _lib.ares_parse_aaaa_reply(abuf, alen, _ffi.NULL, addrttls, naddrttls)
197
+ if parse_status != _lib.ARES_SUCCESS:
198
+ result = None
199
+ status = parse_status
200
+ else:
201
+ result = [ares_query_aaaa_result(addrttls[i]) for i in range(naddrttls[0])]
202
+ status = None
203
+ elif query_type == _lib.T_CAA:
204
+ caa_reply = _ffi.new("struct ares_caa_reply **")
205
+ parse_status = _lib.ares_parse_caa_reply(abuf, alen, caa_reply)
206
+ if parse_status != _lib.ARES_SUCCESS:
207
+ result = None
208
+ status = parse_status
209
+ else:
210
+ result = []
211
+ caa_reply_ptr = caa_reply[0]
212
+ while caa_reply_ptr != _ffi.NULL:
213
+ result.append(ares_query_caa_result(caa_reply_ptr))
214
+ caa_reply_ptr = caa_reply_ptr.next
215
+ _lib.ares_free_data(caa_reply[0])
216
+ status = None
217
+ elif query_type == _lib.T_CNAME:
218
+ host = _ffi.new("struct hostent **")
219
+ parse_status = _lib.ares_parse_a_reply(abuf, alen, host, _ffi.NULL, _ffi.NULL)
220
+ if parse_status != _lib.ARES_SUCCESS:
221
+ result = None
222
+ status = parse_status
223
+ else:
224
+ result = ares_query_cname_result(host[0])
225
+ _lib.ares_free_hostent(host[0])
226
+ status = None
227
+ elif query_type == _lib.T_MX:
228
+ mx_reply = _ffi.new("struct ares_mx_reply **")
229
+ parse_status = _lib.ares_parse_mx_reply(abuf, alen, mx_reply)
230
+ if parse_status != _lib.ARES_SUCCESS:
231
+ result = None
232
+ status = parse_status
233
+ else:
234
+ result = []
235
+ mx_reply_ptr = mx_reply[0]
236
+ while mx_reply_ptr != _ffi.NULL:
237
+ result.append(ares_query_mx_result(mx_reply_ptr))
238
+ mx_reply_ptr = mx_reply_ptr.next
239
+ _lib.ares_free_data(mx_reply[0])
240
+ status = None
241
+ elif query_type == _lib.T_NAPTR:
242
+ naptr_reply = _ffi.new("struct ares_naptr_reply **")
243
+ parse_status = _lib.ares_parse_naptr_reply(abuf, alen, naptr_reply)
244
+ if parse_status != _lib.ARES_SUCCESS:
245
+ result = None
246
+ status = parse_status
247
+ else:
248
+ result = []
249
+ naptr_reply_ptr = naptr_reply[0]
250
+ while naptr_reply_ptr != _ffi.NULL:
251
+ result.append(ares_query_naptr_result(naptr_reply_ptr))
252
+ naptr_reply_ptr = naptr_reply_ptr.next
253
+ _lib.ares_free_data(naptr_reply[0])
254
+ status = None
255
+ elif query_type == _lib.T_NS:
256
+ hostent = _ffi.new("struct hostent **")
257
+ parse_status = _lib.ares_parse_ns_reply(abuf, alen, hostent)
258
+ if parse_status != _lib.ARES_SUCCESS:
259
+ result = None
260
+ status = parse_status
261
+ else:
262
+ result = []
263
+ host = hostent[0]
264
+ i = 0
265
+ while host.h_aliases[i] != _ffi.NULL:
266
+ result.append(ares_query_ns_result(host.h_aliases[i]))
267
+ i += 1
268
+ _lib.ares_free_hostent(host)
269
+ status = None
270
+ elif query_type == _lib.T_PTR:
271
+ hostent = _ffi.new("struct hostent **")
272
+ parse_status = _lib.ares_parse_ptr_reply(abuf, alen, _ffi.NULL, 0, socket.AF_UNSPEC, hostent)
273
+ if parse_status != _lib.ARES_SUCCESS:
274
+ result = None
275
+ status = parse_status
276
+ else:
277
+ aliases = []
278
+ host = hostent[0]
279
+ i = 0
280
+ while host.h_aliases[i] != _ffi.NULL:
281
+ aliases.append(maybe_str(_ffi.string(host.h_aliases[i])))
282
+ i += 1
283
+ result = ares_query_ptr_result(host, aliases)
284
+ _lib.ares_free_hostent(host)
285
+ status = None
286
+ elif query_type == _lib.T_SOA:
287
+ soa_reply = _ffi.new("struct ares_soa_reply **")
288
+ parse_status = _lib.ares_parse_soa_reply(abuf, alen, soa_reply)
289
+ if parse_status != _lib.ARES_SUCCESS:
290
+ result = None
291
+ status = parse_status
292
+ else:
293
+ result = ares_query_soa_result(soa_reply[0])
294
+ _lib.ares_free_data(soa_reply[0])
295
+ status = None
296
+ elif query_type == _lib.T_SRV:
297
+ srv_reply = _ffi.new("struct ares_srv_reply **")
298
+ parse_status = _lib.ares_parse_srv_reply(abuf, alen, srv_reply)
299
+ if parse_status != _lib.ARES_SUCCESS:
300
+ result = None
301
+ status = parse_status
302
+ else:
303
+ result = []
304
+ srv_reply_ptr = srv_reply[0]
305
+ while srv_reply_ptr != _ffi.NULL:
306
+ result.append(ares_query_srv_result(srv_reply_ptr))
307
+ srv_reply_ptr = srv_reply_ptr.next
308
+ _lib.ares_free_data(srv_reply[0])
309
+ status = None
310
+ elif query_type == _lib.T_TXT:
311
+ txt_reply = _ffi.new("struct ares_txt_ext **")
312
+ parse_status = _lib.ares_parse_txt_reply_ext(abuf, alen, txt_reply)
313
+ if parse_status != _lib.ARES_SUCCESS:
314
+ result = None
315
+ status = parse_status
316
+ else:
317
+ result = []
318
+ txt_reply_ptr = txt_reply[0]
319
+ tmp_obj = None
320
+ while True:
321
+ if txt_reply_ptr == _ffi.NULL:
322
+ if tmp_obj is not None:
323
+ result.append(ares_query_txt_result(tmp_obj))
324
+ break
325
+ if txt_reply_ptr.record_start == 1:
326
+ if tmp_obj is not None:
327
+ result.append(ares_query_txt_result(tmp_obj))
328
+ tmp_obj = ares_query_txt_result_chunk(txt_reply_ptr)
329
+ else:
330
+ new_chunk = ares_query_txt_result_chunk(txt_reply_ptr)
331
+ tmp_obj.text += new_chunk.text
332
+ txt_reply_ptr = txt_reply_ptr.next
333
+ _lib.ares_free_data(txt_reply[0])
334
+ status = None
335
+ else:
336
+ raise ValueError("invalid query type specified")
337
+
338
+ return result, status
339
+
340
+
341
+ class _ChannelShutdownManager:
342
+ """Manages channel destruction in a single background thread using SimpleQueue."""
343
+
344
+ def __init__(self) -> None:
345
+ self._queue: SimpleQueue = SimpleQueue()
346
+ self._thread: Optional[threading.Thread] = None
347
+ self._thread_started = False
348
+
349
+ def _run_safe_shutdown_loop(self) -> None:
350
+ """Process channel destruction requests from the queue."""
351
+ while True:
352
+ # Block forever until we get a channel to destroy
353
+ channel = self._queue.get()
354
+
355
+ # Sleep for 1 second to ensure c-ares has finished processing
356
+ # Its important that c-ares is past this critcial section
357
+ # so we use a delay to ensure it has time to finish processing
358
+ # https://github.com/c-ares/c-ares/blob/4f42928848e8b73d322b15ecbe3e8d753bf8734e/src/lib/ares_process.c#L1422
359
+ time.sleep(1.0)
360
+
361
+ # Destroy the channel
362
+ if _lib is not None and channel is not None:
363
+ _lib.ares_destroy(channel[0])
364
+
365
+ def destroy_channel(self, channel) -> None:
366
+ """
367
+ Schedule channel destruction on the background thread with a safety delay.
368
+
369
+ Thread Safety and Synchronization:
370
+ This method uses SimpleQueue which is thread-safe for putting items
371
+ from multiple threads. The background thread processes channels
372
+ sequentially with a 1-second delay before each destruction.
373
+ """
374
+ # Put the channel in the queue
375
+ self._queue.put(channel)
376
+
377
+ # Start the background thread if not already started
378
+ if not self._thread_started:
379
+ self._thread_started = True
380
+ self._thread = threading.Thread(target=self._run_safe_shutdown_loop, daemon=True)
381
+ self._thread.start()
382
+
383
+
384
+ # Global shutdown manager instance
385
+ _shutdown_manager = _ChannelShutdownManager()
386
+
387
+
388
+ class Channel:
389
+ __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)
390
+ __qclasses__ = (_lib.C_IN, _lib.C_CHAOS, _lib.C_HS, _lib.C_NONE, _lib.C_ANY)
391
+
392
+ def __init__(self,
393
+ flags: Optional[int] = None,
394
+ timeout: Optional[float] = None,
395
+ tries: Optional[int] = None,
396
+ ndots: Optional[int] = None,
397
+ tcp_port: Optional[int] = None,
398
+ udp_port: Optional[int] = None,
399
+ servers: Optional[Iterable[Union[str, bytes]]] = None,
400
+ domains: Optional[Iterable[Union[str, bytes]]] = None,
401
+ lookups: Union[str, bytes, None] = None,
402
+ sock_state_cb: Optional[Callable[[int, bool, bool], None]] = None,
403
+ socket_send_buffer_size: Optional[int] = None,
404
+ socket_receive_buffer_size: Optional[int] = None,
405
+ rotate: bool = False,
406
+ local_ip: Union[str, bytes, None] = None,
407
+ local_dev: Optional[str] = None,
408
+ resolvconf_path: Union[str, bytes, None] = None,
409
+ event_thread: bool = False) -> None:
410
+
411
+ # Initialize _channel to None first to ensure __del__ doesn't fail
412
+ self._channel = None
413
+
414
+ channel = _ffi.new("ares_channel *")
415
+ options = _ffi.new("struct ares_options *")
416
+ optmask = 0
417
+
418
+ if flags is not None:
419
+ options.flags = flags
420
+ optmask = optmask | _lib.ARES_OPT_FLAGS
421
+
422
+ if timeout is not None:
423
+ options.timeout = int(timeout * 1000)
424
+ optmask = optmask | _lib.ARES_OPT_TIMEOUTMS
425
+
426
+ if tries is not None:
427
+ options.tries = tries
428
+ optmask = optmask | _lib.ARES_OPT_TRIES
429
+
430
+ if ndots is not None:
431
+ options.ndots = ndots
432
+ optmask = optmask | _lib.ARES_OPT_NDOTS
433
+
434
+ if tcp_port is not None:
435
+ options.tcp_port = tcp_port
436
+ optmask = optmask | _lib.ARES_OPT_TCP_PORT
437
+
438
+ if udp_port is not None:
439
+ options.udp_port = udp_port
440
+ optmask = optmask | _lib.ARES_OPT_UDP_PORT
441
+
442
+ if socket_send_buffer_size is not None:
443
+ options.socket_send_buffer_size = socket_send_buffer_size
444
+ optmask = optmask | _lib.ARES_OPT_SOCK_SNDBUF
445
+
446
+ if socket_receive_buffer_size is not None:
447
+ options.socket_receive_buffer_size = socket_receive_buffer_size
448
+ optmask = optmask | _lib.ARES_OPT_SOCK_RCVBUF
449
+
450
+ if sock_state_cb:
451
+ if not callable(sock_state_cb):
452
+ raise TypeError("sock_state_cb is not callable")
453
+ if event_thread:
454
+ raise RuntimeError("sock_state_cb and event_thread cannot be used together")
455
+
456
+ userdata = _ffi.new_handle(sock_state_cb)
457
+
458
+ # This must be kept alive while the channel is alive.
459
+ self._sock_state_cb_handle = userdata
460
+
461
+ options.sock_state_cb = _lib._sock_state_cb
462
+ options.sock_state_cb_data = userdata
463
+ optmask = optmask | _lib.ARES_OPT_SOCK_STATE_CB
464
+
465
+ if event_thread:
466
+ if not ares_threadsafety():
467
+ raise RuntimeError("c-ares is not built with thread safety")
468
+ if sock_state_cb:
469
+ raise RuntimeError("sock_state_cb and event_thread cannot be used together")
470
+ optmask = optmask | _lib.ARES_OPT_EVENT_THREAD
471
+ options.evsys = _lib.ARES_EVSYS_DEFAULT
472
+
473
+ if lookups:
474
+ options.lookups = _ffi.new('char[]', ascii_bytes(lookups))
475
+ optmask = optmask | _lib.ARES_OPT_LOOKUPS
476
+
477
+ if domains:
478
+ strs = [_ffi.new("char[]", ascii_bytes(i)) for i in domains]
479
+ c = _ffi.new("char *[%d]" % (len(domains) + 1))
480
+ for i in range(len(domains)):
481
+ c[i] = strs[i]
482
+
483
+ options.domains = c
484
+ options.ndomains = len(domains)
485
+ optmask = optmask | _lib.ARES_OPT_DOMAINS
486
+
487
+ if rotate:
488
+ optmask = optmask | _lib.ARES_OPT_ROTATE
489
+
490
+ if resolvconf_path is not None:
491
+ optmask = optmask | _lib.ARES_OPT_RESOLVCONF
492
+ options.resolvconf_path = _ffi.new('char[]', ascii_bytes(resolvconf_path))
493
+
494
+ r = _lib.ares_init_options(channel, options, optmask)
495
+ if r != _lib.ARES_SUCCESS:
496
+ raise AresError('Failed to initialize c-ares channel')
497
+
498
+ # Initialize all attributes for consistency
499
+ self._event_thread = event_thread
500
+ self._channel = channel
501
+ if servers:
502
+ self.servers = servers
503
+
504
+ if local_ip:
505
+ self.set_local_ip(local_ip)
506
+
507
+ if local_dev:
508
+ self.set_local_dev(local_dev)
509
+
510
+ def __enter__(self):
511
+ """Enter the context manager."""
512
+ return self
513
+
514
+ def __exit__(self, exc_type, exc_val, exc_tb):
515
+ """Exit the context manager and close the channel."""
516
+ self.close()
517
+ return False
518
+
519
+ def __del__(self) -> None:
520
+ """Ensure the channel is destroyed when the object is deleted."""
521
+ if self._channel is not None:
522
+ # Schedule channel destruction using the global shutdown manager
523
+ self._schedule_destruction()
524
+
525
+ def _create_callback_handle(self, callback_data):
526
+ """
527
+ Create a callback handle and register it for tracking.
528
+
529
+ This ensures that:
530
+ 1. The callback data is wrapped in a CFFI handle
531
+ 2. The handle is mapped to this channel to keep it alive
532
+
533
+ Args:
534
+ callback_data: The data to pass to the callback (usually a callable or tuple)
535
+
536
+ Returns:
537
+ The CFFI handle that can be passed to C functions
538
+
539
+ Raises:
540
+ RuntimeError: If the channel is destroyed
541
+
542
+ """
543
+ if self._channel is None:
544
+ raise RuntimeError("Channel is destroyed, no new queries allowed")
545
+
546
+ userdata = _ffi.new_handle(callback_data)
547
+ _handle_to_channel[userdata] = self
548
+ return userdata
549
+
550
+ def cancel(self) -> None:
551
+ _lib.ares_cancel(self._channel[0])
552
+
553
+ def reinit(self) -> None:
554
+ r = _lib.ares_reinit(self._channel[0])
555
+ if r != _lib.ARES_SUCCESS:
556
+ raise AresError(r, errno.strerror(r))
557
+
558
+ @property
559
+ def servers(self) -> list[str]:
560
+ servers = _ffi.new("struct ares_addr_node **")
561
+
562
+ r = _lib.ares_get_servers(self._channel[0], servers)
563
+ if r != _lib.ARES_SUCCESS:
564
+ raise AresError(r, errno.strerror(r))
565
+
566
+ server_list = []
567
+ server = _ffi.new("struct ares_addr_node **", servers[0])
568
+ while True:
569
+ if server == _ffi.NULL:
570
+ break
571
+
572
+ ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN)
573
+ s = server[0]
574
+ if _ffi.NULL != _lib.ares_inet_ntop(s.family, _ffi.addressof(s.addr), ip, _lib.INET6_ADDRSTRLEN):
575
+ server_list.append(maybe_str(_ffi.string(ip, _lib.INET6_ADDRSTRLEN)))
576
+
577
+ server = s.next
578
+
579
+ return server_list
580
+
581
+ @servers.setter
582
+ def servers(self, servers: Iterable[Union[str, bytes]]) -> None:
583
+ c = _ffi.new("struct ares_addr_node[%d]" % len(servers))
584
+ for i, server in enumerate(servers):
585
+ if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(server), _ffi.addressof(c[i].addr.addr4)) == 1:
586
+ c[i].family = socket.AF_INET
587
+ elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(server), _ffi.addressof(c[i].addr.addr6)) == 1:
588
+ c[i].family = socket.AF_INET6
589
+ else:
590
+ raise ValueError("invalid IP address")
591
+
592
+ if i > 0:
593
+ c[i - 1].next = _ffi.addressof(c[i])
594
+
595
+ r = _lib.ares_set_servers(self._channel[0], c)
596
+ if r != _lib.ARES_SUCCESS:
597
+ raise AresError(r, errno.strerror(r))
598
+
599
+ def getsock(self):
600
+ rfds = []
601
+ wfds = []
602
+ socks = _ffi.new("ares_socket_t [%d]" % _lib.ARES_GETSOCK_MAXNUM)
603
+ bitmask = _lib.ares_getsock(self._channel[0], socks, _lib.ARES_GETSOCK_MAXNUM)
604
+ for i in range(_lib.ARES_GETSOCK_MAXNUM):
605
+ if _lib.ARES_GETSOCK_READABLE(bitmask, i):
606
+ rfds.append(socks[i])
607
+ if _lib.ARES_GETSOCK_WRITABLE(bitmask, i):
608
+ wfds.append(socks[i])
609
+
610
+ return rfds, wfds
611
+
612
+ def process_fd(self, read_fd: int, write_fd: int) -> None:
613
+ _lib.ares_process_fd(self._channel[0], _ffi.cast("ares_socket_t", read_fd), _ffi.cast("ares_socket_t", write_fd))
614
+
615
+ def timeout(self, t = None):
616
+ maxtv = _ffi.NULL
617
+ tv = _ffi.new("struct timeval*")
618
+
619
+ if t is not None:
620
+ if t >= 0.0:
621
+ maxtv = _ffi.new("struct timeval*")
622
+ maxtv.tv_sec = int(math.floor(t))
623
+ maxtv.tv_usec = int(math.fmod(t, 1.0) * 1000000)
624
+ else:
625
+ raise ValueError("timeout needs to be a positive number or None")
626
+
627
+ _lib.ares_timeout(self._channel[0], maxtv, tv)
628
+
629
+ if tv == _ffi.NULL:
630
+ return 0.0
631
+
632
+ return (tv.tv_sec + tv.tv_usec / 1000000.0)
633
+
634
+ def gethostbyaddr(self, addr: str, callback: Callable[[Any, int], None]) -> None:
635
+ if not callable(callback):
636
+ raise TypeError("a callable is required")
637
+
638
+ addr4 = _ffi.new("struct in_addr*")
639
+ addr6 = _ffi.new("struct ares_in6_addr*")
640
+ if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(addr), (addr4)) == 1:
641
+ address = addr4
642
+ family = socket.AF_INET
643
+ elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(addr), (addr6)) == 1:
644
+ address = addr6
645
+ family = socket.AF_INET6
646
+ else:
647
+ raise ValueError("invalid IP address")
648
+
649
+ userdata = self._create_callback_handle(callback)
650
+ _lib.ares_gethostbyaddr(self._channel[0], address, _ffi.sizeof(address[0]), family, _lib._host_cb, userdata)
651
+
652
+ def gethostbyname(self, name: str, family: socket.AddressFamily, callback: Callable[[Any, int], None]) -> None:
653
+ if not callable(callback):
654
+ raise TypeError("a callable is required")
655
+
656
+ userdata = self._create_callback_handle(callback)
657
+ _lib.ares_gethostbyname(self._channel[0], parse_name(name), family, _lib._host_cb, userdata)
658
+
659
+ def getaddrinfo(
660
+ self,
661
+ host: str,
662
+ port: Optional[int],
663
+ callback: Callable[[Any, int], None],
664
+ family: socket.AddressFamily = 0,
665
+ type: int = 0,
666
+ proto: int = 0,
667
+ flags: int = 0
668
+ ) -> None:
669
+ if not callable(callback):
670
+ raise TypeError("a callable is required")
671
+
672
+ if port is None:
673
+ service = _ffi.NULL
674
+ elif isinstance(port, int):
675
+ service = str(port).encode('ascii')
676
+ else:
677
+ service = ascii_bytes(port)
678
+
679
+ userdata = self._create_callback_handle(callback)
680
+
681
+ hints = _ffi.new('struct ares_addrinfo_hints*')
682
+ hints.ai_flags = flags
683
+ hints.ai_family = family
684
+ hints.ai_socktype = type
685
+ hints.ai_protocol = proto
686
+ _lib.ares_getaddrinfo(self._channel[0], parse_name(host), service, hints, _lib._addrinfo_cb, userdata)
687
+
688
+ def query(self, name: str, query_type: str, callback: Callable[[Any, int], None], query_class: Optional[str] = None) -> None:
689
+ self._do_query(_lib.ares_query, name, query_type, callback, query_class=query_class)
690
+
691
+ def search(self, name, query_type, callback, query_class=None):
692
+ self._do_query(_lib.ares_search, name, query_type, callback, query_class=query_class)
693
+
694
+ def _do_query(self, func, name, query_type, callback, query_class=None):
695
+ if not callable(callback):
696
+ raise TypeError('a callable is required')
697
+
698
+ if query_type not in self.__qtypes__:
699
+ raise ValueError('invalid query type specified')
700
+
701
+ if query_class is None:
702
+ query_class = _lib.C_IN
703
+
704
+ if query_class not in self.__qclasses__:
705
+ raise ValueError('invalid query class specified')
706
+
707
+ userdata = self._create_callback_handle((callback, query_type))
708
+ func(self._channel[0], parse_name(name), query_class, query_type, _lib._query_cb, userdata)
709
+
710
+ def set_local_ip(self, ip):
711
+ addr4 = _ffi.new("struct in_addr*")
712
+ addr6 = _ffi.new("struct ares_in6_addr*")
713
+ if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), addr4) == 1:
714
+ _lib.ares_set_local_ip4(self._channel[0], socket.ntohl(addr4.s_addr))
715
+ elif _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), addr6) == 1:
716
+ _lib.ares_set_local_ip6(self._channel[0], addr6)
717
+ else:
718
+ raise ValueError("invalid IP address")
719
+
720
+ def getnameinfo(self, address: Union[IP4, IP6], flags: int, callback: Callable[[Any, int], None]) -> None:
721
+ if not callable(callback):
722
+ raise TypeError("a callable is required")
723
+
724
+ if len(address) == 2:
725
+ (ip, port) = address
726
+ sa4 = _ffi.new("struct sockaddr_in*")
727
+ if _lib.ares_inet_pton(socket.AF_INET, ascii_bytes(ip), _ffi.addressof(sa4.sin_addr)) != 1:
728
+ raise ValueError("Invalid IPv4 address %r" % ip)
729
+ sa4.sin_family = socket.AF_INET
730
+ sa4.sin_port = socket.htons(port)
731
+ sa = sa4
732
+ elif len(address) == 4:
733
+ (ip, port, flowinfo, scope_id) = address
734
+ sa6 = _ffi.new("struct sockaddr_in6*")
735
+ if _lib.ares_inet_pton(socket.AF_INET6, ascii_bytes(ip), _ffi.addressof(sa6.sin6_addr)) != 1:
736
+ raise ValueError("Invalid IPv6 address %r" % ip)
737
+ sa6.sin6_family = socket.AF_INET6
738
+ sa6.sin6_port = socket.htons(port)
739
+ sa6.sin6_flowinfo = socket.htonl(flowinfo) # I'm unsure about byteorder here.
740
+ sa6.sin6_scope_id = scope_id # Yes, without htonl.
741
+ sa = sa6
742
+ else:
743
+ raise ValueError("Invalid address argument")
744
+
745
+ userdata = self._create_callback_handle(callback)
746
+ _lib.ares_getnameinfo(self._channel[0], _ffi.cast("struct sockaddr*", sa), _ffi.sizeof(sa[0]), flags, _lib._nameinfo_cb, userdata)
747
+
748
+ def set_local_dev(self, dev):
749
+ _lib.ares_set_local_dev(self._channel[0], dev)
750
+
751
+ def close(self) -> None:
752
+ """
753
+ Close the channel as soon as it's safe to do so.
754
+
755
+ This method can be called from any thread. The channel will be destroyed
756
+ safely using a background thread with a 1-second delay to ensure c-ares
757
+ has completed its cleanup.
758
+
759
+ Note: Once close() is called, no new queries can be started. Any pending
760
+ queries will be cancelled and their callbacks will receive ARES_ECANCELLED.
761
+
762
+ """
763
+ if self._channel is None:
764
+ # Already destroyed
765
+ return
766
+
767
+ # Cancel all pending queries - this will trigger callbacks with ARES_ECANCELLED
768
+ self.cancel()
769
+
770
+ # Schedule channel destruction
771
+ self._schedule_destruction()
772
+
773
+ def _schedule_destruction(self) -> None:
774
+ """Schedule channel destruction using the global shutdown manager."""
775
+ if self._channel is None:
776
+ return
777
+ channel = self._channel
778
+ self._channel = None
779
+ # Can't start threads during interpreter shutdown
780
+ # The channel will be cleaned up by the OS
781
+ # TODO: Change to PythonFinalizationError when Python 3.12 support is dropped
782
+ with suppress(RuntimeError):
783
+ _shutdown_manager.destroy_channel(channel)
784
+
785
+
786
+
787
+ class AresResult:
788
+ __slots__ = ()
789
+
790
+ def __repr__(self):
791
+ attrs = ['%s=%s' % (a, getattr(self, a)) for a in self.__slots__]
792
+ return '<%s> %s' % (self.__class__.__name__, ', '.join(attrs))
793
+
794
+
795
+ # DNS query result types
796
+ #
797
+
798
+ class ares_query_a_result(AresResult):
799
+ __slots__ = ('host', 'ttl')
800
+ type = 'A'
801
+
802
+ def __init__(self, ares_addrttl):
803
+ buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
804
+ _lib.ares_inet_ntop(socket.AF_INET, _ffi.addressof(ares_addrttl.ipaddr), buf, _lib.INET6_ADDRSTRLEN)
805
+ self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN))
806
+ self.ttl = ares_addrttl.ttl
807
+
808
+
809
+ class ares_query_aaaa_result(AresResult):
810
+ __slots__ = ('host', 'ttl')
811
+ type = 'AAAA'
812
+
813
+ def __init__(self, ares_addrttl):
814
+ buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
815
+ _lib.ares_inet_ntop(socket.AF_INET6, _ffi.addressof(ares_addrttl.ip6addr), buf, _lib.INET6_ADDRSTRLEN)
816
+ self.host = maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN))
817
+ self.ttl = ares_addrttl.ttl
818
+
819
+
820
+ class ares_query_caa_result(AresResult):
821
+ __slots__ = ('critical', 'property', 'value', 'ttl')
822
+ type = 'CAA'
823
+
824
+ def __init__(self, caa):
825
+ self.critical = caa.critical
826
+ self.property = maybe_str(_ffi.string(caa.property, caa.plength))
827
+ self.value = maybe_str(_ffi.string(caa.value, caa.length))
828
+ self.ttl = -1
829
+
830
+
831
+ class ares_query_cname_result(AresResult):
832
+ __slots__ = ('cname', 'ttl')
833
+ type = 'CNAME'
834
+
835
+ def __init__(self, host):
836
+ self.cname = maybe_str(_ffi.string(host.h_name))
837
+ self.ttl = -1
838
+
839
+
840
+ class ares_query_mx_result(AresResult):
841
+ __slots__ = ('host', 'priority', 'ttl')
842
+ type = 'MX'
843
+
844
+ def __init__(self, mx):
845
+ self.host = maybe_str(_ffi.string(mx.host))
846
+ self.priority = mx.priority
847
+ self.ttl = -1
848
+
849
+
850
+ class ares_query_naptr_result(AresResult):
851
+ __slots__ = ('order', 'preference', 'flags', 'service', 'regex', 'replacement', 'ttl')
852
+ type = 'NAPTR'
853
+
854
+ def __init__(self, naptr):
855
+ self.order = naptr.order
856
+ self.preference = naptr.preference
857
+ self.flags = maybe_str(_ffi.string(naptr.flags))
858
+ self.service = maybe_str(_ffi.string(naptr.service))
859
+ self.regex = maybe_str(_ffi.string(naptr.regexp))
860
+ self.replacement = maybe_str(_ffi.string(naptr.replacement))
861
+ self.ttl = -1
862
+
863
+
864
+ class ares_query_ns_result(AresResult):
865
+ __slots__ = ('host', 'ttl')
866
+ type = 'NS'
867
+
868
+ def __init__(self, ns):
869
+ self.host = maybe_str(_ffi.string(ns))
870
+ self.ttl = -1
871
+
872
+
873
+ class ares_query_ptr_result(AresResult):
874
+ __slots__ = ('name', 'ttl', 'aliases')
875
+ type = 'PTR'
876
+
877
+ def __init__(self, hostent, aliases):
878
+ self.name = maybe_str(_ffi.string(hostent.h_name))
879
+ self.aliases = aliases
880
+ self.ttl = -1
881
+
882
+
883
+ class ares_query_soa_result(AresResult):
884
+ __slots__ = ('nsname', 'hostmaster', 'serial', 'refresh', 'retry', 'expires', 'minttl', 'ttl')
885
+ type = 'SOA'
886
+
887
+ def __init__(self, soa):
888
+ self.nsname = maybe_str(_ffi.string(soa.nsname))
889
+ self.hostmaster = maybe_str(_ffi.string(soa.hostmaster))
890
+ self.serial = soa.serial
891
+ self.refresh = soa.refresh
892
+ self.retry = soa.retry
893
+ self.expires = soa.expire
894
+ self.minttl = soa.minttl
895
+ self.ttl = -1
896
+
897
+
898
+ class ares_query_srv_result(AresResult):
899
+ __slots__ = ('host', 'port', 'priority', 'weight', 'ttl')
900
+ type = 'SRV'
901
+
902
+ def __init__(self, srv):
903
+ self.host = maybe_str(_ffi.string(srv.host))
904
+ self.port = srv.port
905
+ self.priority = srv.priority
906
+ self.weight = srv.weight
907
+ self.ttl = -1
908
+
909
+
910
+ class ares_query_txt_result(AresResult):
911
+ __slots__ = ('text', 'ttl')
912
+ type = 'TXT'
913
+
914
+ def __init__(self, txt_chunk):
915
+ self.text = maybe_str(txt_chunk.text)
916
+ self.ttl = -1
917
+
918
+
919
+ class ares_query_txt_result_chunk(AresResult):
920
+ __slots__ = ('text', 'ttl')
921
+ type = 'TXT'
922
+
923
+ def __init__(self, txt):
924
+ self.text = _ffi.string(txt.txt)
925
+ self.ttl = -1
926
+
927
+
928
+ # Other result types
929
+ #
930
+
931
+ class ares_host_result(AresResult):
932
+ __slots__ = ('name', 'aliases', 'addresses')
933
+
934
+ def __init__(self, hostent):
935
+ self.name = maybe_str(_ffi.string(hostent.h_name))
936
+ self.aliases = []
937
+ self.addresses = []
938
+ i = 0
939
+ while hostent.h_aliases[i] != _ffi.NULL:
940
+ self.aliases.append(maybe_str(_ffi.string(hostent.h_aliases[i])))
941
+ i += 1
942
+
943
+ i = 0
944
+ while hostent.h_addr_list[i] != _ffi.NULL:
945
+ buf = _ffi.new("char[]", _lib.INET6_ADDRSTRLEN)
946
+ if _ffi.NULL != _lib.ares_inet_ntop(hostent.h_addrtype, hostent.h_addr_list[i], buf, _lib.INET6_ADDRSTRLEN):
947
+ self.addresses.append(maybe_str(_ffi.string(buf, _lib.INET6_ADDRSTRLEN)))
948
+ i += 1
949
+
950
+
951
+ class ares_nameinfo_result(AresResult):
952
+ __slots__ = ('node', 'service')
953
+
954
+ def __init__(self, node, service):
955
+ self.node = maybe_str(_ffi.string(node))
956
+ self.service = maybe_str(_ffi.string(service)) if service != _ffi.NULL else None
957
+
958
+
959
+ class ares_addrinfo_node_result(AresResult):
960
+ __slots__ = ('ttl', 'flags', 'family', 'socktype', 'protocol', 'addr')
961
+
962
+ def __init__(self, ares_node):
963
+ self.ttl = ares_node.ai_ttl
964
+ self.flags = ares_node.ai_flags
965
+ self.socktype = ares_node.ai_socktype
966
+ self.protocol = ares_node.ai_protocol
967
+
968
+ addr = ares_node.ai_addr
969
+ assert addr.sa_family == ares_node.ai_family
970
+ ip = _ffi.new("char []", _lib.INET6_ADDRSTRLEN)
971
+ if addr.sa_family == socket.AF_INET:
972
+ self.family = socket.AF_INET
973
+ s = _ffi.cast("struct sockaddr_in*", addr)
974
+ if _ffi.NULL != _lib.ares_inet_ntop(s.sin_family, _ffi.addressof(s.sin_addr), ip, _lib.INET6_ADDRSTRLEN):
975
+ # (address, port) 2-tuple for AF_INET
976
+ self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin_port))
977
+ elif addr.sa_family == socket.AF_INET6:
978
+ self.family = socket.AF_INET6
979
+ s = _ffi.cast("struct sockaddr_in6*", addr)
980
+ if _ffi.NULL != _lib.ares_inet_ntop(s.sin6_family, _ffi.addressof(s.sin6_addr), ip, _lib.INET6_ADDRSTRLEN):
981
+ # (address, port, flow info, scope id) 4-tuple for AF_INET6
982
+ self.addr = (_ffi.string(ip, _lib.INET6_ADDRSTRLEN), socket.ntohs(s.sin6_port), s.sin6_flowinfo, s.sin6_scope_id)
983
+ else:
984
+ raise ValueError("invalid sockaddr family")
985
+
986
+
987
+ class ares_addrinfo_cname_result(AresResult):
988
+ __slots__ = ('ttl', 'alias', 'name')
989
+
990
+ def __init__(self, ares_cname):
991
+ self.ttl = ares_cname.ttl
992
+ self.alias = maybe_str(_ffi.string(ares_cname.alias))
993
+ self.name = maybe_str(_ffi.string(ares_cname.name))
994
+
995
+
996
+ class ares_addrinfo_result(AresResult):
997
+ __slots__ = ('cnames', 'nodes')
998
+
999
+ def __init__(self, ares_addrinfo):
1000
+ self.cnames = []
1001
+ self.nodes = []
1002
+ cname_ptr = ares_addrinfo.cnames
1003
+ while cname_ptr != _ffi.NULL:
1004
+ self.cnames.append(ares_addrinfo_cname_result(cname_ptr))
1005
+ cname_ptr = cname_ptr.next
1006
+ node_ptr = ares_addrinfo.nodes
1007
+ while node_ptr != _ffi.NULL:
1008
+ self.nodes.append(ares_addrinfo_node_result(node_ptr))
1009
+ node_ptr = node_ptr.ai_next
1010
+ _lib.ares_freeaddrinfo(ares_addrinfo)
1011
+
1012
+
1013
+ def ares_threadsafety() -> bool:
1014
+ """
1015
+ Check if c-ares was compiled with thread safety support.
1016
+
1017
+ :return: True if thread-safe, False otherwise.
1018
+ :rtype: bool
1019
+ """
1020
+ return bool(_lib.ares_threadsafety())
1021
+
1022
+ __all__ = (
1023
+ "ARES_FLAG_USEVC",
1024
+ "ARES_FLAG_PRIMARY",
1025
+ "ARES_FLAG_IGNTC",
1026
+ "ARES_FLAG_NORECURSE",
1027
+ "ARES_FLAG_STAYOPEN",
1028
+ "ARES_FLAG_NOSEARCH",
1029
+ "ARES_FLAG_NOALIASES",
1030
+ "ARES_FLAG_NOCHECKRESP",
1031
+ "ARES_FLAG_EDNS",
1032
+ "ARES_FLAG_NO_DFLT_SVR",
1033
+
1034
+ # Nameinfo flag values
1035
+ "ARES_NI_NOFQDN",
1036
+ "ARES_NI_NUMERICHOST",
1037
+ "ARES_NI_NAMEREQD",
1038
+ "ARES_NI_NUMERICSERV",
1039
+ "ARES_NI_DGRAM",
1040
+ "ARES_NI_TCP",
1041
+ "ARES_NI_UDP",
1042
+ "ARES_NI_SCTP",
1043
+ "ARES_NI_DCCP",
1044
+ "ARES_NI_NUMERICSCOPE",
1045
+ "ARES_NI_LOOKUPHOST",
1046
+ "ARES_NI_LOOKUPSERVICE",
1047
+ "ARES_NI_IDN",
1048
+ "ARES_NI_IDN_ALLOW_UNASSIGNED",
1049
+ "ARES_NI_IDN_USE_STD3_ASCII_RULES",
1050
+
1051
+ # Bad socket
1052
+ "ARES_SOCKET_BAD",
1053
+
1054
+
1055
+ # Query types
1056
+ "QUERY_TYPE_A",
1057
+ "QUERY_TYPE_AAAA",
1058
+ "QUERY_TYPE_ANY",
1059
+ "QUERY_TYPE_CAA",
1060
+ "QUERY_TYPE_CNAME",
1061
+ "QUERY_TYPE_MX",
1062
+ "QUERY_TYPE_NAPTR",
1063
+ "QUERY_TYPE_NS",
1064
+ "QUERY_TYPE_PTR",
1065
+ "QUERY_TYPE_SOA",
1066
+ "QUERY_TYPE_SRV",
1067
+ "QUERY_TYPE_TXT",
1068
+
1069
+ # Query classes
1070
+ "QUERY_CLASS_IN",
1071
+ "QUERY_CLASS_CHAOS",
1072
+ "QUERY_CLASS_HS",
1073
+ "QUERY_CLASS_NONE",
1074
+ "QUERY_CLASS_ANY",
1075
+
1076
+
1077
+ "ARES_VERSION",
1078
+ "AresError",
1079
+ "Channel",
1080
+ "ares_threadsafety",
1081
+ "errno",
1082
+ "__version__"
1083
+ )