summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/uvloop/dns.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/uvloop/dns.pyx')
-rw-r--r--venv/lib/python3.11/site-packages/uvloop/dns.pyx471
1 files changed, 471 insertions, 0 deletions
diff --git a/venv/lib/python3.11/site-packages/uvloop/dns.pyx b/venv/lib/python3.11/site-packages/uvloop/dns.pyx
new file mode 100644
index 0000000..7aad631
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/uvloop/dns.pyx
@@ -0,0 +1,471 @@
+cdef __port_to_int(port, proto):
+ if type(port) is int:
+ return port
+
+ if port is None or port == '' or port == b'':
+ return 0
+
+ try:
+ return int(port)
+ except (ValueError, TypeError):
+ pass
+
+ if isinstance(port, bytes):
+ port = port.decode()
+
+ if isinstance(port, str) and proto is not None:
+ if proto == uv.IPPROTO_TCP:
+ return socket_getservbyname(port, 'tcp')
+ elif proto == uv.IPPROTO_UDP:
+ return socket_getservbyname(port, 'udp')
+
+ raise OSError('service/proto not found')
+
+
+cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
+ # Converts sockaddr structs into what Python socket
+ # module can understand:
+ # - for IPv4 a tuple of (host, port)
+ # - for IPv6 a tuple of (host, port, flowinfo, scope_id)
+
+ cdef:
+ char buf[128] # INET6_ADDRSTRLEN is usually 46
+ int err
+ system.sockaddr_in *addr4
+ system.sockaddr_in6 *addr6
+ system.sockaddr_un *addr_un
+
+ if addr.sa_family == uv.AF_INET:
+ addr4 = <system.sockaddr_in*>addr
+
+ err = uv.uv_ip4_name(addr4, buf, sizeof(buf))
+ if err < 0:
+ raise convert_error(err)
+
+ return (
+ PyUnicode_FromString(buf),
+ system.ntohs(addr4.sin_port)
+ )
+
+ elif addr.sa_family == uv.AF_INET6:
+ addr6 = <system.sockaddr_in6*>addr
+
+ err = uv.uv_ip6_name(addr6, buf, sizeof(buf))
+ if err < 0:
+ raise convert_error(err)
+
+ return (
+ PyUnicode_FromString(buf),
+ system.ntohs(addr6.sin6_port),
+ system.ntohl(addr6.sin6_flowinfo),
+ addr6.sin6_scope_id
+ )
+
+ elif addr.sa_family == uv.AF_UNIX:
+ addr_un = <system.sockaddr_un*>addr
+ return system.MakeUnixSockPyAddr(addr_un)
+
+ raise RuntimeError("cannot convert sockaddr into Python object")
+
+
+@cython.freelist(DEFAULT_FREELIST_SIZE)
+cdef class SockAddrHolder:
+ cdef:
+ int family
+ system.sockaddr_storage addr
+ Py_ssize_t addr_size
+
+
+cdef LruCache sockaddrs = LruCache(maxsize=DNS_PYADDR_TO_SOCKADDR_CACHE_SIZE)
+
+
+cdef __convert_pyaddr_to_sockaddr(int family, object addr,
+ system.sockaddr* res):
+ cdef:
+ int err
+ int addr_len
+ int scope_id = 0
+ int flowinfo = 0
+ char *buf
+ Py_ssize_t buflen
+ SockAddrHolder ret
+
+ ret = sockaddrs.get(addr, None)
+ if ret is not None and ret.family == family:
+ memcpy(res, &ret.addr, ret.addr_size)
+ return
+
+ ret = SockAddrHolder.__new__(SockAddrHolder)
+ if family == uv.AF_INET:
+ if not isinstance(addr, tuple):
+ raise TypeError('AF_INET address must be tuple')
+ if len(addr) != 2:
+ raise ValueError('AF_INET address must be tuple of (host, port)')
+ host, port = addr
+ if isinstance(host, str):
+ try:
+ # idna codec is rather slow, so we try ascii first.
+ host = host.encode('ascii')
+ except UnicodeEncodeError:
+ host = host.encode('idna')
+ if not isinstance(host, (bytes, bytearray)):
+ raise TypeError('host must be a string or bytes object')
+
+ port = __port_to_int(port, None)
+
+ ret.addr_size = sizeof(system.sockaddr_in)
+ err = uv.uv_ip4_addr(host, <int>port, <system.sockaddr_in*>&ret.addr)
+ if err < 0:
+ raise convert_error(err)
+
+ elif family == uv.AF_INET6:
+ if not isinstance(addr, tuple):
+ raise TypeError('AF_INET6 address must be tuple')
+
+ addr_len = len(addr)
+ if addr_len < 2 or addr_len > 4:
+ raise ValueError(
+ 'AF_INET6 must be a tuple of 2-4 parameters: '
+ '(host, port, flowinfo?, scope_id?)')
+
+ host = addr[0]
+ if isinstance(host, str):
+ try:
+ # idna codec is rather slow, so we try ascii first.
+ host = host.encode('ascii')
+ except UnicodeEncodeError:
+ host = host.encode('idna')
+ if not isinstance(host, (bytes, bytearray)):
+ raise TypeError('host must be a string or bytes object')
+
+ port = __port_to_int(addr[1], None)
+
+ if addr_len > 2:
+ flowinfo = addr[2]
+ if addr_len > 3:
+ scope_id = addr[3]
+
+ ret.addr_size = sizeof(system.sockaddr_in6)
+
+ err = uv.uv_ip6_addr(host, port, <system.sockaddr_in6*>&ret.addr)
+ if err < 0:
+ raise convert_error(err)
+
+ (<system.sockaddr_in6*>&ret.addr).sin6_flowinfo = flowinfo
+ (<system.sockaddr_in6*>&ret.addr).sin6_scope_id = scope_id
+
+ elif family == uv.AF_UNIX:
+ if isinstance(addr, str):
+ addr = addr.encode(sys_getfilesystemencoding())
+ elif not isinstance(addr, bytes):
+ raise TypeError('AF_UNIX address must be a str or a bytes object')
+
+ PyBytes_AsStringAndSize(addr, &buf, &buflen)
+ if buflen > 107:
+ raise ValueError(
+ f'unix socket path {addr!r} is longer than 107 characters')
+
+ ret.addr_size = sizeof(system.sockaddr_un)
+ memset(&ret.addr, 0, sizeof(system.sockaddr_un))
+ (<system.sockaddr_un*>&ret.addr).sun_family = uv.AF_UNIX
+ memcpy((<system.sockaddr_un*>&ret.addr).sun_path, buf, buflen)
+
+ else:
+ raise ValueError(
+ f'expected AF_INET, AF_INET6, or AF_UNIX family, got {family}')
+
+ ret.family = family
+ sockaddrs[addr] = ret
+ memcpy(res, &ret.addr, ret.addr_size)
+
+
+cdef __static_getaddrinfo(object host, object port,
+ int family, int type,
+ int proto,
+ system.sockaddr *addr):
+
+ if proto not in {0, uv.IPPROTO_TCP, uv.IPPROTO_UDP}:
+ return
+
+ if _is_sock_stream(type):
+ proto = uv.IPPROTO_TCP
+ elif _is_sock_dgram(type):
+ proto = uv.IPPROTO_UDP
+ else:
+ return
+
+ try:
+ port = __port_to_int(port, proto)
+ except Exception:
+ return
+
+ hp = (host, port)
+ if family == uv.AF_UNSPEC:
+ try:
+ __convert_pyaddr_to_sockaddr(uv.AF_INET, hp, addr)
+ except Exception:
+ pass
+ else:
+ return (uv.AF_INET, type, proto)
+
+ try:
+ __convert_pyaddr_to_sockaddr(uv.AF_INET6, hp, addr)
+ except Exception:
+ pass
+ else:
+ return (uv.AF_INET6, type, proto)
+
+ else:
+ try:
+ __convert_pyaddr_to_sockaddr(family, hp, addr)
+ except Exception:
+ pass
+ else:
+ return (family, type, proto)
+
+
+cdef __static_getaddrinfo_pyaddr(object host, object port,
+ int family, int type,
+ int proto, int flags):
+
+ cdef:
+ system.sockaddr_storage addr
+ object triplet
+
+ triplet = __static_getaddrinfo(
+ host, port, family, type,
+ proto, <system.sockaddr*>&addr)
+ if triplet is None:
+ return
+
+ af, type, proto = triplet
+
+ try:
+ pyaddr = __convert_sockaddr_to_pyaddr(<system.sockaddr*>&addr)
+ except Exception:
+ return
+
+ # When the host is an IP while type is one of TCP or UDP, different libc
+ # implementations of getaddrinfo() behave differently:
+ # 1. When AI_CANONNAME is set:
+ # * glibc: returns ai_canonname
+ # * musl: returns ai_canonname
+ # * macOS: returns an empty string for ai_canonname
+ # 2. When AI_CANONNAME is NOT set:
+ # * glibc: returns an empty string for ai_canonname
+ # * musl: returns ai_canonname
+ # * macOS: returns an empty string for ai_canonname
+ # At the same time, libuv and CPython both uses libc directly, even though
+ # this different behavior is violating what is in the documentation.
+ #
+ # uvloop potentially should be a 100% drop-in replacement for asyncio,
+ # doing whatever asyncio does, especially when the libc implementations are
+ # also different in the same way. However, making our implementation to be
+ # consistent with libc/CPython would be complex and hard to maintain
+ # (including caching libc behaviors when flag is/not set), therefore we
+ # decided to simply normalize the behavior in uvloop for this very marginal
+ # case following the documentation, even though uvloop would behave
+ # differently to asyncio on macOS and musl platforms, when again the host
+ # is an IP and type is one of TCP or UDP.
+ # All other cases are still asyncio-compatible.
+ if flags & socket_AI_CANONNAME:
+ if isinstance(host, str):
+ canon_name = host
+ else:
+ canon_name = host.decode('ascii')
+ else:
+ canon_name = ''
+
+ return (
+ _intenum_converter(af, socket_AddressFamily),
+ _intenum_converter(type, socket_SocketKind),
+ proto,
+ canon_name,
+ pyaddr,
+ )
+
+
+@cython.freelist(DEFAULT_FREELIST_SIZE)
+cdef class AddrInfo:
+ cdef:
+ system.addrinfo *data
+
+ def __cinit__(self):
+ self.data = NULL
+
+ def __dealloc__(self):
+ if self.data is not NULL:
+ uv.uv_freeaddrinfo(self.data) # returns void
+ self.data = NULL
+
+ cdef void set_data(self, system.addrinfo *data):
+ self.data = data
+
+ cdef unpack(self):
+ cdef:
+ list result = []
+ system.addrinfo *ptr
+
+ if self.data is NULL:
+ raise RuntimeError('AddrInfo.data is NULL')
+
+ ptr = self.data
+ while ptr != NULL:
+ if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6):
+ result.append((
+ _intenum_converter(ptr.ai_family, socket_AddressFamily),
+ _intenum_converter(ptr.ai_socktype, socket_SocketKind),
+ ptr.ai_protocol,
+ ('' if ptr.ai_canonname is NULL else
+ (<bytes>ptr.ai_canonname).decode()),
+ __convert_sockaddr_to_pyaddr(ptr.ai_addr)
+ ))
+
+ ptr = ptr.ai_next
+
+ return result
+
+ @staticmethod
+ cdef int isinstance(object other):
+ return type(other) is AddrInfo
+
+
+cdef class AddrInfoRequest(UVRequest):
+ cdef:
+ system.addrinfo hints
+ object callback
+ uv.uv_getaddrinfo_t _req_data
+
+ def __cinit__(self, Loop loop,
+ bytes host, bytes port,
+ int family, int type, int proto, int flags,
+ object callback):
+
+ cdef:
+ int err
+ char *chost
+ char *cport
+
+ if host is None:
+ chost = NULL
+ else:
+ chost = <char*>host
+
+ if port is None:
+ cport = NULL
+ else:
+ cport = <char*>port
+
+ if cport is NULL and chost is NULL:
+ self.on_done()
+ msg = system.gai_strerror(socket_EAI_NONAME).decode('utf-8')
+ ex = socket_gaierror(socket_EAI_NONAME, msg)
+ callback(ex)
+ return
+
+ memset(&self.hints, 0, sizeof(system.addrinfo))
+ self.hints.ai_flags = flags
+ self.hints.ai_family = family
+ self.hints.ai_socktype = type
+ self.hints.ai_protocol = proto
+
+ self.request = <uv.uv_req_t*> &self._req_data
+ self.callback = callback
+ self.request.data = <void*>self
+
+ err = uv.uv_getaddrinfo(loop.uvloop,
+ <uv.uv_getaddrinfo_t*>self.request,
+ __on_addrinfo_resolved,
+ chost,
+ cport,
+ &self.hints)
+
+ if err < 0:
+ self.on_done()
+ callback(convert_error(err))
+
+
+cdef class NameInfoRequest(UVRequest):
+ cdef:
+ object callback
+ uv.uv_getnameinfo_t _req_data
+
+ def __cinit__(self, Loop loop, callback):
+ self.request = <uv.uv_req_t*> &self._req_data
+ self.callback = callback
+ self.request.data = <void*>self
+
+ cdef query(self, system.sockaddr *addr, int flags):
+ cdef int err
+ err = uv.uv_getnameinfo(self.loop.uvloop,
+ <uv.uv_getnameinfo_t*>self.request,
+ __on_nameinfo_resolved,
+ addr,
+ flags)
+ if err < 0:
+ self.on_done()
+ self.callback(convert_error(err))
+
+
+cdef _intenum_converter(value, enum_klass):
+ try:
+ return enum_klass(value)
+ except ValueError:
+ return value
+
+
+cdef void __on_addrinfo_resolved(
+ uv.uv_getaddrinfo_t *resolver,
+ int status,
+ system.addrinfo *res,
+) noexcept with gil:
+
+ if resolver.data is NULL:
+ aio_logger.error(
+ 'AddrInfoRequest callback called with NULL resolver.data')
+ return
+
+ cdef:
+ AddrInfoRequest request = <AddrInfoRequest> resolver.data
+ Loop loop = request.loop
+ object callback = request.callback
+ AddrInfo ai
+
+ try:
+ if status < 0:
+ callback(convert_error(status))
+ else:
+ ai = AddrInfo()
+ ai.set_data(res)
+ callback(ai)
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except BaseException as ex:
+ loop._handle_exception(ex)
+ finally:
+ request.on_done()
+
+
+cdef void __on_nameinfo_resolved(
+ uv.uv_getnameinfo_t* req,
+ int status,
+ const char* hostname,
+ const char* service,
+) noexcept with gil:
+ cdef:
+ NameInfoRequest request = <NameInfoRequest> req.data
+ Loop loop = request.loop
+ object callback = request.callback
+
+ try:
+ if status < 0:
+ callback(convert_error(status))
+ else:
+ callback(((<bytes>hostname).decode(),
+ (<bytes>service).decode()))
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except BaseException as ex:
+ loop._handle_exception(ex)
+ finally:
+ request.on_done()