cyares 0.1.0__tar.gz

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.
cyares-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vizonex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cyares-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: cyares
3
+ Version: 0.1.0
4
+ Summary: A Cython Version of c-ares
5
+ Author-email: Vizonex <VizonexBusiness@gmail.com>
6
+ Project-URL: repository, https://github.com/Vizonex/cyares.git
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: typing-extensions; python_version < "3.11"
11
+ Dynamic: license-file
12
+
13
+ ![cy-ares logo](Cy-Ares-Logo.png)
14
+
15
+ An Upgraded version of __pycares__ with faster and safer features.
16
+
17
+
18
+
19
+ # Small Examples of How to Use
20
+
21
+ ```python
22
+ from cyares import Channel
23
+
24
+ # NOTE: Dns Queries contain a Future object
25
+ # This is a little bit different from pycares
26
+ # Handles were used to prevent new or unwanted vulnerabilities along
27
+ # with easier control given to the user...
28
+ # The Future object comes from the python concurrent.futures standard library
29
+
30
+ def main():
31
+ with Channel(servers=["8.8.8.8", "8.8.4.4"], event_thread=True) as channel:
32
+ data = channel.query("google.com", "A").result()
33
+ print(data)
34
+
35
+ if __name__ == "__main__":
36
+ main()
37
+ ```
38
+
39
+ ## In Asyncio
40
+ There's a module for an aiodns version if aiodns were using cyares.
41
+
42
+ ```python
43
+ import winloop # or uvloop (asyncio will also work)
44
+ # I'll add trio support in a future update as well...
45
+ from cyares.aio import DNSResolver
46
+
47
+ async def test():
48
+ async with DNSResolver(["8.8.8.8", "8.8.4.4"]) as dns:
49
+ data = await dns.query("google.com", "A")
50
+ print(data)
51
+
52
+ if __name__ == "__main__":
53
+ winloop.run(test())
54
+ ```
55
+
56
+
57
+
58
+ ## Story
59
+ As Someone who as recently started to contribute to projects such as __aiohttp__ and a few of it's smaller libraries. The asynchronous dns resolver __aiodns__ that __aiohttp__ can optionally use, felt like the oddest one in the group. With __aiodns__ using __pycares__ under the hood I wanted to learn how it worked to see if I could use my skills to optimize it like I had previously done with __propcache__ and __multidict__ as well but I soon came to learn of it's many problems and when __pycares__ started to hang on me with lingering threads when it ran, something didn't feel right to me and it lead me down a very large rabbit-hole waiting to be re-explored.
60
+
61
+ __Pycares__ was quick, dirty and fast but as the years of it's existance went by something wasn't exactly right with it and it could use safer features, better optimization and safety practices. While __cffi__ might be both quick and easy to use I didn't see the benefit of using it. The many vulnerabilities it was getting told me something needed to change if not big. With __Pycares__ reaching version __5.0.0__ I wanted to celebrate it the same way node-js celebrated the 10 year annversery of the original __http-parser__. Write a new one.
62
+
63
+
64
+ ### The Rewrite
65
+
66
+ While contributing to the aio-libs repos whenever I had a gap of free-time on hand while waiting on a question to be answered and when my other python libraries felt stable enough such as (deprecated-params, aiocallback, aiothreading & winloop) this is where I spent my time on. It had a total of 3 re-writes until I was satisfied with the result. Turns out just doing everything in small chunks makes all the difference.
67
+
68
+ It only felt right to me that migrating the library from __cffi__ to __cython__ would be the best solution to the problem. Not __Rust__ or __C__, just __pure-cython__ and small amounts of __C__ whenever nessesary and re-changing many of the internals to incorperate a safer approch. The reason for not picking __Rust__ is that there wasn't nessesarly a need to be memory-safe. What I cared about was speed, when people are doing DNS Lookups, speed matters and __Rust__ did not have a friendly enough archetecture that would set a future or allow me to access the parent from the child.
69
+
70
+ There was no better canadate than to move to using handles the same way __uvloop__ & __winloop__ do it and having authored one of them I was familliar with this concept.
71
+
72
+ The idea was not to reinvent the wheel rather move parts of the library over in chunks and look at __pycares__ for clues on how certain things should be laid out. At the end of the day, __Pycares__ was the blueprint and using __concurrent.futures__ & __Cython__ was the cure. __Py_buffer__ techniques brought over from __msgspec__ were utilized incase users were planning to use any bytes or string objects of any sort and I made sure the license was on it incase users wanted to know where it came from.
73
+
74
+ If there was a deprecated function with an alternative & safer approch to use I felt using it would be a better move. For example, servers are now set with the csv functions rather than the original setup and because of that change you could now set dns servers in url formats and I might possibly look at adding in __yarl__ to be the cherry on top for those who might wish to get real creative about dns server urls.
75
+
76
+ Moving over the __ares_query__ function to use the dns recursion function is planned for the future (maybe after I do the first release of cyares). I still wanted to retain the original response data it gives so that migrating from __pycares__ to __cyares__ wouldn't be a pain to anyone who planned on moving and I also didn't want to take away from __pycares__ either if you prefer using __cffi__ over __cython__ the same way __curl-cffi__ and __cycurl__ were both done. I have my fingers crossed that __aiodns__ will adopt this library in the future or allow users to choose between __pycares__ and __cyares__.
77
+
cyares-0.1.0/README.md ADDED
@@ -0,0 +1,65 @@
1
+ ![cy-ares logo](Cy-Ares-Logo.png)
2
+
3
+ An Upgraded version of __pycares__ with faster and safer features.
4
+
5
+
6
+
7
+ # Small Examples of How to Use
8
+
9
+ ```python
10
+ from cyares import Channel
11
+
12
+ # NOTE: Dns Queries contain a Future object
13
+ # This is a little bit different from pycares
14
+ # Handles were used to prevent new or unwanted vulnerabilities along
15
+ # with easier control given to the user...
16
+ # The Future object comes from the python concurrent.futures standard library
17
+
18
+ def main():
19
+ with Channel(servers=["8.8.8.8", "8.8.4.4"], event_thread=True) as channel:
20
+ data = channel.query("google.com", "A").result()
21
+ print(data)
22
+
23
+ if __name__ == "__main__":
24
+ main()
25
+ ```
26
+
27
+ ## In Asyncio
28
+ There's a module for an aiodns version if aiodns were using cyares.
29
+
30
+ ```python
31
+ import winloop # or uvloop (asyncio will also work)
32
+ # I'll add trio support in a future update as well...
33
+ from cyares.aio import DNSResolver
34
+
35
+ async def test():
36
+ async with DNSResolver(["8.8.8.8", "8.8.4.4"]) as dns:
37
+ data = await dns.query("google.com", "A")
38
+ print(data)
39
+
40
+ if __name__ == "__main__":
41
+ winloop.run(test())
42
+ ```
43
+
44
+
45
+
46
+ ## Story
47
+ As Someone who as recently started to contribute to projects such as __aiohttp__ and a few of it's smaller libraries. The asynchronous dns resolver __aiodns__ that __aiohttp__ can optionally use, felt like the oddest one in the group. With __aiodns__ using __pycares__ under the hood I wanted to learn how it worked to see if I could use my skills to optimize it like I had previously done with __propcache__ and __multidict__ as well but I soon came to learn of it's many problems and when __pycares__ started to hang on me with lingering threads when it ran, something didn't feel right to me and it lead me down a very large rabbit-hole waiting to be re-explored.
48
+
49
+ __Pycares__ was quick, dirty and fast but as the years of it's existance went by something wasn't exactly right with it and it could use safer features, better optimization and safety practices. While __cffi__ might be both quick and easy to use I didn't see the benefit of using it. The many vulnerabilities it was getting told me something needed to change if not big. With __Pycares__ reaching version __5.0.0__ I wanted to celebrate it the same way node-js celebrated the 10 year annversery of the original __http-parser__. Write a new one.
50
+
51
+
52
+ ### The Rewrite
53
+
54
+ While contributing to the aio-libs repos whenever I had a gap of free-time on hand while waiting on a question to be answered and when my other python libraries felt stable enough such as (deprecated-params, aiocallback, aiothreading & winloop) this is where I spent my time on. It had a total of 3 re-writes until I was satisfied with the result. Turns out just doing everything in small chunks makes all the difference.
55
+
56
+ It only felt right to me that migrating the library from __cffi__ to __cython__ would be the best solution to the problem. Not __Rust__ or __C__, just __pure-cython__ and small amounts of __C__ whenever nessesary and re-changing many of the internals to incorperate a safer approch. The reason for not picking __Rust__ is that there wasn't nessesarly a need to be memory-safe. What I cared about was speed, when people are doing DNS Lookups, speed matters and __Rust__ did not have a friendly enough archetecture that would set a future or allow me to access the parent from the child.
57
+
58
+ There was no better canadate than to move to using handles the same way __uvloop__ & __winloop__ do it and having authored one of them I was familliar with this concept.
59
+
60
+ The idea was not to reinvent the wheel rather move parts of the library over in chunks and look at __pycares__ for clues on how certain things should be laid out. At the end of the day, __Pycares__ was the blueprint and using __concurrent.futures__ & __Cython__ was the cure. __Py_buffer__ techniques brought over from __msgspec__ were utilized incase users were planning to use any bytes or string objects of any sort and I made sure the license was on it incase users wanted to know where it came from.
61
+
62
+ If there was a deprecated function with an alternative & safer approch to use I felt using it would be a better move. For example, servers are now set with the csv functions rather than the original setup and because of that change you could now set dns servers in url formats and I might possibly look at adding in __yarl__ to be the cherry on top for those who might wish to get real creative about dns server urls.
63
+
64
+ Moving over the __ares_query__ function to use the dns recursion function is planned for the future (maybe after I do the first release of cyares). I still wanted to retain the original response data it gives so that migrating from __pycares__ to __cyares__ wouldn't be a pain to anyone who planned on moving and I also didn't want to take away from __pycares__ either if you prefer using __cffi__ over __cython__ the same way __curl-cffi__ and __cycurl__ were both done. I have my fingers crossed that __aiodns__ will adopt this library in the future or allow users to choose between __pycares__ and __cyares__.
65
+
@@ -0,0 +1,2 @@
1
+ from .channel import * # type: ignore
2
+ from .resulttypes import *
@@ -0,0 +1,426 @@
1
+ """
2
+ aio
3
+ ---
4
+
5
+ A Version of Aiodns if Aiodns theoretically supported cyares by default.
6
+ This module is experimental and could be removed in the future if merged
7
+ with aiodns.
8
+
9
+ """
10
+
11
+ # Inspired by aiodns, joint library/modue was made incase
12
+ # aiodns devs decided not to adopt this library optionally.
13
+ # This was also made to test socket callbacks to see if they
14
+ # were working properly...
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import socket
19
+ import sys
20
+ from concurrent.futures import Future as cc_Future
21
+ from logging import getLogger
22
+ from typing import Any, Iterable, Literal, Sequence, TypeVar, overload
23
+
24
+ from .channel import * # type: ignore
25
+ from .exception import AresError
26
+ from .resulttypes import *
27
+
28
+ _T = TypeVar("_T")
29
+
30
+
31
+ WINDOWS_SELECTOR_ERR_MSG = (
32
+ "cyares.aio cannot use ProactorEventLoop on Windows"
33
+ " if cyares has no threadsafety. See more: "
34
+ "https://github.com/aio-libs/aiodns#note-for-windows-users",
35
+ )
36
+
37
+ _LOGGER = getLogger(__name__)
38
+
39
+
40
+ query_type_map = {
41
+ "A": QUERY_TYPE_A,
42
+ "AAAA": QUERY_TYPE_AAAA,
43
+ "ANY": QUERY_TYPE_ANY,
44
+ "CAA": QUERY_TYPE_CAA,
45
+ "CNAME": QUERY_TYPE_CNAME,
46
+ "MX": QUERY_TYPE_MX,
47
+ "NAPTR": QUERY_TYPE_NAPTR,
48
+ "NS": QUERY_TYPE_NS,
49
+ "PTR": QUERY_TYPE_PTR,
50
+ "SOA": QUERY_TYPE_SOA,
51
+ "SRV": QUERY_TYPE_SRV,
52
+ "TXT": QUERY_TYPE_TXT,
53
+ }
54
+
55
+ query_class_map = {
56
+ "IN": QUERY_CLASS_IN,
57
+ "CHAOS": QUERY_CLASS_CHAOS,
58
+ "HS": QUERY_CLASS_HS,
59
+ "NONE": QUERY_CLASS_NONE,
60
+ "ANY": QUERY_CLASS_ANY,
61
+ }
62
+
63
+
64
+ # Simillar to aiodns's version but more compact
65
+ class DNSResolver:
66
+ def __init__(
67
+ self,
68
+ nameservers: list[str] | None = None,
69
+ loop: asyncio.AbstractEventLoop | None = None,
70
+ event_thread: bool = True,
71
+ **kwargs,
72
+ ) -> None:
73
+ """
74
+ Params
75
+ ------
76
+
77
+ :param nameservers: a list of dns servers to connect to
78
+ :param loop: the asyncio event loop to utilize, supported
79
+ eventloops include, winloop, uvloop and the asyncio standard library
80
+ NOTE for windows users: `SelectorEventLoop` & `winloop.Loop` are the only
81
+ eventloops you can use when the event_thread is disabled or when event_thread
82
+ is not supported SEE: https://github.com/aio-libs/aiodns#note-for-windows-users
83
+ for more information
84
+
85
+ :param event_thread: if enabled try to see if cyares can utilize \
86
+ event threads otherwise fallback to using a socket callback handle \
87
+ if `false` socket callback will be used no matter what,
88
+ setting to `false` is good for testing purposes or
89
+ when trying to act threadsafe until closure.
90
+ A quick word of caution , deletion is **Not Thread-Safe**
91
+ SEE: https://c-ares.org/docs/ares_destroy.html
92
+
93
+ """
94
+ self._closed = True
95
+ self.loop = loop or asyncio.get_event_loop()
96
+
97
+ # XXX: Do not use, we override this argument by default
98
+ kwargs.pop("sock_state_cb", None)
99
+
100
+ timeout = kwargs.pop("timeout", None)
101
+ self._timeout = timeout
102
+ # Internal (Using with pytest to help debug socket_cb handles)
103
+ if event_thread:
104
+ self._event_thread, self._channel = self._make_channel(**kwargs)
105
+
106
+ else:
107
+ self._raise_if_windows_proctor()
108
+ self._event_thread, self._channel = (
109
+ False,
110
+ Channel(
111
+ sock_state_cb=self._sock_state_cb, timeout=self._timeout, **kwargs
112
+ ),
113
+ )
114
+
115
+ if nameservers:
116
+ self.nameservers = nameservers
117
+ self._read_fds: set[int] = set()
118
+ self._write_fds: set[int] = set()
119
+
120
+ # As an extra safety concern lets ensure
121
+ # that the asyncio futures being carried &
122
+ # can't trigger segfaults at cleanup...
123
+ self._handles: set[asyncio.Future[Any]] = set()
124
+ self._empty = asyncio.Event()
125
+
126
+ self._timer: asyncio.TimerHandle | None = None
127
+ self._closed = False
128
+
129
+ def _raise_if_windows_proctor(self):
130
+ if sys.platform == "win32" and type(self.loop) is asyncio.ProactorEventLoop:
131
+ raise RuntimeError(WINDOWS_SELECTOR_ERR_MSG)
132
+
133
+ def _on_fut_done(self, fut: asyncio.Future[Any]):
134
+ self._handles.remove(fut)
135
+ if not self._handles:
136
+ self._empty.set()
137
+
138
+ def _wrap_future(self, fut: cc_Future[_T]) -> asyncio.Future[_T]:
139
+ out_fut = asyncio.wrap_future(fut)
140
+ self._handles.add(out_fut)
141
+ out_fut.add_done_callback(self._on_fut_done)
142
+ self._empty.clear()
143
+ return out_fut
144
+
145
+ def _make_channel(self, **kwargs: Any) -> tuple[bool, Channel]:
146
+ if cyares_threadsafety():
147
+ # CyAres is Threadsafe
148
+ try:
149
+ return True, Channel(event_thread=True, timeout=self._timeout, **kwargs)
150
+ except AresError as e:
151
+ if sys.platform == "linux":
152
+ _LOGGER.warning(
153
+ "Failed to create DNS resolver channel with automatic "
154
+ "monitoring of resolver configuration changes. This "
155
+ "usually means the system ran out of inotify watches. "
156
+ "Falling back to socket state callback. Consider "
157
+ "increasing the system inotify watch limit: %s",
158
+ e,
159
+ )
160
+ else:
161
+ _LOGGER.warning(
162
+ "Failed to create DNS resolver channel with automatic "
163
+ "monitoring of resolver configuration changes. "
164
+ "Falling back to socket state callback: %s",
165
+ e,
166
+ )
167
+
168
+ self._raise_if_windows_proctor()
169
+
170
+ return False, Channel(
171
+ sock_state_cb=self._sock_state_cb, timeout=self._timeout, **kwargs
172
+ )
173
+
174
+ def _timer_cb(self) -> None:
175
+ if self._read_fds or self._write_fds:
176
+ self._channel.process_fd(CYARES_SOCKET_BAD, CYARES_SOCKET_BAD)
177
+ self._start_timer()
178
+ else:
179
+ self._timer = None
180
+
181
+ def _start_timer(self) -> None:
182
+ timeout = self._timeout
183
+ if timeout is None or timeout < 0 or timeout > 1:
184
+ timeout = 1
185
+ elif timeout == 0:
186
+ timeout = 0.1
187
+
188
+ self._timer = self.loop.call_later(timeout, self._timer_cb)
189
+
190
+ def cancel(self) -> None:
191
+ """Cancels all running futures queued by this dns resolver"""
192
+ self._channel.cancel()
193
+
194
+ async def _cleanup(self) -> None:
195
+ """Cleanup timers and file descriptors when closing resolver."""
196
+ if self._closed:
197
+ return
198
+ # Mark as closed first to prevent double cleanup
199
+ self._closed = True
200
+ # Cancel timer if running
201
+ if self._timer is not None:
202
+ self._timer.cancel()
203
+ self._timer = None
204
+
205
+ # Remove all file descriptors
206
+ for fd in self._read_fds:
207
+ self.loop.remove_reader(fd)
208
+ for fd in self._write_fds:
209
+ self.loop.remove_writer(fd)
210
+
211
+ self._read_fds.clear()
212
+ self._write_fds.clear()
213
+ self.cancel()
214
+
215
+ # Something a little different is that unline aiodns
216
+ # we're carrying handles to prevent crashing.
217
+ # This could be removed in the future if provent that
218
+ # it doesn't crash anymore.
219
+
220
+ if self._handles:
221
+ # wait for all handles to empty out otherwise assume it completed
222
+ try:
223
+ await asyncio.wait_for(self._empty.wait(), 0.1)
224
+ except asyncio.TimeoutError:
225
+ pass
226
+
227
+ def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None:
228
+ if readable or writable:
229
+ if readable:
230
+ self.loop.add_reader(fd, self._channel.process_read_fd, fd)
231
+ self._read_fds.add(fd)
232
+ if writable:
233
+ self.loop.add_writer(fd, self._channel.process_write_fd, fd)
234
+ self._write_fds.add(fd)
235
+ if self._timer is None:
236
+ self._start_timer()
237
+ else:
238
+ # socket is now closed
239
+ if fd in self._read_fds:
240
+ self._read_fds.discard(fd)
241
+ self.loop.remove_reader(fd)
242
+
243
+ if fd in self._write_fds:
244
+ self._write_fds.discard(fd)
245
+ self.loop.remove_writer(fd)
246
+
247
+ if not self._read_fds and not self._write_fds and self._timer is not None:
248
+ self._timer.cancel()
249
+ self._timer = None
250
+
251
+ @property
252
+ def nameservers(self) -> Sequence[str]:
253
+ return self._channel.servers
254
+
255
+ @nameservers.setter
256
+ def nameservers(self, value: Iterable[str | bytes]) -> None:
257
+ self._channel.servers = value
258
+
259
+ @overload
260
+ def query(
261
+ self, host: str, qtype: Literal["A"], qclass: str | None = ...
262
+ ) -> asyncio.Future[list[ares_query_a_result]]: ...
263
+ @overload
264
+ def query(
265
+ self, host: str, qtype: Literal["AAAA"], qclass: str | None = ...
266
+ ) -> asyncio.Future[list[ares_query_aaaa_result]]: ...
267
+ @overload
268
+ def query(
269
+ self, host: str, qtype: Literal["CAA"], qclass: str | None = ...
270
+ ) -> asyncio.Future[list[ares_query_caa_result]]: ...
271
+ @overload
272
+ def query(
273
+ self, host: str, qtype: Literal["CNAME"], qclass: str | None = ...
274
+ ) -> asyncio.Future[ares_query_cname_result]: ...
275
+ @overload
276
+ def query(
277
+ self, host: str, qtype: Literal["MX"], qclass: str | None = ...
278
+ ) -> asyncio.Future[list[ares_query_mx_result]]: ...
279
+ @overload
280
+ def query(
281
+ self, host: str, qtype: Literal["NAPTR"], qclass: str | None = ...
282
+ ) -> asyncio.Future[list[ares_query_naptr_result]]: ...
283
+ @overload
284
+ def query(
285
+ self, host: str, qtype: Literal["NS"], qclass: str | None = ...
286
+ ) -> asyncio.Future[list[ares_query_ns_result]]: ...
287
+ @overload
288
+ def query(
289
+ self, host: str, qtype: Literal["PTR"], qclass: str | None = ...
290
+ ) -> asyncio.Future[list[ares_query_ptr_result]]: ...
291
+ @overload
292
+ def query(
293
+ self, host: str, qtype: Literal["SOA"], qclass: str | None = ...
294
+ ) -> asyncio.Future[ares_query_soa_result]: ...
295
+ @overload
296
+ def query(
297
+ self, host: str, qtype: Literal["SRV"], qclass: str | None = ...
298
+ ) -> asyncio.Future[list[ares_query_srv_result]]: ...
299
+ @overload
300
+ def query(
301
+ self, host: str, qtype: Literal["TXT"], qclass: str | None = ...
302
+ ) -> asyncio.Future[list[ares_query_txt_result]]: ...
303
+
304
+ def query(
305
+ self, host: str, qtype: str, qclass: str | None = None
306
+ ) -> asyncio.Future[list[Any]] | asyncio.Future[Any]:
307
+ try:
308
+ qtype = query_type_map[qtype]
309
+ except KeyError as e:
310
+ raise ValueError(f"invalid query type: {qtype}") from e
311
+ if qclass is not None:
312
+ try:
313
+ qclass = query_class_map[qclass]
314
+ except KeyError as e:
315
+ raise ValueError(f"invalid query class: {qclass}") from e
316
+
317
+ # we use a different technique than pycares to try and
318
+ # aggressively prevent vulnerabilities
319
+
320
+ return self._wrap_future(self._channel.query(host, qtype, qclass))
321
+
322
+ def gethostbyname(
323
+ self, host: str, family: socket.AddressFamily
324
+ ) -> asyncio.Future[ares_host_result]:
325
+ return self._wrap_future(self._channel.gethostbyname(host, family))
326
+
327
+ def getaddrinfo(
328
+ self,
329
+ host: str,
330
+ family: socket.AddressFamily = socket.AF_UNSPEC,
331
+ port: int | None = None,
332
+ proto: int = 0,
333
+ type: int = 0,
334
+ flags: int = 0,
335
+ ) -> asyncio.Future[ares_addrinfo_result]:
336
+ return self._wrap_future(
337
+ self._channel.getaddrinfo(
338
+ host, port, family=family, type=type, proto=proto, flags=flags
339
+ )
340
+ )
341
+
342
+ def gethostbyaddr(
343
+ self, name: str | bytes | bytearray | memoryview[int]
344
+ ) -> asyncio.Future[ares_host_result]:
345
+ return self._wrap_future(self._channel.gethostbyaddr(name))
346
+
347
+ async def close(self) -> None:
348
+ """
349
+ Cleanly close the DNS resolver.
350
+
351
+ This should be called to ensure all resources are properly released.
352
+ After calling close(), the resolver should not be used again.
353
+ """
354
+ await self._cleanup()
355
+
356
+ # Still needs a little bit more work on...
357
+ # @overload
358
+ # def search(
359
+ # self, host: str, qtype: Literal["A"], qclass: str | None = ...
360
+ # ) -> asyncio.Future[list[ares_query_a_result]]: ...
361
+ # @overload
362
+ # def search(
363
+ # self, host: str, qtype: Literal["AAAA"], qclass: str | None = ...
364
+ # ) -> asyncio.Future[list[ares_query_aaaa_result]]: ...
365
+ # @overload
366
+ # def search(
367
+ # self, host: str, qtype: Literal["CAA"], qclass: str | None = ...
368
+ # ) -> asyncio.Future[list[ares_query_caa_result]]: ...
369
+ # @overload
370
+ # def search(
371
+ # self, host: str, qtype: Literal["CNAME"], qclass: str | None = ...
372
+ # ) -> asyncio.Future[ares_query_cname_result]: ...
373
+ # @overload
374
+ # def search(
375
+ # self, host: str, qtype: Literal["MX"], qclass: str | None = ...
376
+ # ) -> asyncio.Future[list[ares_query_mx_result]]: ...
377
+ # @overload
378
+ # def search(
379
+ # self, host: str, qtype: Literal["NAPTR"], qclass: str | None = ...
380
+ # ) -> asyncio.Future[list[ares_query_naptr_result]]: ...
381
+ # @overload
382
+ # def search(
383
+ # self, host: str, qtype: Literal["NS"], qclass: str | None = ...
384
+ # ) -> asyncio.Future[list[ares_query_ns_result]]: ...
385
+ # @overload
386
+ # def search(
387
+ # self, host: str, qtype: Literal["PTR"], qclass: str | None = ...
388
+ # ) -> asyncio.Future[list[ares_query_ptr_result]]: ...
389
+ # @overload
390
+ # def search(
391
+ # self, host: str, qtype: Literal["SOA"], qclass: str | None = ...
392
+ # ) -> asyncio.Future[ares_query_soa_result]: ...
393
+ # @overload
394
+ # def search(
395
+ # self, host: str, qtype: Literal["SRV"], qclass: str | None = ...
396
+ # ) -> asyncio.Future[list[ares_query_srv_result]]: ...
397
+ # @overload
398
+ # def search(
399
+ # self, host: str, qtype: Literal["TXT"], qclass: str | None = ...
400
+ # ) -> asyncio.Future[list[ares_query_txt_result]]: ...
401
+
402
+ # def search(
403
+ # self, host: str, qtype: str, qclass: str | None = None
404
+ # ) -> asyncio.Future[list[Any]] | asyncio.Future[Any]:
405
+ # try:
406
+ # qtype = query_type_map[qtype]
407
+ # except KeyError as e:
408
+ # raise ValueError(f"invalid query type: {qtype}") from e
409
+ # if qclass is not None:
410
+ # try:
411
+ # qclass = query_class_map[qclass]
412
+ # except KeyError as e:
413
+ # raise ValueError(f"invalid query class: {qclass}") from e
414
+
415
+ # # we use a different technique than pycares to try and
416
+ # # aggressively prevent vulnerabilities
417
+
418
+ # return self._wrap_future(self._channel.query(host, qtype, qclass))
419
+
420
+ async def __aenter__(self):
421
+ return self
422
+
423
+ async def __aexit__(self, *args):
424
+ await self.close()
425
+
426
+ # TODO: I will do the rest of the functionality a bit later...