3030 'cidr2block' ,
3131 'ip2long' ,
3232 'long2ip' ,
33+ 'long2rfc1924' ,
34+ 'rfc19242long' ,
3335 'validate_cidr' ,
3436 'validate_ip' ,
3537 'DOCUMENTATION_NETWORK' ,
5658)
5759
5860#: Regex for validating an IPv6 in hex notation
59- _HEX_RE = re .compile (r'^([0-9a-f ]{0,4}:){2,7}[0-9a-f ]{0,4}$' )
61+ _HEX_RE = re .compile (r'^([0-9a-fA-F ]{0,4}:){2,7}[0-9a-fA-F ]{0,4}$' )
6062
6163#: Regex for validating an IPv6 in dotted-quad notation
6264_DOTTED_QUAD_RE = re .compile (r'^([0-9a-f]{0,4}:){2,6}(\d{1,3}\.){0,3}\d{1,3}$' )
136138#: All DHCP servers and relay agents on the local site
137139MULTICAST_SITE_DHCP = "ff05::1:3"
138140
141+ #: RFC 1924 alphabet
142+ _RFC1924_ALPHABET = [
143+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
144+ 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' ,
145+ 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' ,
146+ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' ,
147+ 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
148+ '!' , '#' , '$' , '%' , '&' , '(' , ')' , '*' , '+' , '-' , ';' , '<' , '=' ,
149+ '>' , '?' , '@' , '^' , '_' , '`' , '{' , '|' , '}' , '~' ,
150+ ]
151+ #: RFC 1924 reverse lookup
152+ _RFC1924_REV = None
153+ #: Regex for validating an IPv6 in hex notation
154+ _RFC1924_RE = re .compile (r'^[0-9A-Za-z!#$%&()*+-;<=>?@^_`{|}~]{20}$' )
155+
139156
140157def validate_ip (s ):
141158 """Validate a hexidecimal IPv6 ip address.
@@ -167,6 +184,8 @@ def validate_ip(s):
167184 Traceback (most recent call last):
168185 ...
169186 TypeError: expected string or buffer
187+ >>> validate_ip('1080:0:0:0:8:800:200c:417a')
188+ True
170189
171190
172191 :param s: String to validate as a hexidecimal IPv6 ip address.
@@ -218,6 +237,9 @@ def ip2long(ip):
218237 True
219238 >>> ip2long('ff::ff::ff') == None
220239 True
240+ >>> expect = 21932261930451111902915077091070067066
241+ >>> ip2long('1080:0:0:0:8:800:200C:417A') == expect
242+ True
221243
222244
223245 :param ip: Hexidecimal IPv6 address
@@ -256,7 +278,7 @@ def ip2long(ip):
256278# end ip2long
257279
258280
259- def long2ip (l ):
281+ def long2ip (l , rfc1924 = False ):
260282 """Convert a network byte order 128-bit integer to a canonical IPv6
261283 address.
262284
@@ -281,17 +303,26 @@ def long2ip(l):
281303 Traceback (most recent call last):
282304 ...
283305 TypeError: expected int between 0 and <really big int> inclusive
306+ >>> long2ip(ip2long('1080::8:800:200C:417A'), rfc1924=True)
307+ '4)+k&C#VzJ4br>0wv%Yp'
308+ >>> long2ip(ip2long('::'), rfc1924=True)
309+ '00000000000000000000'
284310
285311
286312 :param l: Network byte order 128-bit integer.
287313 :type l: int
314+ :param rfc1924: Encode in RFC 1924 notation (base 85)
315+ :type rfc1924: bool
288316 :returns: Canonical IPv6 address (eg. '::1').
289317 :raises: TypeError
290318 """
291319 if MAX_IP < l or l < MIN_IP :
292320 raise TypeError (
293321 "expected int between %d and %d inclusive" % (MIN_IP , MAX_IP ))
294322
323+ if rfc1924 :
324+ return long2rfc1924 (l )
325+
295326 # format as one big hex value
296327 hex_str = '%032x' % l
297328 # split into double octet chunks without padding zeros
@@ -323,6 +354,72 @@ def long2ip(l):
323354# end long2ip
324355
325356
357+ def long2rfc1924 (l ):
358+ """Convert a network byte order 128-bit integer to an rfc1924 IPv6
359+ address.
360+
361+
362+ >>> long2rfc1924(ip2long('1080::8:800:200C:417A'))
363+ '4)+k&C#VzJ4br>0wv%Yp'
364+ >>> long2rfc1924(ip2long('::'))
365+ '00000000000000000000'
366+ >>> long2rfc1924(MAX_IP)
367+ '=r54lj&NUUO~Hi%c2ym0'
368+
369+
370+ :param l: Network byte order 128-bit integer.
371+ :type l: int
372+ :returns: RFC 1924 IPv6 address
373+ :raises: TypeError
374+ """
375+ if MAX_IP < l or l < MIN_IP :
376+ raise TypeError (
377+ "expected int between %d and %d inclusive" % (MIN_IP , MAX_IP ))
378+ o = []
379+ r = l
380+ while r > 85 :
381+ o .append (_RFC1924_ALPHABET [r % 85 ])
382+ r = r // 85
383+ o .append (_RFC1924_ALPHABET [r ])
384+ return '' .join (reversed (o )).zfill (20 )
385+
386+
387+ def rfc19242long (s ):
388+ """Convert an RFC 1924 IPv6 address to a network byte order 128-bit
389+ integer.
390+
391+
392+ >>> expect = 0
393+ >>> rfc19242long('00000000000000000000') == expect
394+ True
395+ >>> expect = 21932261930451111902915077091070067066
396+ >>> rfc19242long('4)+k&C#VzJ4br>0wv%Yp') == expect
397+ True
398+ >>> rfc19242long('pizza') == None
399+ True
400+ >>> rfc19242long('~~~~~~~~~~~~~~~~~~~~') == None
401+ True
402+ >>> rfc19242long('=r54lj&NUUO~Hi%c2ym0') == MAX_IP
403+ True
404+
405+
406+ :param ip: RFC 1924 IPv6 address
407+ :type ip: str
408+ :returns: Network byte order 128-bit integer or ``None`` if ip is invalid.
409+ """
410+ global _RFC1924_REV
411+ if not _RFC1924_RE .match (s ):
412+ return None
413+ if _RFC1924_REV is None :
414+ _RFC1924_REV = {v : k for k , v in enumerate (_RFC1924_ALPHABET )}
415+ l = 0
416+ for c in s :
417+ l = l * 85 + _RFC1924_REV [c ]
418+ if l > MAX_IP :
419+ return None
420+ return l
421+
422+
326423def validate_cidr (s ):
327424 """Validate a CIDR notation ip address.
328425
0 commit comments