[Zconfig] SVN: ZConfig/trunk/ support for IPv6 addresses, contributed by Martin von L?\195?\182wis
Fred Drake
fdrake at gmail.com
Mon Sep 27 13:45:51 EDT 2010
Log message for revision 116987:
support for IPv6 addresses, contributed by Martin von L?\195?\182wis
Changed:
U ZConfig/trunk/NEWS.txt
U ZConfig/trunk/ZConfig/datatypes.py
U ZConfig/trunk/ZConfig/tests/test_datatypes.py
U ZConfig/trunk/doc/zconfig.tex
-=-
Modified: ZConfig/trunk/NEWS.txt
===================================================================
--- ZConfig/trunk/NEWS.txt 2010-09-27 17:41:29 UTC (rev 116986)
+++ ZConfig/trunk/NEWS.txt 2010-09-27 17:45:51 UTC (rev 116987)
@@ -7,6 +7,7 @@
--------------------------------
- Allow identical redefinition of ``%define`` names.
+- Added support for IPv6 addresses.
ZConfig 2.8.0 (2010-04-13)
Modified: ZConfig/trunk/ZConfig/datatypes.py
===================================================================
--- ZConfig/trunk/ZConfig/datatypes.py 2010-09-27 17:41:29 UTC (rev 116986)
+++ ZConfig/trunk/ZConfig/datatypes.py 2010-09-27 17:45:51 UTC (rev 116987)
@@ -177,9 +177,17 @@
host = ''
port = None
if ":" in s:
- host, s = s.split(":", 1)
- if s:
- port = port_number(s)
+ host, p = s.rsplit(":", 1)
+ if host.startswith('[') and host.endswith(']'):
+ # [IPv6]:port
+ host = host[1:-1]
+ elif ':' in host:
+ # Unbracketed IPv6 address;
+ # last part is not the port number
+ host = s
+ p = None
+ if p: # else leave port at None
+ port = port_number(p)
host = host.lower()
else:
try:
@@ -203,6 +211,14 @@
inet_binding_address = InetAddress("")
class SocketAddress:
+ # Parsing results in family and address
+ # Family can be AF_UNIX (for addresses that are path names)
+ # or AF_INET6 (for inet addresses with colons in them)
+ # or AF_INET (for all other inet addresses);
+ # An inet address is a (host, port) pair
+ # Notice that no DNS lookup is performed, so if the host
+ # is a DNS name, DNS lookup may end up with either IPv4 or
+ # IPv6 addresses, or both
def __init__(self, s):
import socket
if "/" in s or s.find(os.sep) >= 0:
@@ -211,6 +227,8 @@
else:
self.family = socket.AF_INET
self.address = self._parse_address(s)
+ if ':' in self.address[0]:
+ self.family = socket.AF_INET6
def _parse_address(self, s):
return inet_address(s)
@@ -237,15 +255,28 @@
# IP address regex from the Perl Cookbook, Recipe 6.23 (revised ed.)
# We allow underscores in hostnames although this is considered
# illegal according to RFC1034.
+ # Addition: IPv6 addresses are now also accepted
expr = (r"(^(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr
r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr cont'd
r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])\." #ipaddr cont'd
r"(\d|[01]?\d\d|2[0-4]\d|25[0-5])$)" #ipaddr cont'd
- r"|([A-Za-z_][-A-Za-z0-9_.]*[-A-Za-z0-9_])") # or hostname
+ r"|([A-Za-z_][-A-Za-z0-9_.]*[-A-Za-z0-9_])" # or hostname
+ r"|([0-9A-Fa-f:.]+:[0-9A-Fa-f:.]*)" # or superset of IPv6 addresses
+ # (requiring at least one colon)
+ )
RegularExpressionConversion.__init__(self, expr)
def __call__(self, value):
- return RegularExpressionConversion.__call__(self, value).lower()
+ result = RegularExpressionConversion.__call__(self, value).lower()
+ # Use C library to validate IPv6 addresses, in particular wrt.
+ # number of colons and number of digits per group
+ if ':' in result:
+ import socket
+ try:
+ socket.inet_pton(socket.AF_INET6, result)
+ except socket.error:
+ raise ValueError('%r is not a valid IPv6 address' % value)
+ return result
def existing_directory(v):
nv = os.path.expanduser(v)
Modified: ZConfig/trunk/ZConfig/tests/test_datatypes.py
===================================================================
--- ZConfig/trunk/ZConfig/tests/test_datatypes.py 2010-09-27 17:41:29 UTC (rev 116986)
+++ ZConfig/trunk/ZConfig/tests/test_datatypes.py 2010-09-27 17:45:51 UTC (rev 116987)
@@ -185,7 +185,9 @@
eq(convert("Host.Example.Com:80"), ("host.example.com", 80))
eq(convert(":80"), (defhost, 80))
eq(convert("80"), (defhost, 80))
+ eq(convert("[::1]:80"), ("::1", 80))
eq(convert("host.EXAMPLE.com"), ("host.example.com", None))
+ eq(convert("2001::ABCD"), ("2001::abcd", None))
self.assertRaises(ValueError, convert, "40 # foo")
def test_datatype_inet_binding_address(self):
@@ -258,6 +260,7 @@
convert = self.types.get("socket-address")
eq = self.assertEqual
AF_INET = socket.AF_INET
+ AF_INET6 = socket.AF_INET6
defhost = ZConfig.datatypes.DEFAULT_HOST
def check(value, family, address, self=self, convert=convert):
@@ -269,6 +272,8 @@
check(":80", AF_INET, (defhost, 80))
check("80", AF_INET, (defhost, 80))
check("host.EXAMPLE.com", AF_INET, ("host.example.com",None))
+ check("::1", AF_INET6,("::1", None))
+ check("[::]:80", AF_INET6,("::", 80))
a1 = convert("/tmp/var/@345.4")
a2 = convert("/tmp/var/@345.4:80")
self.assertEqual(a1.address, "/tmp/var/@345.4")
@@ -291,11 +296,16 @@
eq(convert('HOSTNAME.COM'), 'hostname.com')
eq(convert('WWW.HOSTNAME.COM'), 'www.hostname.com')
eq(convert('127.0.0.1'), '127.0.0.1')
+ eq(convert('::1'), '::1')
+ eq(convert('2001:DB8:1234:4567:89AB:cdef:0:1'), '2001:db8:1234:4567:89ab:cdef:0:1')
+ eq(convert('2001:DB8:1234:4567::10.11.12.13'), '2001:db8:1234:4567::10.11.12.13')
raises(ValueError, convert, '1hostnamewithleadingnumeric')
raises(ValueError, convert, '255.255')
raises(ValueError, convert, '12345678')
raises(ValueError, convert, '999.999.999.999')
raises(ValueError, convert, 'a!badhostname')
+ raises(ValueError, convert, '2001:DB8:0123:4567:89AB:cdef:0:1:2')
+ raises(ValueError, convert, '2001:DB8:0123:4567::10.11.12.13.14')
def test_existing_directory(self):
convert = self.types.get('existing-directory')
Modified: ZConfig/trunk/doc/zconfig.tex
===================================================================
--- ZConfig/trunk/doc/zconfig.tex 2010-09-27 17:41:29 UTC (rev 116986)
+++ ZConfig/trunk/doc/zconfig.tex 2010-09-27 17:45:51 UTC (rev 116987)
@@ -905,7 +905,9 @@
will be returned for \var{hostname}. The default host is
\code{localhost} on Windows and the empty string on all other
platforms. If the port is omitted, \code{None} will be returned for
- \var{port}.
+ \var{port}. IPv6 addresses can be specified in colon-separated notation;
+ if both host and port need to be specified, the bracketed form
+ (\code{[addr]:port}) must be used.
\term{\datatype{inet-binding-address}}
An Internet address expressed as a \code{(\var{hostname},
@@ -931,7 +933,8 @@
Validates a valid IP address or hostname. If the first
character is a digit, the value is assumed to be an IP
address. If the first character is not a digit, the value
- is assumed to be a hostname. Hostnames are converted to lower
+ is assumed to be a hostname. Strings containing colons are
+ considered IPv6 address. Hostnames are converted to lower
case.
\term{\datatype{locale}}
More information about the ZConfig
mailing list