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):
this implementation returns only the host name. This appears to be
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::
This module is considered extremely experimental on PyPy, and
......@@ -102,7 +106,7 @@ class Resolver(AbstractResolver):
.. _c-ares: http://c-ares.haxx.se
"""
ares_class = channel
cares_class = channel
def __init__(self, hub=None, use_environ=True, **kwargs):
if hub is None:
......@@ -114,27 +118,27 @@ class Resolver(AbstractResolver):
value = setting.get()
if value is not None:
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.params = kwargs
self.fork_watcher = hub.loop.fork(ref=False)
self.fork_watcher.start(self._on_fork)
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):
# NOTE: See comment in gevent.hub.reinit.
pid = os.getpid()
if pid != self.pid:
self.hub.loop.run_callback(self.ares.destroy)
self.ares = self.ares_class(self.hub.loop, **self.params)
self.hub.loop.run_callback(self.cares.destroy)
self.cares = self.cares_class(self.hub.loop, **self.params)
self.pid = pid
def close(self):
if self.ares is not None:
self.hub.loop.run_callback(self.ares.destroy)
self.ares = None
if self.cares is not None:
self.hub.loop.run_callback(self.cares.destroy)
self.cares = None
self.fork_watcher.stop()
def gethostbyname(self, hostname, family=AF_INET):
......@@ -154,7 +158,7 @@ class Resolver(AbstractResolver):
raise TypeError('Expected string, not %s' % type(hostname).__name__)
while True:
ares = self.ares
ares = self.cares
try:
waiter = Waiter(self.hub)
ares.gethostbyname(waiter, hostname, family)
......@@ -163,14 +167,14 @@ class Resolver(AbstractResolver):
raise gaierror(-5, 'No address associated with hostname')
return result
except gaierror:
if ares is self.ares:
if ares is self.cares:
if hostname == b'255.255.255.255':
# 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.
# HACK: So hardcode the expected return.
return ('255.255.255.255', [], ['255.255.255.255'])
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):
return lookup_port(port, socktype)
......@@ -200,17 +204,17 @@ class Resolver(AbstractResolver):
if proto:
socktype_proto = [(x, y) for (x, y) in socktype_proto if proto == y]
ares = self.ares
ares = self.cares
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_INET6)
elif family == AF_INET:
ares_values = Values(self.hub, 1)
ares_values = _Values(self.hub, 1)
ares.gethostbyname(ares_values, host, AF_INET)
elif family == AF_INET6:
ares_values = Values(self.hub, 1)
ares_values = _Values(self.hub, 1)
ares.gethostbyname(ares_values, host, AF_INET6)
else:
raise gaierror(5, 'ai_family not supported: %r' % (family, ))
......@@ -252,11 +256,11 @@ class Resolver(AbstractResolver):
def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0):
while True:
ares = self.ares
ares = self.cares
try:
return self._getaddrinfo(host, port, family, socktype, proto, flags)
except gaierror:
if ares is self.ares:
if ares is self.cares:
raise
def _gethostbyaddr(self, ip_address):
......@@ -273,7 +277,7 @@ class Resolver(AbstractResolver):
waiter = Waiter(self.hub)
try:
self.ares.gethostbyaddr(waiter, ip_address)
self.cares.gethostbyaddr(waiter, ip_address)
return waiter.get()
except InvalidIP:
result = self._getaddrinfo(ip_address, None, family=AF_UNSPEC, socktype=SOCK_DGRAM)
......@@ -285,17 +289,17 @@ class Resolver(AbstractResolver):
if _ip_address == ip_address:
raise
waiter.clear()
self.ares.gethostbyaddr(waiter, _ip_address)
self.cares.gethostbyaddr(waiter, _ip_address)
return waiter.get()
def gethostbyaddr(self, ip_address):
ip_address = _resolve_special(ip_address, AF_UNSPEC)
while True:
ares = self.ares
ares = self.cares
try:
return self._gethostbyaddr(ip_address)
except gaierror:
if ares is self.ares:
if ares is self.cares:
raise
def _getnameinfo(self, sockaddr, flags):
......@@ -329,7 +333,7 @@ class Resolver(AbstractResolver):
elif family == AF_INET6:
address = address[:2] + sockaddr[2:]
self.ares.getnameinfo(waiter, address, flags)
self.cares.getnameinfo(waiter, address, flags)
node, service = waiter.get()
if service is None:
......@@ -347,16 +351,18 @@ class Resolver(AbstractResolver):
def getnameinfo(self, sockaddr, flags):
while True:
ares = self.ares
ares = self.cares
try:
return self._getnameinfo(sockaddr, flags)
except gaierror:
if ares is self.ares:
if ares is self.cares:
raise
class Values(object):
# helper to collect multiple values; ignore errors unless nothing has succeeded
class _Values(object):
# 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?
__slots__ = ['count', 'values', 'error', 'waiter']
......
......@@ -7,6 +7,7 @@
cimport libcares as cares
import sys
from cpython.version cimport PY_MAJOR_VERSION
from cpython.tuple cimport PyTuple_Check
from cpython.getargs cimport PyArg_ParseTuple
from cpython.ref cimport Py_INCREF
......@@ -20,17 +21,17 @@ from _socket import gaierror
__all__ = ['channel']
cdef object string_types
cdef object text_type
cdef tuple string_types
cdef type text_type
if sys.version_info[0] >= 3:
if PY_MAJOR_VERSION >= 3:
string_types = str,
text_type = str
else:
string_types = __builtins__.basestring,
text_type = __builtins__.unicode
TIMEOUT = 1
DEF TIMEOUT = 1
DEF EV_READ = 1
DEF EV_WRITE = 2
......@@ -85,96 +86,63 @@ cdef extern from "ares.h":
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([
(cares.ARES_SUCCESS, 'ARES_SUCCESS'),
(cares.ARES_ENODATA, 'ARES_ENODATA'),
(cares.ARES_EFORMERR, 'ARES_EFORMERR'),
(cares.ARES_ESERVFAIL, 'ARES_ESERVFAIL'),
(cares.ARES_ENOTFOUND, 'ARES_ENOTFOUND'),
(cares.ARES_ENOTIMP, 'ARES_ENOTIMP'),
(cares.ARES_EREFUSED, 'ARES_EREFUSED'),
(cares.ARES_EBADQUERY, 'ARES_EBADQUERY'),
(cares.ARES_EBADNAME, 'ARES_EBADNAME'),
(cares.ARES_EBADFAMILY, 'ARES_EBADFAMILY'),
(cares.ARES_EBADRESP, 'ARES_EBADRESP'),
(cares.ARES_ECONNREFUSED, 'ARES_ECONNREFUSED'),
(cares.ARES_ETIMEOUT, 'ARES_ETIMEOUT'),
(cares.ARES_EOF, 'ARES_EOF'),
(cares.ARES_EFILE, 'ARES_EFILE'),
(cares.ARES_ENOMEM, 'ARES_ENOMEM'),
(cares.ARES_EDESTRUCTION, 'ARES_EDESTRUCTION'),
(cares.ARES_EBADSTR, 'ARES_EBADSTR'),
(cares.ARES_EBADFLAGS, 'ARES_EBADFLAGS'),
(cares.ARES_ENONAME, 'ARES_ENONAME'),
(cares.ARES_EBADHINTS, 'ARES_EBADHINTS'),
(cares.ARES_ENOTINITIALIZED, 'ARES_ENOTINITIALIZED'),
(cares.ARES_ELOADIPHLPAPI, 'ARES_ELOADIPHLPAPI'),
(cares.ARES_EADDRGETNETWORKPARAMS, 'ARES_EADDRGETNETWORKPARAMS'),
(cares.ARES_ECANCELLED, 'ARES_ECANCELLED')])
# maps c-ares flag to _socket module flag
_cares_flag_map = None
cdef _prepare_cares_flag_map():
global _cares_flag_map
(cares.ARES_SUCCESS, 'ARES_SUCCESS'),
(cares.ARES_ENODATA, 'ARES_ENODATA'),
(cares.ARES_EFORMERR, 'ARES_EFORMERR'),
(cares.ARES_ESERVFAIL, 'ARES_ESERVFAIL'),
(cares.ARES_ENOTFOUND, 'ARES_ENOTFOUND'),
(cares.ARES_ENOTIMP, 'ARES_ENOTIMP'),
(cares.ARES_EREFUSED, 'ARES_EREFUSED'),
(cares.ARES_EBADQUERY, 'ARES_EBADQUERY'),
(cares.ARES_EBADNAME, 'ARES_EBADNAME'),
(cares.ARES_EBADFAMILY, 'ARES_EBADFAMILY'),
(cares.ARES_EBADRESP, 'ARES_EBADRESP'),
(cares.ARES_ECONNREFUSED, 'ARES_ECONNREFUSED'),
(cares.ARES_ETIMEOUT, 'ARES_ETIMEOUT'),
(cares.ARES_EOF, 'ARES_EOF'),
(cares.ARES_EFILE, 'ARES_EFILE'),
(cares.ARES_ENOMEM, 'ARES_ENOMEM'),
(cares.ARES_EDESTRUCTION, 'ARES_EDESTRUCTION'),
(cares.ARES_EBADSTR, 'ARES_EBADSTR'),
(cares.ARES_EBADFLAGS, 'ARES_EBADFLAGS'),
(cares.ARES_ENONAME, 'ARES_ENONAME'),
(cares.ARES_EBADHINTS, 'ARES_EBADHINTS'),
(cares.ARES_ENOTINITIALIZED, 'ARES_ENOTINITIALIZED'),
(cares.ARES_ELOADIPHLPAPI, 'ARES_ELOADIPHLPAPI'),
(cares.ARES_EADDRGETNETWORKPARAMS, 'ARES_EADDRGETNETWORKPARAMS'),
(cares.ARES_ECANCELLED, 'ARES_ECANCELLED')
])
# maps c-ares getnameinfo() flag to _socket module flag
cdef list _cares_ni_flag_map = None
cdef _prepare_cares_ni_flag_map():
global _cares_ni_flag_map
import _socket
_cares_flag_map = [
(getattr(_socket, 'NI_NUMERICHOST', 1), cares.ARES_NI_NUMERICHOST),
(getattr(_socket, 'NI_NUMERICSERV', 2), cares.ARES_NI_NUMERICSERV),
(getattr(_socket, 'NI_NOFQDN', 4), cares.ARES_NI_NOFQDN),
(getattr(_socket, 'NI_NAMEREQD', 8), cares.ARES_NI_NAMEREQD),
(getattr(_socket, 'NI_DGRAM', 16), 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:
_prepare_cares_flag_map()
for socket_flag, cares_flag in _cares_flag_map:
_cares_ni_flag_map = [
(_socket.NI_NUMERICHOST, cares.ARES_NI_NUMERICHOST),
(_socket.NI_NUMERICSERV, cares.ARES_NI_NUMERICSERV),
(_socket.NI_NOFQDN, cares.ARES_NI_NOFQDN),
(_socket.NI_NAMEREQD, cares.ARES_NI_NAMEREQD),
(_socket.NI_DGRAM, cares.ARES_NI_DGRAM)
]
cdef _convert_cares_ni_flags(int flags,
int default=cares.ARES_NI_LOOKUPHOST|cares.ARES_NI_LOOKUPSERVICE):
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:
default |= cares_flag
flags &= ~socket_flag
if not flags:
return default
raise gaierror(-1, "Bad value for ai_flags: 0x%x" % flags)
result |= cares_flag
return result
cpdef strerror(code):
......@@ -288,7 +256,6 @@ cdef void gevent_ares_host_callback(void *arg, int status, int timeouts, hostent
except:
channel.loop.handle_error(callback, *sys.exc_info())
from cpython.version cimport PY_MAJOR_VERSION
cdef object _as_str(const char* val):
if not val:
......@@ -335,11 +302,12 @@ cdef int _make_sockaddr(const char* hostp, int port, int flowinfo, int scope_id,
cdef class channel:
cdef public object loop
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,
udp_port=None, tcp_port=None, servers=None):
......@@ -347,26 +315,34 @@ cdef class channel:
cdef cares.ares_options options
memset(&options, 0, sizeof(cares.ares_options))
cdef int optmask = cares.ARES_OPT_SOCK_STATE_CB
options.sock_state_cb = <void*>gevent_sock_state_callback
options.sock_state_cb_data = <void*>self
if flags is not None:
options.flags = int(flags)
optmask |= cares.ARES_OPT_FLAGS
if timeout is not None:
options.timeout = int(float(timeout) * 1000)
optmask |= cares.ARES_OPT_TIMEOUTMS
if tries is not None:
options.tries = int(tries)
optmask |= cares.ARES_OPT_TRIES
if ndots is not None:
options.ndots = int(ndots)
optmask |= cares.ARES_OPT_NDOTS
if udp_port is not None:
options.udp_port = int(udp_port)
optmask |= cares.ARES_OPT_UDP_PORT
if tcp_port is not None:
options.tcp_port = int(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?
if result:
raise gaierror(result, strerror(result))
......@@ -539,9 +515,16 @@ cdef class channel:
cares.ares_getnameinfo(self.channel, x, length, flags, <void*>gevent_ares_nameinfo_callback, <void*>arg)
def getnameinfo(self, object callback, tuple sockaddr, int flags):
try:
flags = _convert_cares_flags(flags)
except gaierror:
# The stdlib just ignores bad flags
flags = 0
flags = _convert_cares_ni_flags(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":
void ares_cancel(void* channel)
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:
pass
......@@ -113,6 +118,12 @@ cdef extern from "ares.h":
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:
int ai_flags
int ai_family
......@@ -149,6 +160,3 @@ cdef extern from "ares.h":
void *arg)
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