Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix loop.getaddrinfo() and tests #495

Merged
merged 3 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions tests/test_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,14 @@ def test_getaddrinfo_18(self):
def test_getaddrinfo_19(self):
self._test_getaddrinfo('::1', 80)
self._test_getaddrinfo('::1', 80, type=socket.SOCK_STREAM)
self._test_getaddrinfo('::1', 80, type=socket.SOCK_STREAM,
flags=socket.AI_CANONNAME)

def test_getaddrinfo_20(self):
self._test_getaddrinfo('127.0.0.1', 80)
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM)
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM,
flags=socket.AI_CANONNAME)

######

Expand Down
2 changes: 1 addition & 1 deletion tests/test_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def test_create_server_4(self):

with self.assertRaisesRegex(OSError,
r"error while attempting.*\('127.*: "
r"address already in use"):
r"address( already)? in use"):

self.loop.run_until_complete(
self.loop.create_server(object, *addr))
Expand Down
52 changes: 49 additions & 3 deletions uvloop/dns.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,28 @@ cdef __static_getaddrinfo(object host, object port,
return (family, type, proto)


# This flag is used in __static_getaddrinfo_pyaddr() to manage
# if ai_canonname should be returned when AI_CANONNAME flag is set,
# because its behavior varies in different libc implementations (see #494).
# This flag is lazily set in loop.getaddrinfo() to make sure that
# __static_getaddrinfo_pyaddr() behaves consistently as libc getaddrinfo().
cdef int __static_getaddrinfo_canonname_mode = 0

# Bitmasks for __static_getaddrinfo_canonname_mode:

# If STATIC_GETADDRINFO_CANONNAME_ON_RETURN is set
cdef int STATIC_GETADDRINFO_CANONNAME_ON_BEHAVIOR_SET = 1 << 0

# If ai_canonname should be returned when AI_CANONNAME is set
cdef int STATIC_GETADDRINFO_CANONNAME_ON_RETURN = 1 << 1

# If STATIC_GETADDRINFO_CANONNAME_OFF_RETURN is set
cdef int STATIC_GETADDRINFO_CANONNAME_OFF_BEHAVIOR_SET = 1 << 2

# If ai_canonname should be returned when AI_CANONNAME is not set
cdef int STATIC_GETADDRINFO_CANONNAME_OFF_RETURN = 1 << 3


cdef __static_getaddrinfo_pyaddr(object host, object port,
int family, int type,
int proto, int flags):
Expand All @@ -245,7 +267,24 @@ cdef __static_getaddrinfo_pyaddr(object host, object port,
except Exception:
return

return af, type, proto, '', pyaddr
if __static_getaddrinfo_canonname_mode & (
STATIC_GETADDRINFO_CANONNAME_ON_RETURN if flags & socket_AI_CANONNAME
else STATIC_GETADDRINFO_CANONNAME_OFF_RETURN
):
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)
Expand Down Expand Up @@ -276,8 +315,8 @@ cdef class AddrInfo:
while ptr != NULL:
if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6):
result.append((
ptr.ai_family,
ptr.ai_socktype,
_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()),
Expand Down Expand Up @@ -370,6 +409,13 @@ cdef class NameInfoRequest(UVRequest):
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) with gil:

Expand Down
1 change: 1 addition & 0 deletions uvloop/includes/stdlib.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ cdef int has_SO_REUSEPORT = hasattr(socket, 'SO_REUSEPORT')
cdef int SO_REUSEPORT = getattr(socket, 'SO_REUSEPORT', 0)
cdef int SO_BROADCAST = getattr(socket, 'SO_BROADCAST')
cdef int SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', -1)
cdef int socket_AI_CANONNAME = getattr(socket, 'AI_CANONNAME')

cdef socket_gaierror = socket.gaierror
cdef socket_error = socket.error
Expand Down
33 changes: 30 additions & 3 deletions uvloop/loop.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1523,13 +1523,40 @@ cdef class Loop:
@cython.iterable_coroutine
async def getaddrinfo(self, object host, object port, *,
int family=0, int type=0, int proto=0, int flags=0):
global __static_getaddrinfo_canonname_mode

addr = __static_getaddrinfo_pyaddr(host, port, family,
type, proto, flags)
if addr is not None:
fut = self._new_future()
fut.set_result([addr])
return await fut
if __static_getaddrinfo_canonname_mode & (
STATIC_GETADDRINFO_CANONNAME_ON_BEHAVIOR_SET
if flags & socket_AI_CANONNAME
else STATIC_GETADDRINFO_CANONNAME_OFF_BEHAVIOR_SET
):
return [addr]

rv = await self._getaddrinfo(
host, port, family, type, proto, flags, 1)

_af, _type, _proto, canon_name, _addr = rv[0]
if flags & socket_AI_CANONNAME:
__static_getaddrinfo_canonname_mode |= (
STATIC_GETADDRINFO_CANONNAME_ON_BEHAVIOR_SET
)
if canon_name:
__static_getaddrinfo_canonname_mode |= (
STATIC_GETADDRINFO_CANONNAME_ON_RETURN
)
else:
__static_getaddrinfo_canonname_mode |= (
STATIC_GETADDRINFO_CANONNAME_OFF_BEHAVIOR_SET
)
if canon_name:
__static_getaddrinfo_canonname_mode |= (
STATIC_GETADDRINFO_CANONNAME_OFF_RETURN
)

return rv

return await self._getaddrinfo(
host, port, family, type, proto, flags, 1)
Expand Down