[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