emailsec._dns_resolver

 1import asyncio
 2import typing
 3
 4from emailsec import _errors as errors
 5
 6import aiodns
 7import pycares
 8
 9_MAX_LOOKUPS = 10
10
11
12class DNSResolver:
13    def __init__(self) -> None:
14        self.__lookups_counter = 0
15        self.__resolver = aiodns.DNSResolver(loop=asyncio.get_running_loop())
16
17    async def _query(self, name: str, query_type: str) -> typing.Any:
18        self.__lookups_counter += 1
19        if self.__lookups_counter > _MAX_LOOKUPS:
20            raise errors.Permerror("Max DNS lookups exceeded")
21
22        try:
23            return await self.__resolver.query(name, query_type)
24        except aiodns.error.DNSError as dns_error:
25            error_msg = dns_error.args[1]
26            match dns_error.args[0]:
27                case aiodns.error.ARES_ENOTFOUND | aiodns.error.ARES_ENODATA:  # type: ignore
28                    return None
29                case (
30                    aiodns.error.ARES_EBADQUERY  # type: ignore
31                    | aiodns.error.ARES_EFORMERR  # type: ignore
32                    | aiodns.error.ARES_EBADNAME  # type: ignore
33                    | aiodns.error.ARES_EBADRESP  # type: ignore
34                ):
35                    raise errors.Permerror(
36                        f"DNS error (code {dns_error.args[0]}): {error_msg}"
37                    )
38                case _:
39                    raise errors.Temperror(
40                        f"DNS error (code {dns_error.args[0]}): {error_msg}"
41                    )
42
43    async def txt(self, name: str) -> list[pycares.ares_query_txt_result] | None:
44        return await self._query(name, "TXT")
45
46    async def mx(self, name: str) -> list[pycares.ares_query_mx_result] | None:
47        return await self._query(name, "MX")
48
49    async def a(self, name: str) -> list[pycares.ares_query_a_result] | None:
50        return await self._query(name, "A")
51
52    async def aaaa(self, name: str) -> list[pycares.ares_query_aaaa_result] | None:
53        return await self._query(name, "AAAA")
54
55    async def ptr(self, name: str) -> pycares.ares_query_ptr_result | None:
56        return await self._query(name, "PTR")
class DNSResolver:
13class DNSResolver:
14    def __init__(self) -> None:
15        self.__lookups_counter = 0
16        self.__resolver = aiodns.DNSResolver(loop=asyncio.get_running_loop())
17
18    async def _query(self, name: str, query_type: str) -> typing.Any:
19        self.__lookups_counter += 1
20        if self.__lookups_counter > _MAX_LOOKUPS:
21            raise errors.Permerror("Max DNS lookups exceeded")
22
23        try:
24            return await self.__resolver.query(name, query_type)
25        except aiodns.error.DNSError as dns_error:
26            error_msg = dns_error.args[1]
27            match dns_error.args[0]:
28                case aiodns.error.ARES_ENOTFOUND | aiodns.error.ARES_ENODATA:  # type: ignore
29                    return None
30                case (
31                    aiodns.error.ARES_EBADQUERY  # type: ignore
32                    | aiodns.error.ARES_EFORMERR  # type: ignore
33                    | aiodns.error.ARES_EBADNAME  # type: ignore
34                    | aiodns.error.ARES_EBADRESP  # type: ignore
35                ):
36                    raise errors.Permerror(
37                        f"DNS error (code {dns_error.args[0]}): {error_msg}"
38                    )
39                case _:
40                    raise errors.Temperror(
41                        f"DNS error (code {dns_error.args[0]}): {error_msg}"
42                    )
43
44    async def txt(self, name: str) -> list[pycares.ares_query_txt_result] | None:
45        return await self._query(name, "TXT")
46
47    async def mx(self, name: str) -> list[pycares.ares_query_mx_result] | None:
48        return await self._query(name, "MX")
49
50    async def a(self, name: str) -> list[pycares.ares_query_a_result] | None:
51        return await self._query(name, "A")
52
53    async def aaaa(self, name: str) -> list[pycares.ares_query_aaaa_result] | None:
54        return await self._query(name, "AAAA")
55
56    async def ptr(self, name: str) -> pycares.ares_query_ptr_result | None:
57        return await self._query(name, "PTR")
async def txt(self, name: str) -> list[pycares.ares_query_txt_result] | None:
44    async def txt(self, name: str) -> list[pycares.ares_query_txt_result] | None:
45        return await self._query(name, "TXT")
async def mx(self, name: str) -> list[pycares.ares_query_mx_result] | None:
47    async def mx(self, name: str) -> list[pycares.ares_query_mx_result] | None:
48        return await self._query(name, "MX")
async def a(self, name: str) -> list[pycares.ares_query_a_result] | None:
50    async def a(self, name: str) -> list[pycares.ares_query_a_result] | None:
51        return await self._query(name, "A")
async def aaaa(self, name: str) -> list[pycares.ares_query_aaaa_result] | None:
53    async def aaaa(self, name: str) -> list[pycares.ares_query_aaaa_result] | None:
54        return await self._query(name, "AAAA")
async def ptr(self, name: str) -> pycares.ares_query_ptr_result | None:
56    async def ptr(self, name: str) -> pycares.ares_query_ptr_result | None:
57        return await self._query(name, "PTR")