[pypy-svn] r42108 - in pypy/dist/pypy/rlib: . test
afa at codespeak.net
afa at codespeak.net
Tue Apr 17 00:59:33 CEST 2007
Author: afa
Date: Tue Apr 17 00:59:32 2007
New Revision: 42108
Added:
pypy/dist/pypy/rlib/getaddrinfo.py
pypy/dist/pypy/rlib/getnameinfo.py
Modified:
pypy/dist/pypy/rlib/_rsocket_ctypes.py
pypy/dist/pypy/rlib/rsocket.py
pypy/dist/pypy/rlib/test/test_rsocket.py
Log:
rsocket progress: implementation of getnameinfo() and getaddrinfo() on Windows.
Some basic tests added, but no IPv6 support.
Modified: pypy/dist/pypy/rlib/_rsocket_ctypes.py
==============================================================================
--- pypy/dist/pypy/rlib/_rsocket_ctypes.py (original)
+++ pypy/dist/pypy/rlib/_rsocket_ctypes.py Tue Apr 17 00:59:32 2007
@@ -222,7 +222,9 @@
CConfig.servent = ctypes_platform.Struct('struct servent',
[('s_name', c_char_p),
- ('s_port', c_int)])
+ ('s_port', c_int),
+ ('s_proto', c_char_p),
+ ])
CConfig.protoent = ctypes_platform.Struct('struct protoent',
[('p_proto', c_int),
@@ -421,6 +423,10 @@
inet_ntop.argtypes = [c_int, c_void_p, c_char_p, socklen_t]
inet_ntop.restype = c_char_p
+inet_addr = socketdll.inet_addr
+inet_addr.argtypes = [c_char_p]
+inet_addr.restype = c_uint
+
socketaccept = socketdll.accept
socketaccept.argtypes = [c_int, sockaddr_ptr, POINTER(socklen_t)]
socketaccept.restype = c_int
Added: pypy/dist/pypy/rlib/getaddrinfo.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rlib/getaddrinfo.py Tue Apr 17 00:59:32 2007
@@ -0,0 +1,225 @@
+"""
+An RPython implementation of getaddrinfo() based on ctypes.
+This is a rewrite of the CPython source: Modules/getaddrinfo.c
+"""
+
+from ctypes import POINTER, sizeof, cast, pointer
+from pypy.rlib import _rsocket_ctypes as _c
+from pypy.rlib.rsocket import GAIError, CSocketError
+from pypy.rlib.rsocket import gethost_common, make_address
+
+# valid flags for addrinfo
+AI_MASK = (_c.AI_PASSIVE | _c.AI_CANONNAME | _c.AI_NUMERICHOST)
+
+GAI_ERRORS = [
+ (1, 'EAI_ADDRFAMILY', "address family for hostname not supported"),
+ (2, 'EAI_AGAIN', "temporary failure in name resolution"),
+ (3, 'EAI_BADFLAGS', "invalid value for ai_flags"),
+ (4, 'EAI_FAIL', "failure in name resolution"),
+ (5, 'EAI_FAMILY', "ai_family not supported"),
+ (6, 'EAI_MEMORY', "memory allocation failure"),
+ (7, 'EAI_NODATA', "no address associated with hostname"),
+ (8, 'EAI_NONAME', "hostname nor servname provided, or not known"),
+ (9, 'EAI_SERVICE', "servname not supported for ai_socktype"),
+ (10, 'EAI_SOCKTYPE', "ai_socktype not supported"),
+ (11, 'EAI_SYSTEM', "system error returned in errno"),
+ (12, 'EAI_BADHINTS', "invalid value for hints"),
+ (13, 'EAI_PROTOCOL', "resolved protocol is unknown."),
+ (14, 'EAI_MAX', "unknown error"),
+]
+
+GAI_ERROR_MESSAGES = {}
+
+for value, name, text in GAI_ERRORS:
+ globals()[name] = value
+ GAI_ERROR_MESSAGES[value] = text
+
+# Replacement function for rsocket.GAIError.get_msg
+def GAIError_getmsg(self):
+ return GAI_ERROR_MESSAGES[self.errno]
+
+# str.isdigit is not RPython, so provide our own
+def str_isdigit(name):
+ if name == "":
+ return False
+ for c in name:
+ if c not in "012345789":
+ return False
+ return True
+
+GAI_ANY = 0
+INADDR_NONE = 0xFFFFFFFF
+
+def getaddrinfo(hostname, servname,
+ family=_c.AF_UNSPEC, socktype=0,
+ protocol=0, flags=0,
+ address_to_fill=None):
+
+ if not hostname and not servname:
+ raise GAIError(EAI_NONAME)
+
+ # error checks for hints
+ if flags & ~AI_MASK:
+ raise GAIError(EAI_BADFLAGS)
+ if family not in (_c.AF_UNSPEC, _c.AF_INET):
+ raise GAIError(EAI_FAMILY)
+
+ if socktype == GAI_ANY:
+ if protocol == GAI_ANY:
+ pass
+ elif protocol == _c.IPPROTO_UDP:
+ socktype = _c.SOCK_DGRAM
+ elif protocol == _c.IPPROTO_TCP:
+ socktype = _c.SOCK_STREAM
+ else:
+ socktype = _c.SOCK_RAW
+
+ elif socktype == _c.SOCK_RAW:
+ pass
+ elif socktype == _c.SOCK_DGRAM:
+ if protocol not in (_c.IPPROTO_UDP, GAI_ANY):
+ raise GAIError(EAI_BADHINTS)
+ protocol = _c.IPPROTO_UDP
+ elif socktype == _c.SOCK_STREAM:
+ if protocol not in (_c.IPPROTO_TCP, GAI_ANY):
+ raise GAIError(EAI_BADHINTS)
+ protocol = _c.IPPROTO_TCP
+ else:
+ raise GAIError(EAI_SOCKTYPE)
+
+ port = GAI_ANY
+
+ # service port
+ if servname:
+ if str_isdigit(servname):
+ port = _c.htons(int(servname))
+ # On windows, python2.3 uses getattrinfo.c,
+ # python2.4 uses VC2003 implementation of getaddrinfo().
+ # if sys.version < (2, 4)
+ # socktype = _c.SOCK_DGRAM
+ # protocol = _c.IPPROTO_UDP
+ else:
+ if socktype == _c.SOCK_DGRAM:
+ proto = "udp"
+ elif socktype == _c.SOCK_STREAM:
+ proto = "tcp"
+ else:
+ proto = None
+
+ sp = _c.getservbyname(servname, proto)
+ if not sp:
+ raise GAIError(EAI_SERVICE)
+ port = sp.contents.s_port
+ if socktype == GAI_ANY:
+ if sp.contents.s_proto == "udp":
+ socktype = _c.SOCK_DGRAM
+ protocol = _c.IPPROTO_UDP
+ elif sp.contents.s_proto == "tcp":
+ socktype = _c.SOCK_STREAM
+ protocol = _c.IPPROTO_TCP
+ else:
+ raise GAIError(EAI_PROTOCOL)
+
+ # hostname == NULL
+ # passive socket -> anyaddr (0.0.0.0 or ::)
+ # non-passive socket -> localhost (127.0.0.1 or ::1)
+ if not hostname:
+ result = []
+ if family in (_c.AF_UNSPEC, _c.AF_INET):
+
+ sin = _c.sockaddr_in(sin_family=_c.AF_INET, sin_port=port)
+ if flags & _c.AI_PASSIVE:
+ sin.sin_addr.s_addr = 0x0 # addrany
+ else:
+ sin.sin_addr.s_addr = 0x0100007f # loopback
+
+ addr = make_address(cast(pointer(sin), POINTER(_c.sockaddr)),
+ sizeof(_c.sockaddr_in), address_to_fill)
+
+ result.append((_c.AF_INET, socktype, protocol, "", # xxx canonname meaningless? "anyaddr"
+ addr))
+
+ if not result:
+ raise GAIError(EAI_FAMILY)
+ return result
+
+ # hostname as numeric name
+ if family in (_c.AF_UNSPEC, _c.AF_INET):
+
+ packedaddr = _c.inet_addr(hostname)
+ if packedaddr != INADDR_NONE:
+
+ v4a = _c.ntohl(packedaddr)
+ if (v4a & 0xf0000000 == 0xe0000000 or # IN_MULTICAST()
+ v4a & 0xe0000000 == 0xe0000000): # IN_EXPERIMENTAL()
+ flags &= ~_c.AI_CANONNAME
+ v4a >>= 24 # = IN_CLASSA_NSHIFT
+ if v4a in (0, 127): # = IN_LOOPBACKNET
+ flags &= ~_c.AI_CANONNAME
+
+ if not flags & _c.AI_CANONNAME:
+ sin = _c.sockaddr_in(sin_family=_c.AF_INET, sin_port=port)
+ sin.sin_addr.s_addr = packedaddr
+ addr = make_address(cast(pointer(sin), POINTER(_c.sockaddr)),
+ sizeof(_c.sockaddr_in), address_to_fill)
+ return [(_c.AF_INET, socktype, protocol, None, addr)]
+ else:
+ # XXX getaddrinfo() is a name->address translation function,
+ # and it looks strange that we do addr->name translation here.
+ sin = _c.sockaddr_in(sin_family=_c.AF_INET, sin_port=port)
+
+ sin.sin_addr.s_addr = packedaddr
+
+ canonname = get_name(hostname, sin.sin_addr, sizeof(_c.in_addr))
+
+ addr = make_address(cast(pointer(sin), POINTER(_c.sockaddr)),
+ sizeof(_c.sockaddr_in), address_to_fill)
+ return [(_c.AF_INET, socktype, protocol, canonname, addr)]
+
+ if flags & _c.AI_NUMERICHOST:
+ raise GAIError(EAI_NONAME)
+
+ # hostname as alphabetical name
+ result = get_addr(hostname, socktype, protocol, port, address_to_fill)
+
+ if result:
+ return result
+
+ raise GAIError(EAI_FAIL)
+
+def get_name(hostname, addr, addrlen):
+ hostent = _c.gethostbyaddr(pointer(addr), addrlen, _c.AF_INET)
+
+ # if reverse lookup fail,
+ # return address anyway to pacify calling application.
+ if not hostent:
+ return hostname
+
+ hname, aliases, address_list = gethost_common("", hostent)
+ if hostent and hostent.contents.h_name and hostent.contents.h_addr_list[0]:
+ return hostent.contents.h_name
+ else:
+ return hostname
+
+def get_addr(hostname, socktype, protocol, port, address_to_fill):
+ hostent = _c.gethostbyname(hostname)
+
+ if not hostent:
+ raise GAIError(EAI_FAIL)
+
+ hname, aliases, address_list = gethost_common("", hostent)
+
+ result = []
+
+ for address in address_list:
+ if address.addr.sa_family == _c.AF_INET:
+ a = cast(pointer(address.addr), POINTER(_c.sockaddr_in)).contents
+ a.sin_port = port & 0xffff
+ addr = make_address(pointer(address.addr),address.addrlen,address_to_fill)
+ result.append((address.addr.sa_family,
+ socktype,
+ protocol,
+ "", # XXX canonname?
+ addr))
+
+ return result
Added: pypy/dist/pypy/rlib/getnameinfo.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rlib/getnameinfo.py Tue Apr 17 00:59:32 2007
@@ -0,0 +1,68 @@
+"""
+An RPython implementation of getnameinfo() based on ctypes.
+This is a rewrite of the CPython source: Modules/getaddrinfo.c
+"""
+from ctypes import POINTER, sizeof, cast, pointer
+from pypy.rlib import _rsocket_ctypes as _c
+from pypy.rlib.rsocket import RSocketError, GAIError
+
+NI_NOFQDN = 0x00000001
+NI_NUMERICHOST = 0x00000002
+NI_NAMEREQD = 0x00000004
+NI_NUMERICSERV = 0x00000008
+NI_DGRAM = 0x00000010
+
+def _getservicename(sin_port, flags):
+ if flags & NI_NUMERICSERV:
+ sp = None
+ elif flags & NI_DGRAM:
+ sp = _c.getservbyport(sin_port, "udp")
+ else:
+ sp = _c.getservbyport(sin_port, "tcp")
+
+ if sp:
+ serv = sp.contents.s_name
+ else:
+ serv = "%d" % _c.ntohs(sin_port)
+
+ return serv
+
+
+def getnameinfo(_addr, flags):
+ addr = _addr.addr
+
+ if addr.sa_family != _c.AF_INET:
+ raise RSocketError("unknown address family")
+
+ sockaddr = cast(pointer(addr), POINTER(_c.sockaddr_in)).contents
+ sin_port = sockaddr.sin_port
+ sin_addr = sockaddr.sin_addr
+
+ v4a = _c.ntohl(sin_addr.s_addr)
+ if (v4a & 0xf0000000 == 0xe0000000 or # IN_MULTICAST()
+ v4a & 0xe0000000 == 0xe0000000): # IN_EXPERIMENTAL()
+ flags |= NI_NUMERICHOST
+ # XXX Why does this work in CPython?
+ # v4a >>= 24 # = IN_CLASSA_NSHIFT
+ # if v4a in (0, 127): # = IN_LOOPBACKNET
+ # flags |= NI_NUMERICHOST
+ numsize = _c.INET_ADDRSTRLEN
+
+ serv = _getservicename(sin_port, flags)
+
+ if not (flags & NI_NUMERICHOST):
+ hostent = _c.gethostbyaddr(pointer(sin_addr), sizeof(_c.in_addr), addr.sa_family)
+ else:
+ hostent = None
+
+ if hostent:
+ from pypy.rlib.rsocket import gethost_common
+ host, _, _ = gethost_common("", hostent)
+ else:
+ from pypy.rlib.rsocket import copy_buffer
+ host = _c.inet_ntoa(sin_addr)
+ #buf = copy_buffer(ptr, len(ptr))
+ #host = buf.raw
+
+ return host, serv
+
Modified: pypy/dist/pypy/rlib/rsocket.py
==============================================================================
--- pypy/dist/pypy/rlib/rsocket.py (original)
+++ pypy/dist/pypy/rlib/rsocket.py Tue Apr 17 00:59:32 2007
@@ -87,21 +87,15 @@
family = result.family
if len(name) == 0:
- hints = _c.addrinfo(ai_family = family,
- ai_socktype = SOCK_DGRAM, # dummy
- ai_flags = AI_PASSIVE)
- res = _c.addrinfo_ptr()
- error = _c.getaddrinfo(None, "0", byref(hints), byref(res))
- if error:
- raise GAIError(error)
- try:
- info = res.contents
- if info.ai_next:
- raise RSocketError("wildcard resolved to "
- "multiple addresses")
- return make_address(info.ai_addr, info.ai_addrlen, result)
- finally:
- _c.freeaddrinfo(res)
+ info = getaddrinfo(None, "0",
+ family=family,
+ socktype=SOCK_DGRAM, # dummy
+ flags=AI_PASSIVE,
+ address_to_fill=result)
+ if len(info) > 1:
+ raise RSocketError("wildcard resolved to "
+ "multiple addresses")
+ return info[0][4]
# IPv4 also supports the special name "<broadcast>".
if name == '<broadcast>':
@@ -127,17 +121,8 @@
result)
# generic host name to IP conversion
- hints = _c.addrinfo(ai_family = family)
- res = _c.addrinfo_ptr()
- error = _c.getaddrinfo(name, None, byref(hints), byref(res))
- # PLAT EAI_NONAME
- if error:
- raise GAIError(error)
- try:
- info = res.contents
- return make_address(info.ai_addr, info.ai_addrlen, result)
- finally:
- _c.freeaddrinfo(res)
+ info = getaddrinfo(name, None, family=family, address_to_fill=result)
+ return info[0][4]
class IPAddress(Address):
"""AF_INET and AF_INET6 addresses"""
@@ -146,13 +131,8 @@
# Create a string object representing an IP address.
# For IPv4 this is always a string of the form 'dd.dd.dd.dd'
# (with variable size numbers).
- buf = create_string_buffer(NI_MAXHOST)
- error = _c.getnameinfo(byref(self.addr), self.addrlen,
- buf, NI_MAXHOST,
- None, 0, NI_NUMERICHOST)
- if error:
- raise GAIError(error)
- return buf.value
+ host, serv = getnameinfo(self, NI_NUMERICHOST | NI_NUMERICSERV)
+ return host
# ____________________________________________________________
@@ -895,11 +875,11 @@
makeipaddr(name, result)
return result
-def gethost_common(hostname, hostent, addr):
+def gethost_common(hostname, hostent, addr=None):
if not hostent:
raise HSocketError(hostname)
- family = addr.family
- if hostent.contents.h_addrtype != family:
+ family = hostent.contents.h_addrtype
+ if addr is not None and addr.family != family:
raise CSocketError(_c.EAFNOSUPPORT)
aliases = []
@@ -944,7 +924,8 @@
return gethost_common(ip, hostent, addr)
def getaddrinfo(host, port_or_service,
- family=AF_UNSPEC, socktype=0, proto=0, flags=0):
+ family=AF_UNSPEC, socktype=0, proto=0, flags=0,
+ address_to_fill=None):
# port_or_service is a string, not an int (but try str(port_number)).
assert port_or_service is None or isinstance(port_or_service, str)
hints = _c.addrinfo(ai_family = family,
@@ -961,7 +942,7 @@
p = res
while p:
info = p.contents
- addr = make_address(info.ai_addr, info.ai_addrlen)
+ addr = make_address(info.ai_addr, info.ai_addrlen, address_to_fill)
canonname = info.ai_canonname
if canonname is None:
canonname = ""
@@ -1060,3 +1041,17 @@
if timeout < 0.0:
timeout = -1.0
defaults.timeout = timeout
+
+# _______________________________________________________________
+#
+# Patch module, for platforms without getaddrinfo / getnameinfo
+#
+
+if not getattr(_c, 'getaddrinfo', None):
+ from pypy.rlib.getaddrinfo import getaddrinfo
+ from pypy.rlib.getaddrinfo import GAIError_getmsg
+ GAIError.get_msg = GAIError_getmsg
+
+if not getattr(_c, 'getnameinfo', None):
+ from pypy.rlib.getnameinfo import getnameinfo
+ from pypy.rlib.getnameinfo import NI_NUMERICHOST, NI_NUMERICSERV
Modified: pypy/dist/pypy/rlib/test/test_rsocket.py
==============================================================================
--- pypy/dist/pypy/rlib/test/test_rsocket.py (original)
+++ pypy/dist/pypy/rlib/test/test_rsocket.py Tue Apr 17 00:59:32 2007
@@ -180,6 +180,7 @@
addr.get_port() == 80):
found = True
assert found, lst
+ py.test.raises(GAIError, getaddrinfo, 'www.very-invalidaddress.com', None)
def test_getaddrinfo_snake():
lst = getaddrinfo('snake.cs.uni-duesseldorf.de', None)
@@ -190,6 +191,24 @@
found = True
assert found, lst
+def test_getaddrinfo_reverse_snake():
+ lst = getaddrinfo('134.99.112.214', None, flags=AI_CANONNAME)
+ assert isinstance(lst, list)
+ found = False
+ for family, socktype, protocol, canonname, addr in lst:
+ if canonname == 'snake.cs.uni-duesseldorf.de':
+ found = True
+ assert found, lst
+
+def test_getaddrinfo_reverse_lookup_fail():
+ lst = getaddrinfo('1.2.3.4', None, flags=AI_CANONNAME)
+ assert isinstance(lst, list)
+ found = False
+ for family, socktype, protocol, canonname, addr in lst:
+ if canonname == '1.2.3.4':
+ found = True
+ assert found, lst
+
def test_connect_ex():
s = RSocket()
err = s.connect_ex(s.getsockname()) # should not work
More information about the Pypy-commit
mailing list