[Pkg-privacy-commits] [pyptlib] 62/136: Be able to parse IPv6 addresses.

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:25:08 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository pyptlib.

commit 8b5ac8495e9563d8255284bdfd43ae5737250980
Author: George Kadianakis <desnacked at riseup.net>
Date:   Tue Jan 15 16:23:40 2013 +0200

    Be able to parse IPv6 addresses.
    
    Reuse code from flashproxy.
---
 pyptlib/server_config.py    | 40 +++++++--------------
 pyptlib/test/test_server.py |  2 +-
 pyptlib/test/test_util.py   | 34 ++++++++++++++++++
 pyptlib/util.py             | 85 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 133 insertions(+), 28 deletions(-)

diff --git a/pyptlib/server_config.py b/pyptlib/server_config.py
index 4ceb3f4..ba0fa63 100644
--- a/pyptlib/server_config.py
+++ b/pyptlib/server_config.py
@@ -6,6 +6,7 @@ Low-level parts of pyptlib that are only useful to servers.
 """
 
 import pyptlib.config as config
+import pyptlib.util as util
 
 class ServerConfig(config.Config):
     """
@@ -56,7 +57,13 @@ class ServerConfig(config.Config):
         bindaddrs = self.get('TOR_PT_SERVER_BINDADDR').split(',')
         for bindaddr in bindaddrs:
             (transport_name, addrport) = bindaddr.split('-')
-            (addr, port) = self.get_addrport_from_string(addrport)
+
+            try:
+                (addr, port) = util.parse_addr_spec(addrport)
+            except ValueError, err:
+                self.writeEnvError(err)
+                raise config.EnvError(err)
+
             self.serverBindAddr[transport_name] = (addr, port)
 
         # Get transports.
@@ -148,30 +155,9 @@ class ServerConfig(config.Config):
         """
 
         string = self.get(key)
-        return self.get_addrport_from_string(string)
-
-    def get_addrport_from_string(self, string):
-        """
-        Parse a string holding an address:port value.
-
-        :param str string: A string.
-
-        :returns: tuple -- (address,port)
-
-        :raises: :class:`pyptlib.config.EnvError` if string was not in address:port format.
-        """
-
-        addrport = string.split(':')
-
-        if (len(addrport) != 2) or (not addrport[1].isdigit()):
-            message = 'Parsing error (%s).' % (string)
-            self.writeEnvError(message)
-            raise config.EnvError(message) # XXX maybe return ValueError
-
-        if (not 0 <= int(addrport[1]) < 65536):
-            message = 'Port out of range (%s).' % (string)
-            self.writeEnvError(message)
-            raise config.EnvError(message)
-
-        return addrport
+        try:
+            return util.parse_addr_spec(string)
+        except ValueError, err:
+            self.writeEnvError(err)
+            raise config.EnvError(err)
 
diff --git a/pyptlib/test/test_server.py b/pyptlib/test/test_server.py
index e5896e8..a73fb8c 100644
--- a/pyptlib/test/test_server.py
+++ b/pyptlib/test/test_server.py
@@ -176,7 +176,7 @@ class testServer(unittest.TestCase):
         os.environ = TEST_ENVIRON
         retval = pyptlib.server.init(["what"])
         self.assertEquals(retval['auth_cookie_file'], '/lulzie')
-        self.assertEquals(retval['ext_orport'], ['127.0.0.1', '5555'])
+        self.assertEquals(retval['ext_orport'], ('127.0.0.1', 5555))
 
     def test_ext_or_but_no_auth_cookie(self):
         """TOR_PT_EXTENDED_SERVER_PORT without TOR_PT_AUTH_COOKIE_FILE."""
diff --git a/pyptlib/test/test_util.py b/pyptlib/test/test_util.py
new file mode 100644
index 0000000..9b4e809
--- /dev/null
+++ b/pyptlib/test/test_util.py
@@ -0,0 +1,34 @@
+import unittest
+
+import pyptlib.util
+
+# Tests borrowed from flashproxy.
+class ParseAddrSpecTest(unittest.TestCase):
+    def test_ipv4(self):
+        self.assertEqual(pyptlib.util.parse_addr_spec("192.168.0.1:9999"), ("192.168.0.1", 9999))
+
+    def test_ipv6(self):
+        self.assertEqual(pyptlib.util.parse_addr_spec("[12::34]:9999"), ("12::34", 9999))
+
+    def test_defhost_defport_ipv4(self):
+        self.assertEqual(pyptlib.util.parse_addr_spec("192.168.0.2:8888", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 8888))
+        self.assertEqual(pyptlib.util.parse_addr_spec("192.168.0.2:", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec("192.168.0.2", defhost="192.168.0.1", defport=9999), ("192.168.0.2", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec(":8888", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 8888))
+        self.assertEqual(pyptlib.util.parse_addr_spec(":", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec("", defhost="192.168.0.1", defport=9999), ("192.168.0.1", 9999))
+
+    def test_defhost_defport_ipv6(self):
+        self.assertEqual(pyptlib.util.parse_addr_spec("[1234::2]:8888", defhost="1234::1", defport=9999), ("1234::2", 8888))
+        self.assertEqual(pyptlib.util.parse_addr_spec("[1234::2]:", defhost="1234::1", defport=9999), ("1234::2", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec("[1234::2]", defhost="1234::1", defport=9999), ("1234::2", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec(":8888", defhost="1234::1", defport=9999), ("1234::1", 8888))
+        self.assertEqual(pyptlib.util.parse_addr_spec(":", defhost="1234::1", defport=9999), ("1234::1", 9999))
+        self.assertEqual(pyptlib.util.parse_addr_spec("", defhost="1234::1", defport=9999), ("1234::1", 9999))
+
+    def test_noresolve(self):
+        """Test that parse_addr_spec does not do DNS resolution by default."""
+        self.assertRaises(ValueError, pyptlib.util.parse_addr_spec, "example.com")
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/pyptlib/util.py b/pyptlib/util.py
index 9b75786..9966625 100644
--- a/pyptlib/util.py
+++ b/pyptlib/util.py
@@ -5,6 +5,9 @@
 Utility functions.
 """
 
+import re
+import socket
+
 from pyptlib.config import Config, EnvError
 
 def checkClientMode(): # XXX WTF!???! This also exists in config.py.
@@ -19,4 +22,86 @@ def checkClientMode(): # XXX WTF!???! This also exists in config.py.
     except EnvError:
         return False
 
+# This code is borrowed from flashproxy. Thanks David!
+def parse_addr_spec(spec, defhost = None, defport = None, resolve = False):
+    """
+    Parse a host:port specification and return a 2-tuple ("host", port) as
+    understood by the Python socket functions.
+
+    If resolve is true, then the host in the specification or the defhost may be
+    a domain name, which will be resolved. If resolve is false, then the host
+    must be a numeric IPv4 or IPv6 address.
+
+    IPv6 addresses must be enclosed in square brackets.
+
+    :returns: tuple -- (address, port)
+
+    :raises: ValueError if spec is not well formed.
+
+    >>> parse_addr_spec("192.168.0.1:9999")
+    ('192.168.0.1', 9999)
+
+    If defhost or defport are given, those parts of the specification may be
+    omitted; if so, they will be filled in with defaults.
+    >>> parse_addr_spec("192.168.0.2:8888", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.2', 8888)
+    >>> parse_addr_spec(":8888", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.1', 8888)
+    >>> parse_addr_spec("192.168.0.2", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.2', 9999)
+    >>> parse_addr_spec("192.168.0.2:", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.2', 9999)
+    >>> parse_addr_spec(":", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.1', 9999)
+    >>> parse_addr_spec("", defhost="192.168.0.1", defport=9999)
+    ('192.168.0.1', 9999)
+    """
+    host = None
+    port = None
+    af = 0
+    m = None
+    # IPv6 syntax.
+    if not m:
+        m = re.match(ur'^\[(.+)\]:(\d*)$', spec)
+        if m:
+            host, port = m.groups()
+            af = socket.AF_INET6
+    if not m:
+        m = re.match(ur'^\[(.+)\]$', spec)
+        if m:
+            host, = m.groups()
+            af = socket.AF_INET6
+    # IPv4/hostname/port-only syntax.
+    if not m:
+        try:
+            host, port = spec.split(":", 1)
+        except ValueError:
+            host = spec
+        if re.match(ur'^[\d.]+$', host):
+            af = socket.AF_INET
+        else:
+            af = 0
+    host = host or defhost
+    port = port or defport
+    if host is None or port is None:
+        raise ValueError("Bad address specification \"%s\"" % spec)
+
+    # Now we have split around the colon and have a guess at the address family.
+    # Forward-resolve the name into an addrinfo struct. Real DNS resolution is
+    # done only if resolve is true; otherwise the address must be numeric.
+    if resolve:
+        flags = 0
+    else:
+        flags = socket.AI_NUMERICHOST
+    try:
+        addrs = socket.getaddrinfo(host, port, af, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags)
+    except socket.gaierror, e:
+        raise ValueError("Bad host or port: \"%s\" \"%s\": %s" % (host, port, str(e)))
+    if not addrs:
+        raise ValueError("Bad host or port: \"%s\" \"%s\"" % (host, port))
 
+    # Convert the result of socket.getaddrinfo (which is a 2-tuple for IPv4 and
+    # a 4-tuple for IPv6) into a (host, port) 2-tuple.
+    host, port = socket.getnameinfo(addrs[0][4], socket.NI_NUMERICHOST | socket.NI_NUMERICSERV)
+    port = int(port)
+    return host, port

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pyptlib.git



More information about the Pkg-privacy-commits mailing list