@@ -310,7 +310,7 @@ def collapse_addresses(addresses):
310
310
[IPv4Network('192.0.2.0/24')]
311
311
312
312
Args:
313
- addresses: An iterator of IPv4Network or IPv6Network objects.
313
+ addresses: An iterable of IPv4Network or IPv6Network objects.
314
314
315
315
Returns:
316
316
An iterator of the collapsed IPv(4|6)Network objects.
@@ -734,7 +734,7 @@ def __eq__(self, other):
734
734
return NotImplemented
735
735
736
736
def __hash__ (self ):
737
- return hash (int (self .network_address ) ^ int (self .netmask ))
737
+ return hash (( int (self .network_address ), int (self .netmask ) ))
738
738
739
739
def __contains__ (self , other ):
740
740
# always false if one is v4 and the other is v6.
@@ -1086,7 +1086,11 @@ def is_private(self):
1086
1086
"""
1087
1087
return any (self .network_address in priv_network and
1088
1088
self .broadcast_address in priv_network
1089
- for priv_network in self ._constants ._private_networks )
1089
+ for priv_network in self ._constants ._private_networks ) and all (
1090
+ self .network_address not in network and
1091
+ self .broadcast_address not in network
1092
+ for network in self ._constants ._private_networks_exceptions
1093
+ )
1090
1094
1091
1095
@property
1092
1096
def is_global (self ):
@@ -1333,18 +1337,41 @@ def is_reserved(self):
1333
1337
@property
1334
1338
@functools .lru_cache ()
1335
1339
def is_private (self ):
1336
- """Test if this address is allocated for private networks.
1340
+ """``True`` if the address is defined as not globally reachable by
1341
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1342
+ (for IPv6) with the following exceptions:
1337
1343
1338
- Returns:
1339
- A boolean, True if the address is reserved per
1340
- iana-ipv4-special-registry.
1344
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
1345
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1346
+ semantics of the underlying IPv4 addresses and the following condition holds
1347
+ (see :attr:`IPv6Address.ipv4_mapped`)::
1341
1348
1349
+ address.is_private == address.ipv4_mapped.is_private
1350
+
1351
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
1352
+ IPv4 range where they are both ``False``.
1342
1353
"""
1343
- return any (self in net for net in self ._constants ._private_networks )
1354
+ return (
1355
+ any (self in net for net in self ._constants ._private_networks )
1356
+ and all (self not in net for net in self ._constants ._private_networks_exceptions )
1357
+ )
1344
1358
1345
1359
@property
1346
1360
@functools .lru_cache ()
1347
1361
def is_global (self ):
1362
+ """``True`` if the address is defined as globally reachable by
1363
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1364
+ (for IPv6) with the following exception:
1365
+
1366
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1367
+ semantics of the underlying IPv4 addresses and the following condition holds
1368
+ (see :attr:`IPv6Address.ipv4_mapped`)::
1369
+
1370
+ address.is_global == address.ipv4_mapped.is_global
1371
+
1372
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
1373
+ IPv4 range where they are both ``False``.
1374
+ """
1348
1375
return self not in self ._constants ._public_network and not self .is_private
1349
1376
1350
1377
@property
@@ -1389,6 +1416,16 @@ def is_link_local(self):
1389
1416
"""
1390
1417
return self in self ._constants ._linklocal_network
1391
1418
1419
+ @property
1420
+ def ipv6_mapped (self ):
1421
+ """Return the IPv4-mapped IPv6 address.
1422
+
1423
+ Returns:
1424
+ The IPv4-mapped IPv6 address per RFC 4291.
1425
+
1426
+ """
1427
+ return IPv6Address (f'::ffff:{ self } ' )
1428
+
1392
1429
1393
1430
class IPv4Interface (IPv4Address ):
1394
1431
@@ -1548,13 +1585,15 @@ class _IPv4Constants:
1548
1585
1549
1586
_public_network = IPv4Network ('100.64.0.0/10' )
1550
1587
1588
+ # Not globally reachable address blocks listed on
1589
+ # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
1551
1590
_private_networks = [
1552
1591
IPv4Network ('0.0.0.0/8' ),
1553
1592
IPv4Network ('10.0.0.0/8' ),
1554
1593
IPv4Network ('127.0.0.0/8' ),
1555
1594
IPv4Network ('169.254.0.0/16' ),
1556
1595
IPv4Network ('172.16.0.0/12' ),
1557
- IPv4Network ('192.0.0.0/29 ' ),
1596
+ IPv4Network ('192.0.0.0/24 ' ),
1558
1597
IPv4Network ('192.0.0.170/31' ),
1559
1598
IPv4Network ('192.0.2.0/24' ),
1560
1599
IPv4Network ('192.168.0.0/16' ),
@@ -1565,6 +1604,11 @@ class _IPv4Constants:
1565
1604
IPv4Network ('255.255.255.255/32' ),
1566
1605
]
1567
1606
1607
+ _private_networks_exceptions = [
1608
+ IPv4Network ('192.0.0.9/32' ),
1609
+ IPv4Network ('192.0.0.10/32' ),
1610
+ ]
1611
+
1568
1612
_reserved_network = IPv4Network ('240.0.0.0/4' )
1569
1613
1570
1614
_unspecified_address = IPv4Address ('0.0.0.0' )
@@ -1630,8 +1674,18 @@ def _ip_int_from_string(cls, ip_str):
1630
1674
"""
1631
1675
if not ip_str :
1632
1676
raise AddressValueError ('Address cannot be empty' )
1633
-
1634
- parts = ip_str .split (':' )
1677
+ if len (ip_str ) > 45 :
1678
+ shorten = ip_str
1679
+ if len (shorten ) > 100 :
1680
+ shorten = f'{ ip_str [:45 ]} ({ len (ip_str )- 90 } chars elided){ ip_str [- 45 :]} '
1681
+ raise AddressValueError (f"At most 45 characters expected in "
1682
+ f"{ shorten !r} " )
1683
+
1684
+ # We want to allow more parts than the max to be 'split'
1685
+ # to preserve the correct error message when there are
1686
+ # too many parts combined with '::'
1687
+ _max_parts = cls ._HEXTET_COUNT + 1
1688
+ parts = ip_str .split (':' , maxsplit = _max_parts )
1635
1689
1636
1690
# An IPv6 address needs at least 2 colons (3 parts).
1637
1691
_min_parts = 3
@@ -1651,7 +1705,6 @@ def _ip_int_from_string(cls, ip_str):
1651
1705
# An IPv6 address can't have more than 8 colons (9 parts).
1652
1706
# The extra colon comes from using the "::" notation for a single
1653
1707
# leading or trailing zero part.
1654
- _max_parts = cls ._HEXTET_COUNT + 1
1655
1708
if len (parts ) > _max_parts :
1656
1709
msg = "At most %d colons permitted in %r" % (_max_parts - 1 , ip_str )
1657
1710
raise AddressValueError (msg )
@@ -1923,8 +1976,49 @@ def __init__(self, address):
1923
1976
1924
1977
self ._ip = self ._ip_int_from_string (addr_str )
1925
1978
1979
+ def _explode_shorthand_ip_string (self ):
1980
+ ipv4_mapped = self .ipv4_mapped
1981
+ if ipv4_mapped is None :
1982
+ return super ()._explode_shorthand_ip_string ()
1983
+ prefix_len = 30
1984
+ raw_exploded_str = super ()._explode_shorthand_ip_string ()
1985
+ return f"{ raw_exploded_str [:prefix_len ]} { ipv4_mapped !s} "
1986
+
1987
+ def _reverse_pointer (self ):
1988
+ ipv4_mapped = self .ipv4_mapped
1989
+ if ipv4_mapped is None :
1990
+ return super ()._reverse_pointer ()
1991
+ prefix_len = 30
1992
+ raw_exploded_str = super ()._explode_shorthand_ip_string ()[:prefix_len ]
1993
+ # ipv4 encoded using hexadecimal nibbles instead of decimals
1994
+ ipv4_int = ipv4_mapped ._ip
1995
+ reverse_chars = f"{ raw_exploded_str } { ipv4_int :008x} " [::- 1 ].replace (':' , '' )
1996
+ return '.' .join (reverse_chars ) + '.ip6.arpa'
1997
+
1998
+ def _ipv4_mapped_ipv6_to_str (self ):
1999
+ """Return convenient text representation of IPv4-mapped IPv6 address
2000
+
2001
+ See RFC 4291 2.5.5.2, 2.2 p.3 for details.
2002
+
2003
+ Returns:
2004
+ A string, 'x:x:x:x:x:x:d.d.d.d', where the 'x's are the hexadecimal values of
2005
+ the six high-order 16-bit pieces of the address, and the 'd's are
2006
+ the decimal values of the four low-order 8-bit pieces of the
2007
+ address (standard IPv4 representation) as defined in RFC 4291 2.2 p.3.
2008
+
2009
+ """
2010
+ ipv4_mapped = self .ipv4_mapped
2011
+ if ipv4_mapped is None :
2012
+ raise AddressValueError ("Can not apply to non-IPv4-mapped IPv6 address %s" % str (self ))
2013
+ high_order_bits = self ._ip >> 32
2014
+ return "%s:%s" % (self ._string_from_ip_int (high_order_bits ), str (ipv4_mapped ))
2015
+
1926
2016
def __str__ (self ):
1927
- ip_str = super ().__str__ ()
2017
+ ipv4_mapped = self .ipv4_mapped
2018
+ if ipv4_mapped is None :
2019
+ ip_str = super ().__str__ ()
2020
+ else :
2021
+ ip_str = self ._ipv4_mapped_ipv6_to_str ()
1928
2022
return ip_str + '%' + self ._scope_id if self ._scope_id else ip_str
1929
2023
1930
2024
def __hash__ (self ):
@@ -1967,6 +2061,9 @@ def is_multicast(self):
1967
2061
See RFC 2373 2.7 for details.
1968
2062
1969
2063
"""
2064
+ ipv4_mapped = self .ipv4_mapped
2065
+ if ipv4_mapped is not None :
2066
+ return ipv4_mapped .is_multicast
1970
2067
return self in self ._constants ._multicast_network
1971
2068
1972
2069
@property
@@ -1978,6 +2075,9 @@ def is_reserved(self):
1978
2075
reserved IPv6 Network ranges.
1979
2076
1980
2077
"""
2078
+ ipv4_mapped = self .ipv4_mapped
2079
+ if ipv4_mapped is not None :
2080
+ return ipv4_mapped .is_reserved
1981
2081
return any (self in x for x in self ._constants ._reserved_networks )
1982
2082
1983
2083
@property
@@ -1988,6 +2088,9 @@ def is_link_local(self):
1988
2088
A boolean, True if the address is reserved per RFC 4291.
1989
2089
1990
2090
"""
2091
+ ipv4_mapped = self .ipv4_mapped
2092
+ if ipv4_mapped is not None :
2093
+ return ipv4_mapped .is_link_local
1991
2094
return self in self ._constants ._linklocal_network
1992
2095
1993
2096
@property
@@ -2007,28 +2110,46 @@ def is_site_local(self):
2007
2110
@property
2008
2111
@functools .lru_cache ()
2009
2112
def is_private (self ):
2010
- """Test if this address is allocated for private networks.
2113
+ """``True`` if the address is defined as not globally reachable by
2114
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2115
+ (for IPv6) with the following exceptions:
2011
2116
2012
- Returns:
2013
- A boolean, True if the address is reserved per
2014
- iana-ipv6-special-registry, or is ipv4_mapped and is
2015
- reserved in the iana-ipv4-special-registry.
2117
+ * ``is_private`` is ``False`` for ``100.64.0.0/10``
2118
+ * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2119
+ semantics of the underlying IPv4 addresses and the following condition holds
2120
+ (see :attr:`IPv6Address.ipv4_mapped`)::
2121
+
2122
+ address.is_private == address.ipv4_mapped.is_private
2016
2123
2124
+ ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
2125
+ IPv4 range where they are both ``False``.
2017
2126
"""
2018
2127
ipv4_mapped = self .ipv4_mapped
2019
2128
if ipv4_mapped is not None :
2020
2129
return ipv4_mapped .is_private
2021
- return any (self in net for net in self ._constants ._private_networks )
2130
+ return (
2131
+ any (self in net for net in self ._constants ._private_networks )
2132
+ and all (self not in net for net in self ._constants ._private_networks_exceptions )
2133
+ )
2022
2134
2023
2135
@property
2024
2136
def is_global (self ):
2025
- """Test if this address is allocated for public networks.
2137
+ """``True`` if the address is defined as globally reachable by
2138
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2139
+ (for IPv6) with the following exception:
2026
2140
2027
- Returns:
2028
- A boolean, true if the address is not reserved per
2029
- iana-ipv6-special-registry.
2141
+ For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2142
+ semantics of the underlying IPv4 addresses and the following condition holds
2143
+ (see :attr:`IPv6Address.ipv4_mapped`)::
2030
2144
2145
+ address.is_global == address.ipv4_mapped.is_global
2146
+
2147
+ ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
2148
+ IPv4 range where they are both ``False``.
2031
2149
"""
2150
+ ipv4_mapped = self .ipv4_mapped
2151
+ if ipv4_mapped is not None :
2152
+ return ipv4_mapped .is_global
2032
2153
return not self .is_private
2033
2154
2034
2155
@property
@@ -2040,6 +2161,9 @@ def is_unspecified(self):
2040
2161
RFC 2373 2.5.2.
2041
2162
2042
2163
"""
2164
+ ipv4_mapped = self .ipv4_mapped
2165
+ if ipv4_mapped is not None :
2166
+ return ipv4_mapped .is_unspecified
2043
2167
return self ._ip == 0
2044
2168
2045
2169
@property
@@ -2051,6 +2175,9 @@ def is_loopback(self):
2051
2175
RFC 2373 2.5.3.
2052
2176
2053
2177
"""
2178
+ ipv4_mapped = self .ipv4_mapped
2179
+ if ipv4_mapped is not None :
2180
+ return ipv4_mapped .is_loopback
2054
2181
return self ._ip == 1
2055
2182
2056
2183
@property
@@ -2167,7 +2294,7 @@ def is_unspecified(self):
2167
2294
2168
2295
@property
2169
2296
def is_loopback (self ):
2170
- return self . _ip == 1 and self .network .is_loopback
2297
+ return super (). is_loopback and self .network .is_loopback
2171
2298
2172
2299
2173
2300
class IPv6Network (_BaseV6 , _BaseNetwork ):
@@ -2268,19 +2395,33 @@ class _IPv6Constants:
2268
2395
2269
2396
_multicast_network = IPv6Network ('ff00::/8' )
2270
2397
2398
+ # Not globally reachable address blocks listed on
2399
+ # https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
2271
2400
_private_networks = [
2272
2401
IPv6Network ('::1/128' ),
2273
2402
IPv6Network ('::/128' ),
2274
2403
IPv6Network ('::ffff:0:0/96' ),
2404
+ IPv6Network ('64:ff9b:1::/48' ),
2275
2405
IPv6Network ('100::/64' ),
2276
2406
IPv6Network ('2001::/23' ),
2277
- IPv6Network ('2001:2::/48' ),
2278
2407
IPv6Network ('2001:db8::/32' ),
2279
- IPv6Network ('2001:10::/28' ),
2408
+ # IANA says N/A, let's consider it not globally reachable to be safe
2409
+ IPv6Network ('2002::/16' ),
2410
+ # RFC 9637: https://www.rfc-editor.org/rfc/rfc9637.html#section-6-2.2
2411
+ IPv6Network ('3fff::/20' ),
2280
2412
IPv6Network ('fc00::/7' ),
2281
2413
IPv6Network ('fe80::/10' ),
2282
2414
]
2283
2415
2416
+ _private_networks_exceptions = [
2417
+ IPv6Network ('2001:1::1/128' ),
2418
+ IPv6Network ('2001:1::2/128' ),
2419
+ IPv6Network ('2001:3::/32' ),
2420
+ IPv6Network ('2001:4:112::/48' ),
2421
+ IPv6Network ('2001:20::/28' ),
2422
+ IPv6Network ('2001:30::/28' ),
2423
+ ]
2424
+
2284
2425
_reserved_networks = [
2285
2426
IPv6Network ('::/8' ), IPv6Network ('100::/8' ),
2286
2427
IPv6Network ('200::/7' ), IPv6Network ('400::/6' ),
0 commit comments