[Jython-checkins] jython (merge 2.5 -> default): merge w/2.5: Fixes for getaddrinfo

alan.kennedy jython-checkins at python.org
Tue Aug 14 23:11:35 CEST 2012


http://hg.python.org/jython/rev/a423b9e08626
changeset:   6846:a423b9e08626
parent:      6844:d7e04f80af9a
parent:      6845:d56c4119fed1
user:        Alan Kennedy <alan at xhaus.com>
date:        Tue Aug 14 22:10:15 2012 +0100
summary:
  merge w/2.5: Fixes for getaddrinfo

files:
  Lib/socket.py           |  159 ++++++++++++++++++++-------
  Lib/test/test_socket.py |   79 +++++++++++--
  2 files changed, 181 insertions(+), 57 deletions(-)


diff --git a/Lib/socket.py b/Lib/socket.py
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -96,7 +96,7 @@
 def java_net_socketexception_handler(exc):
     if exc.message.startswith("Address family not supported by protocol family"):
         return _add_exception_attrs(error(errno.EAFNOSUPPORT, 
-                'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6addresssupport'))
+                'Address family not supported by protocol family: See http://wiki.python.org/jython/NewSocketModule#IPV6_address_support'))
     return _unmapped_exception(exc)
 
 def would_block_error(exc=None):
@@ -220,8 +220,28 @@
 SOCK_SEQPACKET = 5 # not supported
 
 SOL_SOCKET = 0xFFFF
-IPPROTO_TCP = 6
-IPPROTO_UDP = 17
+
+IPPROTO_AH       =  51 # not supported
+IPPROTO_DSTOPTS  =  60 # not supported
+IPPROTO_ESP      =  50 # not supported
+IPPROTO_FRAGMENT =  44 # not supported
+IPPROTO_GGP      =   3 # not supported
+IPPROTO_HOPOPTS  =   0 # not supported
+IPPROTO_ICMP     =   1 # not supported
+IPPROTO_ICMPV6   =  58 # not supported
+IPPROTO_IDP      =  22 # not supported
+IPPROTO_IGMP     =   2 # not supported
+IPPROTO_IP       =   0
+IPPROTO_IPV4     =   4 # not supported
+IPPROTO_IPV6     =  41 # not supported
+IPPROTO_MAX      = 256 # not supported
+IPPROTO_ND       =  77 # not supported
+IPPROTO_NONE     =  59 # not supported
+IPPROTO_PUP      =  12 # not supported
+IPPROTO_RAW      = 255 # not supported
+IPPROTO_ROUTING  =  43 # not supported
+IPPROTO_TCP      =   6
+IPPROTO_UDP      =  17
 
 SO_BROADCAST   = 1
 SO_KEEPALIVE   = 2
@@ -256,26 +276,58 @@
 SO_TYPE             = -1024
 SO_USELOOPBACK      = -2048
 
-__all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM',
-        'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET',
-        'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE',
-        'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY',
-        'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP',
-        'SocketType', 'error', 'herror', 'gaierror', 'timeout',
-        'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname',
-        'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout',
-        'has_ipv6', 'htons', 'htonl', 'ntohs', 'ntohl',
-        'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR',
-        ]
+__all__ = [
+    # Families
+    'AF_UNSPEC', 'AF_INET', 'AF_INET6', 
+    # getaddrinfo and getnameinfo flags
+    'AI_PASSIVE', 'AI_CANONNAME', 'AI_NUMERICHOST', 'AI_V4MAPPED',
+    'AI_ALL', 'AI_ADDRCONFIG', 'AI_NUMERICSERV', 'EAI_NONAME', 
+    'EAI_SERVICE', 'EAI_ADDRFAMILY',
+    'NI_NUMERICHOST', 'NI_NUMERICSERV', 'NI_NOFQDN', 'NI_NAMEREQD',
+    'NI_DGRAM', 'NI_MAXSERV', 'NI_IDN', 'NI_IDN_ALLOW_UNASSIGNED',
+    'NI_IDN_USE_STD3_ASCII_RULES', 'NI_MAXHOST',
+    # socket types
+    'SOCK_DGRAM', 'SOCK_STREAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET',
+    # levels
+    'SOL_SOCKET',
+    # protocols
+    'IPPROTO_AH', 'IPPROTO_DSTOPTS', 'IPPROTO_ESP', 'IPPROTO_FRAGMENT',
+    'IPPROTO_GGP', 'IPPROTO_HOPOPTS', 'IPPROTO_ICMP', 'IPPROTO_ICMPV6',
+    'IPPROTO_IDP', 'IPPROTO_IGMP', 'IPPROTO_IP', 'IPPROTO_IPV4',
+    'IPPROTO_IPV6', 'IPPROTO_MAX', 'IPPROTO_ND', 'IPPROTO_NONE',
+    'IPPROTO_PUP', 'IPPROTO_RAW', 'IPPROTO_ROUTING', 'IPPROTO_TCP', 
+    'IPPROTO_UDP',
+    # Special hostnames
+    'INADDR_ANY', 'INADDR_BROADCAST', 'IN6ADDR_ANY_INIT',
+    # support socket options
+    'SO_BROADCAST', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE',
+    'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY',
+    # unsupported socket options
+    'SO_ACCEPTCONN', 'SO_DEBUG', 'SO_DONTROUTE', 'SO_ERROR',
+    'SO_EXCLUSIVEADDRUSE', 'SO_RCVLOWAT', 'SO_RCVTIMEO', 'SO_REUSEPORT',
+    'SO_SNDLOWAT', 'SO_SNDTIMEO', 'SO_TYPE', 'SO_USELOOPBACK',
+    # functions
+    'getfqdn', 'gethostname', 'gethostbyname', 'gethostbyaddr',
+    'getservbyname', 'getservbyport', 'getprotobyname', 'getaddrinfo',
+    'getnameinfo', 'getdefaulttimeout', 'setdefaulttimeout', 'htons',
+    'htonl', 'ntohs', 'ntohl', 'inet_pton', 'inet_ntop', 'inet_aton',
+    'inet_ntoa', 'create_connection', 'socket', 'ssl',
+    # exceptions
+    'error', 'herror', 'gaierror', 'timeout', 'sslerror,
+    # classes
+    'SocketType', 
+    # Misc flags     
+    'has_ipv6', 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR',
+]
 
-def _constant_to_name(const_value):
+def _constant_to_name(const_value, expected_name_starts):
     sock_module = sys.modules['socket']
     try:
         for name in dir(sock_module):
-            if getattr(sock_module, name) is const_value and \
-                (name.startswith('SO_') or name.startswith('SOL_') or \
-                name.startswith('TCP_') or name.startswith('IPPROTO_')):
-                return name
+            if getattr(sock_module, name) is const_value:
+                for name_start in expected_name_starts:
+                    if name.startswith(name_start):
+                        return name
         return "Unknown"
     finally:
         sock_module = None
@@ -325,7 +377,8 @@
                 return struct.pack('ii', enabled, linger_time)
             return result
         else:
-            raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket)))
+            raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \
+                (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level, ['SOL_', 'IPPROTO_']), str(self.jsocket)))
 
     def setsockopt(self, level, option, value):
         if (level, option) in self.options:
@@ -335,7 +388,8 @@
             else:
                 getattr(self.jsocket, "set%s" % self.options[ (level, option) ])(value)
         else:
-            raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket)))
+            raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % \
+                (_constant_to_name(option, ['SO_', 'TCP_']), _constant_to_name(level,  ['SOL_', 'IPPROTO_']), str(self.jsocket)))
 
     def close(self):
         self.jsocket.close()
@@ -785,15 +839,18 @@
     error_message = "Address must be a 2-tuple (ipv4: (host, port)) or a 4-tuple (ipv6: (host, port, flow, scope))"
     if not isinstance(address_object, tuple) or \
             ((family == AF_INET and len(address_object) != 2) or (family == AF_INET6 and len(address_object) not in [2,4] )) or \
-            not isinstance(address_object[0], basestring) or \
+            not isinstance(address_object[0], (basestring, types.NoneType)) or \
             not isinstance(address_object[1], (int, long)):
         raise TypeError(error_message)
     if len(address_object) == 4 and not isinstance(address_object[3], (int, long)):
         raise TypeError(error_message)
-    hostname, port = address_object[0].strip(), address_object[1]
+    hostname = address_object[0]
+    if hostname is not None:
+        hostname = hostname.strip()
+    port = address_object[1]
     if family == AF_INET and sock_type == SOCK_DGRAM and hostname == "<broadcast>":
         hostname = INADDR_BROADCAST
-    if hostname == "":
+    if hostname in ["", None]:
         if flags & AI_PASSIVE:
             hostname = {AF_INET: INADDR_ANY, AF_INET6: IN6ADDR_ANY_INIT}[family]
         else:
@@ -815,9 +872,7 @@
     _ipv4_addresses_only = value
 
 def _getaddrinfo_get_host(host, family, flags):
-    if host is None:
-        return host
-    if not isinstance(host, basestring):
+    if not isinstance(host, basestring) and host is not None:
         raise TypeError("getaddrinfo() argument 1 must be string or None")
     if flags & AI_NUMERICHOST:
         if not is_ip_address(host):
@@ -850,7 +905,7 @@
         int_port = int(port)
     return int_port % 65536
 
-def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=0):
+def getaddrinfo(host, port, family=AF_UNSPEC, socktype=0, proto=0, flags=0):
     try:
         if _ipv4_addresses_only:
             family = AF_INET
@@ -858,29 +913,43 @@
             raise gaierror(errno.EIO, 'ai_family not supported')
         host = _getaddrinfo_get_host(host, family, flags)
         port = _getaddrinfo_get_port(port, flags)
+        if socktype not in [0, SOCK_DGRAM, SOCK_STREAM]:
+            raise error(errno.ESOCKTNOSUPPORT, "Socket type %s is not supported" % _constant_to_name(socktype, ['SOCK_']))
         filter_fns = []
         filter_fns.append({
             AF_INET:   lambda x: isinstance(x, java.net.Inet4Address),
             AF_INET6:  lambda x: isinstance(x, java.net.Inet6Address),
             AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress),
         }[family])
-        passive_mode = flags is not None and flags & AI_PASSIVE
-        canonname_mode = flags is not None and flags & AI_CANONNAME
+        if host in [None, ""]:
+            if flags & AI_PASSIVE:
+                 hosts = {AF_INET: [INADDR_ANY], AF_INET6: [IN6ADDR_ANY_INIT], AF_UNSPEC: [INADDR_ANY, IN6ADDR_ANY_INIT]}[family]
+            else:
+                 hosts = ["localhost"]
+        else:
+            hosts = [host]
         results = []
-        for a in java.net.InetAddress.getAllByName(host):
-            if len([f for f in filter_fns if f(a)]):
-                family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()]
-                if passive_mode and not canonname_mode:
-                    canonname = ""
-                else:
-                    canonname = asPyString(a.getCanonicalHostName())
-                if host is None and passive_mode and not canonname_mode:
-                    sockaddr = INADDR_ANY
-                else:
+        for h in hosts:
+            for a in java.net.InetAddress.getAllByName(h):
+                if len([f for f in filter_fns if f(a)]):
+                    family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()]
+                    if flags & AI_CANONNAME:
+                        canonname = asPyString(a.getCanonicalHostName())
+                    else:
+                        canonname = ""
                     sockaddr = asPyString(a.getHostAddress())
-                # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses
-                sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a)
-                results.append((family, socktype, proto, canonname, sock_tuple))
+                    # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses
+                    sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a)
+                    if socktype == 0:
+                        socktypes = [SOCK_DGRAM, SOCK_STREAM]
+                    else:
+                        socktypes = [socktype]
+                    for result_socktype in socktypes:
+                        result_proto = {SOCK_DGRAM: IPPROTO_UDP, SOCK_STREAM: IPPROTO_TCP}[result_socktype]
+                        if proto in [0, result_proto]:
+                            # The returned socket will only support the result_proto
+                            # If this does not match the requested proto, don't return it
+                            results.append((family, result_socktype, result_proto, canonname, sock_tuple))
         return results
     except java.lang.Exception, jlx:
         raise _map_exception(jlx)
@@ -1120,7 +1189,7 @@
         assert not self.sock_impl
         assert not self.local_addr
         # Do the address format check
-        _get_jsockaddr(addr, self.family, self.type, self.proto, 0)
+        _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE)
         self.local_addr = addr
 
     def listen(self, backlog):
@@ -1246,7 +1315,7 @@
             assert not self.sock_impl
             assert not self.local_addr
             # Do the address format check
-            _get_jsockaddr(addr, self.family, self.type, self.proto, 0)
+            _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE)
             self.local_addr = addr
             self.sock_impl = _datagram_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE), 
                                                     self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -277,8 +277,20 @@
 
     def testConstantToNameMapping(self):
         # Testing for mission critical constants
-        for name in ['SOL_SOCKET', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SO_BROADCAST', 'SO_KEEPALIVE', 'TCP_NODELAY', 'SO_ACCEPTCONN', 'SO_DEBUG']:
-            self.failUnlessEqual(socket._constant_to_name(getattr(socket, name)), name)
+        for name, expected_name_starts in [
+            ('IPPROTO_ICMP',  ['IPPROTO_']),
+            ('IPPROTO_TCP',   ['IPPROTO_']),
+            ('IPPROTO_UDP',   ['IPPROTO_']),
+            ('SO_BROADCAST',  ['SO_', 'TCP_']),
+            ('SO_KEEPALIVE',  ['SO_', 'TCP_']),
+            ('SO_ACCEPTCONN', ['SO_', 'TCP_']),
+            ('SO_DEBUG',      ['SO_', 'TCP_']),
+            ('SOCK_DGRAM',    ['SOCK_']),
+            ('SOCK_RAW',      ['SOCK_']),
+            ('SOL_SOCKET',    ['SOL_', 'IPPROTO_']),
+            ('TCP_NODELAY',   ['SO_', 'TCP_']),
+            ]:
+            self.failUnlessEqual(socket._constant_to_name(getattr(socket, name), expected_name_starts), name)
 
     def testHostnameRes(self):
         # Testing hostname resolution mechanisms
@@ -1638,6 +1650,46 @@
         else:
             self.fail("getaddrinfo with bad family should have raised exception")
 
+    def testBadSockType(self):
+        for socktype in [socket.SOCK_RAW, socket.SOCK_RDM, socket.SOCK_SEQPACKET]:
+            try:
+                socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype)
+            except socket.error, se:
+                self.failUnlessEqual(se[0], errno.ESOCKTNOSUPPORT)
+            except Exception, x:
+                self.fail("getaddrinfo with bad socktype raised wrong exception: %s" % x)
+            else:
+                self.fail("getaddrinfo with bad socktype should have raised exception")
+
+    def testBadSockTypeProtoCombination(self):
+        for socktype, proto in [
+            (socket.SOCK_STREAM, socket.IPPROTO_UDP),
+            (socket.SOCK_STREAM, socket.IPPROTO_ICMP),
+            (socket.SOCK_DGRAM,  socket.IPPROTO_TCP),
+            (socket.SOCK_DGRAM,  socket.IPPROTO_FRAGMENT),
+            ]:
+            try:
+                results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socktype, proto)
+                self.failUnless(len(results) == 0, "getaddrinfo with bad socktype/proto combo should not have returned results")
+            except Exception, x:
+                self.fail("getaddrinfo with bad socktype/proto combo should not have raised exception")
+
+    def testNoSockTypeWithProto(self):
+        for expect_results, proto in [
+            (True,  socket.IPPROTO_UDP),
+            (False, socket.IPPROTO_ICMP),
+            (True,  socket.IPPROTO_TCP),
+            (False, socket.IPPROTO_FRAGMENT),
+            ]:
+            try:
+                results = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, 0, proto)
+                if expect_results:
+                    self.failUnless(len(results) > 0, "getaddrinfo with no socktype and supported proto combo should have returned results")
+                else:
+                    self.failUnless(len(results) == 0, "getaddrinfo with no socktype and unsupported proto combo should not have returned results")
+            except Exception, x:
+                self.fail("getaddrinfo with no socktype (un)supported proto combo should not have raised exception")
+
     def testReturnsAreStrings(self):
         addrinfos = socket.getaddrinfo(HOST, PORT)
         for addrinfo in addrinfos:
@@ -1772,12 +1824,15 @@
             self.fail("getaddrinfo for unknown service name failed to raise exception")
 
     def testHostNames(self):
-        # None is always acceptable
-        for flags in [0, socket.AI_NUMERICHOST]:
+        # None is only acceptable if AI_NUMERICHOST is not specified
+        for flags, expect_exception in [(0, False), (socket.AI_NUMERICHOST, True)]:
             try:
                 socket.getaddrinfo(None, 80, 0, 0, 0, flags)
+                if expect_exception:
+                    self.fail("Non-numeric hostname == None should have raised exception")
             except Exception, x:
-                self.fail("hostname == None should not have raised exception: %s" % str(x))
+                if not expect_exception:
+                    self.fail("hostname == None should not have raised exception: %s" % str(x))
 
         # Check enforcement of AI_NUMERICHOST
         for host in ["", " ", "localhost"]:
@@ -1905,7 +1960,7 @@
             (socket.AF_INET,  ("localhost", 80), java.net.Inet4Address, ["127.0.0.1"]),
             (socket.AF_INET6, ("localhost", 80), java.net.Inet6Address, ["::1", "0:0:0:0:0:0:0:1"]),
             ]:
-            sockaddr = socket._get_jsockaddr(addr_tuple, family, None, 0, 0)
+            sockaddr = socket._get_jsockaddr(addr_tuple, family, 0, 0, 0)
             self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
             self.failUnless(isinstance(sockaddr.address, jaddress_type), "_get_jsockaddr returned wrong address type: '%s'(family=%d)" % (str(type(sockaddr.address)), family))
             self.failUnless(sockaddr.address.hostAddress in expected)
@@ -1916,7 +1971,7 @@
             ("localhost", 80),
             ("localhost", 80, 0, 0),
             ]:
-            sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, None, 0, 0)
+            sockaddr = socket._get_jsockaddr(addr_tuple, socket.AF_INET6, 0, 0, 0)
             self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
             self.failUnless(isinstance(sockaddr.address, java.net.Inet6Address), "_get_jsockaddr returned wrong address type: '%s'" % str(type(sockaddr.address)))
             self.failUnless(sockaddr.address.hostAddress in ["::1", "0:0:0:0:0:0:0:1"])
@@ -1925,10 +1980,10 @@
 
     def testSpecialHostnames(self):
         for family, sock_type, flags, addr_tuple, expected in [
-            ( socket.AF_INET,  None,              0,                 ("", 80),            ["localhost"]),
-            ( socket.AF_INET,  None,              socket.AI_PASSIVE, ("", 80),            [socket.INADDR_ANY]),
-            ( socket.AF_INET6, None,              0,                 ("", 80),            ["localhost"]),
-            ( socket.AF_INET6, None,              socket.AI_PASSIVE, ("", 80),            [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]),
+            ( socket.AF_INET,  0,                 0,                 ("", 80),            ["localhost"]),
+            ( socket.AF_INET,  0,                 socket.AI_PASSIVE, ("", 80),            [socket.INADDR_ANY]),
+            ( socket.AF_INET6, 0,                 0,                 ("", 80),            ["localhost"]),
+            ( socket.AF_INET6, 0,                 socket.AI_PASSIVE, ("", 80),            [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]),
             ( socket.AF_INET,  socket.SOCK_DGRAM, 0,                 ("<broadcast>", 80), [socket.INADDR_BROADCAST]),
             ]:
             sockaddr = socket._get_jsockaddr(addr_tuple, family, sock_type, 0, flags)
@@ -1941,7 +1996,7 @@
             ( socket.AF_INET6, 0,                 ["localhost"]),
             ( socket.AF_INET6, socket.AI_PASSIVE, [socket.IN6ADDR_ANY_INIT, "0:0:0:0:0:0:0:0"]),
             ]:
-            sockaddr = socket._get_jsockaddr(None, family, None, 0, flags)
+            sockaddr = socket._get_jsockaddr(None, family, 0, 0, flags)
             self.failUnless(sockaddr.hostName in expected, "_get_jsockaddr returned wrong hostname '%s' for sock tuple == None (family=%d)" % (sockaddr.hostName, family))
 
     def testBadAddressTuples(self):

-- 
Repository URL: http://hg.python.org/jython


More information about the Jython-checkins mailing list