Commit ff3cc4d2 authored by Jason Madden's avatar Jason Madden

Minor tweaks to converting getnameinfo flags for cares.

parent 5901babc
...@@ -87,6 +87,10 @@ class Resolver(AbstractResolver): ...@@ -87,6 +87,10 @@ class Resolver(AbstractResolver):
this implementation returns only the host name. This appears to be this implementation returns only the host name. This appears to be
the case only with entries found in ``/etc/hosts``. the case only with entries found in ``/etc/hosts``.
- c-ares supports a limited set of flags for ``getnameinfo`` and
``getaddrinfo``; unknown flags are ignored. System-specific flags
such as ``AI_V4MAPPED_CFG`` are not supported.
.. caution:: .. caution::
This module is considered extremely experimental on PyPy, and This module is considered extremely experimental on PyPy, and
...@@ -102,7 +106,7 @@ class Resolver(AbstractResolver): ...@@ -102,7 +106,7 @@ class Resolver(AbstractResolver):
.. _c-ares: http://c-ares.haxx.se .. _c-ares: http://c-ares.haxx.se
""" """
ares_class = channel cares_class = channel
def __init__(self, hub=None, use_environ=True, **kwargs): def __init__(self, hub=None, use_environ=True, **kwargs):
if hub is None: if hub is None:
...@@ -114,27 +118,27 @@ class Resolver(AbstractResolver): ...@@ -114,27 +118,27 @@ class Resolver(AbstractResolver):
value = setting.get() value = setting.get()
if value is not None: if value is not None:
kwargs.setdefault(setting.kwarg_name, value) kwargs.setdefault(setting.kwarg_name, value)
self.ares = self.ares_class(hub.loop, **kwargs) self.cares = self.cares_class(hub.loop, **kwargs)
self.pid = os.getpid() self.pid = os.getpid()
self.params = kwargs self.params = kwargs
self.fork_watcher = hub.loop.fork(ref=False) self.fork_watcher = hub.loop.fork(ref=False)
self.fork_watcher.start(self._on_fork) self.fork_watcher.start(self._on_fork)
def __repr__(self): def __repr__(self):
return '<gevent.resolver_ares.Resolver at 0x%x ares=%r>' % (id(self), self.ares) return '<gevent.resolver_ares.Resolver at 0x%x ares=%r>' % (id(self), self.cares)
def _on_fork(self): def _on_fork(self):
# NOTE: See comment in gevent.hub.reinit. # NOTE: See comment in gevent.hub.reinit.
pid = os.getpid() pid = os.getpid()
if pid != self.pid: if pid != self.pid:
self.hub.loop.run_callback(self.ares.destroy) self.hub.loop.run_callback(self.cares.destroy)
self.ares = self.ares_class(self.hub.loop, **self.params) self.cares = self.cares_class(self.hub.loop, **self.params)
self.pid = pid self.pid = pid
def close(self): def close(self):
if self.ares is not None: if self.cares is not None:
self.hub.loop.run_callback(self.ares.destroy) self.hub.loop.run_callback(self.cares.destroy)
self.ares = None self.cares = None
self.fork_watcher.stop() self.fork_watcher.stop()
def gethostbyname(self, hostname, family=AF_INET): def gethostbyname(self, hostname, family=AF_INET):
...@@ -154,7 +158,7 @@ class Resolver(AbstractResolver): ...@@ -154,7 +158,7 @@ class Resolver(AbstractResolver):
raise TypeError('Expected string, not %s' % type(hostname).__name__) raise TypeError('Expected string, not %s' % type(hostname).__name__)
while True: while True:
ares = self.ares ares = self.cares
try: try:
waiter = Waiter(self.hub) waiter = Waiter(self.hub)
ares.gethostbyname(waiter, hostname, family) ares.gethostbyname(waiter, hostname, family)
...@@ -163,14 +167,14 @@ class Resolver(AbstractResolver): ...@@ -163,14 +167,14 @@ class Resolver(AbstractResolver):
raise gaierror(-5, 'No address associated with hostname') raise gaierror(-5, 'No address associated with hostname')
return result return result
except gaierror: except gaierror:
if ares is self.ares: if ares is self.cares:
if hostname == b'255.255.255.255': if hostname == b'255.255.255.255':
# The stdlib handles this case in 2.7 and 3.x, but ares does not. # The stdlib handles this case in 2.7 and 3.x, but ares does not.
# It is tested by test_socket.py in 3.4. # It is tested by test_socket.py in 3.4.
# HACK: So hardcode the expected return. # HACK: So hardcode the expected return.
return ('255.255.255.255', [], ['255.255.255.255']) return ('255.255.255.255', [], ['255.255.255.255'])
raise raise
# "self.ares is not ares" means channel was destroyed (because we were forked) # "self.cares is not ares" means channel was destroyed (because we were forked)
def _lookup_port(self, port, socktype): def _lookup_port(self, port, socktype):
return lookup_port(port, socktype) return lookup_port(port, socktype)
...@@ -200,17 +204,17 @@ class Resolver(AbstractResolver): ...@@ -200,17 +204,17 @@ class Resolver(AbstractResolver):
if proto: if proto:
socktype_proto = [(x, y) for (x, y) in socktype_proto if proto == y] socktype_proto = [(x, y) for (x, y) in socktype_proto if proto == y]
ares = self.ares ares = self.cares
if family == AF_UNSPEC: if family == AF_UNSPEC:
ares_values = Values(self.hub, 2) ares_values = _Values(self.hub, 2)
ares.gethostbyname(ares_values, host, AF_INET) ares.gethostbyname(ares_values, host, AF_INET)
ares.gethostbyname(ares_values, host, AF_INET6) ares.gethostbyname(ares_values, host, AF_INET6)
elif family == AF_INET: elif family == AF_INET:
ares_values = Values(self.hub, 1) ares_values = _Values(self.hub, 1)
ares.gethostbyname(ares_values, host, AF_INET) ares.gethostbyname(ares_values, host, AF_INET)
elif family == AF_INET6: elif family == AF_INET6:
ares_values = Values(self.hub, 1) ares_values = _Values(self.hub, 1)
ares.gethostbyname(ares_values, host, AF_INET6) ares.gethostbyname(ares_values, host, AF_INET6)
else: else:
raise gaierror(5, 'ai_family not supported: %r' % (family, )) raise gaierror(5, 'ai_family not supported: %r' % (family, ))
...@@ -252,11 +256,11 @@ class Resolver(AbstractResolver): ...@@ -252,11 +256,11 @@ class Resolver(AbstractResolver):
def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0): def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0):
while True: while True:
ares = self.ares ares = self.cares
try: try:
return self._getaddrinfo(host, port, family, socktype, proto, flags) return self._getaddrinfo(host, port, family, socktype, proto, flags)
except gaierror: except gaierror:
if ares is self.ares: if ares is self.cares:
raise raise
def _gethostbyaddr(self, ip_address): def _gethostbyaddr(self, ip_address):
...@@ -273,7 +277,7 @@ class Resolver(AbstractResolver): ...@@ -273,7 +277,7 @@ class Resolver(AbstractResolver):
waiter = Waiter(self.hub) waiter = Waiter(self.hub)
try: try:
self.ares.gethostbyaddr(waiter, ip_address) self.cares.gethostbyaddr(waiter, ip_address)
return waiter.get() return waiter.get()
except InvalidIP: except InvalidIP:
result = self._getaddrinfo(ip_address, None, family=AF_UNSPEC, socktype=SOCK_DGRAM) result = self._getaddrinfo(ip_address, None, family=AF_UNSPEC, socktype=SOCK_DGRAM)
...@@ -285,17 +289,17 @@ class Resolver(AbstractResolver): ...@@ -285,17 +289,17 @@ class Resolver(AbstractResolver):
if _ip_address == ip_address: if _ip_address == ip_address:
raise raise
waiter.clear() waiter.clear()
self.ares.gethostbyaddr(waiter, _ip_address) self.cares.gethostbyaddr(waiter, _ip_address)
return waiter.get() return waiter.get()
def gethostbyaddr(self, ip_address): def gethostbyaddr(self, ip_address):
ip_address = _resolve_special(ip_address, AF_UNSPEC) ip_address = _resolve_special(ip_address, AF_UNSPEC)
while True: while True:
ares = self.ares ares = self.cares
try: try:
return self._gethostbyaddr(ip_address) return self._gethostbyaddr(ip_address)
except gaierror: except gaierror:
if ares is self.ares: if ares is self.cares:
raise raise
def _getnameinfo(self, sockaddr, flags): def _getnameinfo(self, sockaddr, flags):
...@@ -329,7 +333,7 @@ class Resolver(AbstractResolver): ...@@ -329,7 +333,7 @@ class Resolver(AbstractResolver):
elif family == AF_INET6: elif family == AF_INET6:
address = address[:2] + sockaddr[2:] address = address[:2] + sockaddr[2:]
self.ares.getnameinfo(waiter, address, flags) self.cares.getnameinfo(waiter, address, flags)
node, service = waiter.get() node, service = waiter.get()
if service is None: if service is None:
...@@ -347,16 +351,18 @@ class Resolver(AbstractResolver): ...@@ -347,16 +351,18 @@ class Resolver(AbstractResolver):
def getnameinfo(self, sockaddr, flags): def getnameinfo(self, sockaddr, flags):
while True: while True:
ares = self.ares ares = self.cares
try: try:
return self._getnameinfo(sockaddr, flags) return self._getnameinfo(sockaddr, flags)
except gaierror: except gaierror:
if ares is self.ares: if ares is self.cares:
raise raise
class Values(object): class _Values(object):
# helper to collect multiple values; ignore errors unless nothing has succeeded # helper to collect the results of multiple c-ares calls
# and ignore errors unless nothing has succeeded
# QQQ could probably be moved somewhere - hub.py? # QQQ could probably be moved somewhere - hub.py?
__slots__ = ['count', 'values', 'error', 'waiter'] __slots__ = ['count', 'values', 'error', 'waiter']
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
cimport libcares as cares cimport libcares as cares
import sys import sys
from cpython.version cimport PY_MAJOR_VERSION
from cpython.tuple cimport PyTuple_Check from cpython.tuple cimport PyTuple_Check
from cpython.getargs cimport PyArg_ParseTuple from cpython.getargs cimport PyArg_ParseTuple
from cpython.ref cimport Py_INCREF from cpython.ref cimport Py_INCREF
...@@ -20,17 +21,17 @@ from _socket import gaierror ...@@ -20,17 +21,17 @@ from _socket import gaierror
__all__ = ['channel'] __all__ = ['channel']
cdef object string_types cdef tuple string_types
cdef object text_type cdef type text_type
if sys.version_info[0] >= 3: if PY_MAJOR_VERSION >= 3:
string_types = str, string_types = str,
text_type = str text_type = str
else: else:
string_types = __builtins__.basestring, string_types = __builtins__.basestring,
text_type = __builtins__.unicode text_type = __builtins__.unicode
TIMEOUT = 1 DEF TIMEOUT = 1
DEF EV_READ = 1 DEF EV_READ = 1
DEF EV_WRITE = 2 DEF EV_WRITE = 2
...@@ -85,43 +86,6 @@ cdef extern from "ares.h": ...@@ -85,43 +86,6 @@ cdef extern from "ares.h":
unsigned int htons(unsigned int hostshort) unsigned int htons(unsigned int hostshort)
ARES_SUCCESS = cares.ARES_SUCCESS
ARES_ENODATA = cares.ARES_ENODATA
ARES_EFORMERR = cares.ARES_EFORMERR
ARES_ESERVFAIL = cares.ARES_ESERVFAIL
ARES_ENOTFOUND = cares.ARES_ENOTFOUND
ARES_ENOTIMP = cares.ARES_ENOTIMP
ARES_EREFUSED = cares.ARES_EREFUSED
ARES_EBADQUERY = cares.ARES_EBADQUERY
ARES_EBADNAME = cares.ARES_EBADNAME
ARES_EBADFAMILY = cares.ARES_EBADFAMILY
ARES_EBADRESP = cares.ARES_EBADRESP
ARES_ECONNREFUSED = cares.ARES_ECONNREFUSED
ARES_ETIMEOUT = cares.ARES_ETIMEOUT
ARES_EOF = cares.ARES_EOF
ARES_EFILE = cares.ARES_EFILE
ARES_ENOMEM = cares.ARES_ENOMEM
ARES_EDESTRUCTION = cares.ARES_EDESTRUCTION
ARES_EBADSTR = cares.ARES_EBADSTR
ARES_EBADFLAGS = cares.ARES_EBADFLAGS
ARES_ENONAME = cares.ARES_ENONAME
ARES_EBADHINTS = cares.ARES_EBADHINTS
ARES_ENOTINITIALIZED = cares.ARES_ENOTINITIALIZED
ARES_ELOADIPHLPAPI = cares.ARES_ELOADIPHLPAPI
ARES_EADDRGETNETWORKPARAMS = cares.ARES_EADDRGETNETWORKPARAMS
ARES_ECANCELLED = cares.ARES_ECANCELLED
ARES_FLAG_USEVC = cares.ARES_FLAG_USEVC
ARES_FLAG_PRIMARY = cares.ARES_FLAG_PRIMARY
ARES_FLAG_IGNTC = cares.ARES_FLAG_IGNTC
ARES_FLAG_NORECURSE = cares.ARES_FLAG_NORECURSE
ARES_FLAG_STAYOPEN = cares.ARES_FLAG_STAYOPEN
ARES_FLAG_NOSEARCH = cares.ARES_FLAG_NOSEARCH
ARES_FLAG_NOALIASES = cares.ARES_FLAG_NOALIASES
ARES_FLAG_NOCHECKRESP = cares.ARES_FLAG_NOCHECKRESP
_ares_errors = dict([ _ares_errors = dict([
(cares.ARES_SUCCESS, 'ARES_SUCCESS'), (cares.ARES_SUCCESS, 'ARES_SUCCESS'),
(cares.ARES_ENODATA, 'ARES_ENODATA'), (cares.ARES_ENODATA, 'ARES_ENODATA'),
...@@ -147,34 +111,38 @@ _ares_errors = dict([ ...@@ -147,34 +111,38 @@ _ares_errors = dict([
(cares.ARES_ENOTINITIALIZED, 'ARES_ENOTINITIALIZED'), (cares.ARES_ENOTINITIALIZED, 'ARES_ENOTINITIALIZED'),
(cares.ARES_ELOADIPHLPAPI, 'ARES_ELOADIPHLPAPI'), (cares.ARES_ELOADIPHLPAPI, 'ARES_ELOADIPHLPAPI'),
(cares.ARES_EADDRGETNETWORKPARAMS, 'ARES_EADDRGETNETWORKPARAMS'), (cares.ARES_EADDRGETNETWORKPARAMS, 'ARES_EADDRGETNETWORKPARAMS'),
(cares.ARES_ECANCELLED, 'ARES_ECANCELLED')]) (cares.ARES_ECANCELLED, 'ARES_ECANCELLED')
])
# maps c-ares flag to _socket module flag # maps c-ares getnameinfo() flag to _socket module flag
_cares_flag_map = None cdef list _cares_ni_flag_map = None
cdef _prepare_cares_flag_map(): cdef _prepare_cares_ni_flag_map():
global _cares_flag_map global _cares_ni_flag_map
import _socket import _socket
_cares_flag_map = [ _cares_ni_flag_map = [
(getattr(_socket, 'NI_NUMERICHOST', 1), cares.ARES_NI_NUMERICHOST), (_socket.NI_NUMERICHOST, cares.ARES_NI_NUMERICHOST),
(getattr(_socket, 'NI_NUMERICSERV', 2), cares.ARES_NI_NUMERICSERV), (_socket.NI_NUMERICSERV, cares.ARES_NI_NUMERICSERV),
(getattr(_socket, 'NI_NOFQDN', 4), cares.ARES_NI_NOFQDN), (_socket.NI_NOFQDN, cares.ARES_NI_NOFQDN),
(getattr(_socket, 'NI_NAMEREQD', 8), cares.ARES_NI_NAMEREQD), (_socket.NI_NAMEREQD, cares.ARES_NI_NAMEREQD),
(getattr(_socket, 'NI_DGRAM', 16), cares.ARES_NI_DGRAM)] (_socket.NI_DGRAM, cares.ARES_NI_DGRAM)
]
cpdef _convert_cares_flags(int flags, int default=cares.ARES_NI_LOOKUPHOST|cares.ARES_NI_LOOKUPSERVICE):
if _cares_flag_map is None: cdef _convert_cares_ni_flags(int flags,
_prepare_cares_flag_map() int default=cares.ARES_NI_LOOKUPHOST|cares.ARES_NI_LOOKUPSERVICE):
for socket_flag, cares_flag in _cares_flag_map: if _cares_ni_flag_map is None:
_prepare_cares_ni_flag_map()
cdef int result = default
for socket_flag, cares_flag in _cares_ni_flag_map:
# XXX: This is doing a lot of bouncing back and forth between
# C ints and Python objects, so it's slower than it has to be.
if socket_flag & flags: if socket_flag & flags:
default |= cares_flag result |= cares_flag
flags &= ~socket_flag return result
if not flags:
return default
raise gaierror(-1, "Bad value for ai_flags: 0x%x" % flags)
cpdef strerror(code): cpdef strerror(code):
...@@ -288,7 +256,6 @@ cdef void gevent_ares_host_callback(void *arg, int status, int timeouts, hostent ...@@ -288,7 +256,6 @@ cdef void gevent_ares_host_callback(void *arg, int status, int timeouts, hostent
except: except:
channel.loop.handle_error(callback, *sys.exc_info()) channel.loop.handle_error(callback, *sys.exc_info())
from cpython.version cimport PY_MAJOR_VERSION
cdef object _as_str(const char* val): cdef object _as_str(const char* val):
if not val: if not val:
...@@ -335,11 +302,12 @@ cdef int _make_sockaddr(const char* hostp, int port, int flowinfo, int scope_id, ...@@ -335,11 +302,12 @@ cdef int _make_sockaddr(const char* hostp, int port, int flowinfo, int scope_id,
cdef class channel: cdef class channel:
cdef public object loop
cdef ares_channeldata* channel cdef ares_channeldata* channel
cdef public dict _watchers
cdef public object _timer cdef readonly object loop
cdef dict _watchers
cdef object _timer
def __init__(self, object loop, flags=None, timeout=None, tries=None, ndots=None, def __init__(self, object loop, flags=None, timeout=None, tries=None, ndots=None,
udp_port=None, tcp_port=None, servers=None): udp_port=None, tcp_port=None, servers=None):
...@@ -347,26 +315,34 @@ cdef class channel: ...@@ -347,26 +315,34 @@ cdef class channel:
cdef cares.ares_options options cdef cares.ares_options options
memset(&options, 0, sizeof(cares.ares_options)) memset(&options, 0, sizeof(cares.ares_options))
cdef int optmask = cares.ARES_OPT_SOCK_STATE_CB cdef int optmask = cares.ARES_OPT_SOCK_STATE_CB
options.sock_state_cb = <void*>gevent_sock_state_callback options.sock_state_cb = <void*>gevent_sock_state_callback
options.sock_state_cb_data = <void*>self options.sock_state_cb_data = <void*>self
if flags is not None: if flags is not None:
options.flags = int(flags) options.flags = int(flags)
optmask |= cares.ARES_OPT_FLAGS optmask |= cares.ARES_OPT_FLAGS
if timeout is not None: if timeout is not None:
options.timeout = int(float(timeout) * 1000) options.timeout = int(float(timeout) * 1000)
optmask |= cares.ARES_OPT_TIMEOUTMS optmask |= cares.ARES_OPT_TIMEOUTMS
if tries is not None: if tries is not None:
options.tries = int(tries) options.tries = int(tries)
optmask |= cares.ARES_OPT_TRIES optmask |= cares.ARES_OPT_TRIES
if ndots is not None: if ndots is not None:
options.ndots = int(ndots) options.ndots = int(ndots)
optmask |= cares.ARES_OPT_NDOTS optmask |= cares.ARES_OPT_NDOTS
if udp_port is not None: if udp_port is not None:
options.udp_port = int(udp_port) options.udp_port = int(udp_port)
optmask |= cares.ARES_OPT_UDP_PORT optmask |= cares.ARES_OPT_UDP_PORT
if tcp_port is not None: if tcp_port is not None:
options.tcp_port = int(tcp_port) options.tcp_port = int(tcp_port)
optmask |= cares.ARES_OPT_TCP_PORT optmask |= cares.ARES_OPT_TCP_PORT
cdef int result = cares.ares_library_init(cares.ARES_LIB_INIT_ALL) # ARES_LIB_INIT_WIN32 -DUSE_WINSOCK? cdef int result = cares.ares_library_init(cares.ARES_LIB_INIT_ALL) # ARES_LIB_INIT_WIN32 -DUSE_WINSOCK?
if result: if result:
raise gaierror(result, strerror(result)) raise gaierror(result, strerror(result))
...@@ -539,9 +515,16 @@ cdef class channel: ...@@ -539,9 +515,16 @@ cdef class channel:
cares.ares_getnameinfo(self.channel, x, length, flags, <void*>gevent_ares_nameinfo_callback, <void*>arg) cares.ares_getnameinfo(self.channel, x, length, flags, <void*>gevent_ares_nameinfo_callback, <void*>arg)
def getnameinfo(self, object callback, tuple sockaddr, int flags): def getnameinfo(self, object callback, tuple sockaddr, int flags):
try: flags = _convert_cares_ni_flags(flags)
flags = _convert_cares_flags(flags)
except gaierror:
# The stdlib just ignores bad flags
flags = 0
return self._getnameinfo(callback, sockaddr, flags) return self._getnameinfo(callback, sockaddr, flags)
def getaddrinfo(self, object callback, const char* name,
const char* service, # AKA port
int family=0,
int type=0,
int proto=0,
int flags=0):
cdef cares.ares_addrinfo_hints hints;
memset(&hints, 0, sizeof(cares.ares_addrinfo_hints))
# c-ares supports a limited set of flags.
flags |= cares.ARES_AI_NOSORT # Do not attempt connections to the resolved address
...@@ -96,6 +96,11 @@ cdef extern from "ares.h": ...@@ -96,6 +96,11 @@ cdef extern from "ares.h":
void ares_cancel(void* channel) void ares_cancel(void* channel)
void ares_getnameinfo(void* channel, void* sa, int salen, int flags, void* callback, void *arg) void ares_getnameinfo(void* channel, void* sa, int salen, int flags, void* callback, void *arg)
# Added in 1.10
int ares_inet_pton(int af, const char *src, void *dst)
const char* ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size);
struct in_addr: struct in_addr:
pass pass
...@@ -113,6 +118,12 @@ cdef extern from "ares.h": ...@@ -113,6 +118,12 @@ cdef extern from "ares.h":
int ares_set_servers(void* channel, ares_addr_node *servers) int ares_set_servers(void* channel, ares_addr_node *servers)
# Added in 1.16
int ARES_AI_NOSORT
int ARES_AI_ENVHOSTS
int ARES_AI_CANONNAME
int ARES_AI_NUMERICSERV
struct ares_addrinfo_hints: struct ares_addrinfo_hints:
int ai_flags int ai_flags
int ai_family int ai_family
...@@ -149,6 +160,3 @@ cdef extern from "ares.h": ...@@ -149,6 +160,3 @@ cdef extern from "ares.h":
void *arg) void *arg)
void ares_freeaddrinfo(ares_addrinfo *ai) void ares_freeaddrinfo(ares_addrinfo *ai)
int ares_inet_pton(int af, const char *src, void *dst)
const char* ares_inet_ntop(int af, const void *src, char *dst, ares_socklen_t size);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment