Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf86b17a

Browse files
encukoujstasiak
andauthored
[3.12]gh-113171:gh-65056: Fix "private" (non-global) IP address ranges (GH-113179) (GH-113186) (GH-118177)
*GH-113171: Fix "private" (non-global) IP address ranges (GH-113179)The _private_networks variables, used by various is_privateimplementations, were missing some ranges and at the same time hadoverly strict ranges (where there are more specific ranges consideredglobally reachable by the IANA registries).This patch updates the ranges with what was missing or otherwiseincorrect.100.64.0.0/10 is left alone, for now, as it's been made special in [1].The _address_exclude_many() call returns 8 networks for IPv4, 121networks for IPv6.[1]#61602*GH-65056: Improve the IP address' is_global/is_private documentation (GH-113186)It wasn't clear what the semantics of is_global/is_private are and, whenone gets to the bottom of it, it's not quite so simple (hence theexceptions listed).(cherry picked from commit2a4cbf1)(cherry picked from commit40d75c2)---------Co-authored-by: Jakub Stasiak <jakub@stasiak.at>
1 parent2eaf9ba commitf86b17a

File tree

5 files changed

+157
-24
lines changed

5 files changed

+157
-24
lines changed

‎Doc/library/ipaddress.rst

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,18 +178,53 @@ write code that handles both IP versions correctly. Address objects are
178178

179179
..attribute::is_private
180180

181-
``True`` if the address isallocated for private networks. See
181+
``True`` if the address isdefined as not globally reachable by
182182
iana-ipv4-special-registry_ (for IPv4) oriana-ipv6-special-registry_
183-
(for IPv6).
183+
(for IPv6) with the following exceptions:
184+
185+
* ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``)
186+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
187+
semantics of the underlying IPv4 addresses and the following condition holds
188+
(see:attr:`IPv6Address.ipv4_mapped`)::
189+
190+
address.is_private == address.ipv4_mapped.is_private
191+
192+
``is_private`` has value opposite to:attr:`is_global`, except for the shared address space
193+
(``100.64.0.0/10`` range) where they are both ``False``.
194+
195+
..versionchanged::3.12.4
196+
197+
Fixed some false positives and false negatives.
198+
199+
* ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and
200+
``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private).
201+
* ``64:ff9b:1::/48`` is considered private.
202+
* ``2002::/16`` is considered private.
203+
* There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``,
204+
``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``.
205+
The exceptions are not considered private.
184206

185207
..attribute::is_global
186208

187-
``True`` if the address isallocated for public networks. See
209+
``True`` if the address isdefined as globally reachable by
188210
iana-ipv4-special-registry_ (for IPv4) oriana-ipv6-special-registry_
189-
(for IPv6).
211+
(for IPv6) with the following exception:
212+
213+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
214+
semantics of the underlying IPv4 addresses and the following condition holds
215+
(see:attr:`IPv6Address.ipv4_mapped`)::
216+
217+
address.is_global == address.ipv4_mapped.is_global
218+
219+
``is_global`` has value opposite to:attr:`is_private`, except for the shared address space
220+
(``100.64.0.0/10`` range) where they are both ``False``.
190221

191222
..versionadded::3.4
192223

224+
..versionchanged::3.12.4
225+
226+
Fixed some false positives and false negatives, see:attr:`is_private` for details.
227+
193228
..attribute::is_unspecified
194229

195230
``True`` if the address is unspecified. See:RFC:`5735` (for IPv4)

‎Doc/whatsnew/3.12.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,3 +2457,12 @@ Removed
24572457

24582458
* Remove the ``PyUnicode_InternImmortal()`` function macro.
24592459
(Contributed by Victor Stinner in:gh:`85858`.)
2460+
2461+
Notable changes in 3.12.4
2462+
=========================
2463+
2464+
ipaddress
2465+
---------
2466+
2467+
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
2468+
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.

‎Lib/ipaddress.py

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,11 @@ def is_private(self):
10861086
"""
10871087
returnany(self.network_addressinpriv_networkand
10881088
self.broadcast_addressinpriv_network
1089-
forpriv_networkinself._constants._private_networks)
1089+
forpriv_networkinself._constants._private_networks)andall(
1090+
self.network_addressnotinnetworkand
1091+
self.broadcast_addressnotinnetwork
1092+
fornetworkinself._constants._private_networks_exceptions
1093+
)
10901094

10911095
@property
10921096
defis_global(self):
@@ -1333,18 +1337,41 @@ def is_reserved(self):
13331337
@property
13341338
@functools.lru_cache()
13351339
defis_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:
13371343
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`)::
13411348
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``.
13421353
"""
1343-
returnany(selfinnetfornetinself._constants._private_networks)
1354+
return (
1355+
any(selfinnetfornetinself._constants._private_networks)
1356+
andall(selfnotinnetfornetinself._constants._private_networks_exceptions)
1357+
)
13441358

13451359
@property
13461360
@functools.lru_cache()
13471361
defis_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+
"""
13481375
returnselfnotinself._constants._public_networkandnotself.is_private
13491376

13501377
@property
@@ -1548,13 +1575,15 @@ class _IPv4Constants:
15481575

15491576
_public_network=IPv4Network('100.64.0.0/10')
15501577

1578+
# Not globally reachable address blocks listed on
1579+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
15511580
_private_networks= [
15521581
IPv4Network('0.0.0.0/8'),
15531582
IPv4Network('10.0.0.0/8'),
15541583
IPv4Network('127.0.0.0/8'),
15551584
IPv4Network('169.254.0.0/16'),
15561585
IPv4Network('172.16.0.0/12'),
1557-
IPv4Network('192.0.0.0/29'),
1586+
IPv4Network('192.0.0.0/24'),
15581587
IPv4Network('192.0.0.170/31'),
15591588
IPv4Network('192.0.2.0/24'),
15601589
IPv4Network('192.168.0.0/16'),
@@ -1565,6 +1594,11 @@ class _IPv4Constants:
15651594
IPv4Network('255.255.255.255/32'),
15661595
]
15671596

1597+
_private_networks_exceptions= [
1598+
IPv4Network('192.0.0.9/32'),
1599+
IPv4Network('192.0.0.10/32'),
1600+
]
1601+
15681602
_reserved_network=IPv4Network('240.0.0.0/4')
15691603

15701604
_unspecified_address=IPv4Address('0.0.0.0')
@@ -2007,27 +2041,42 @@ def is_site_local(self):
20072041
@property
20082042
@functools.lru_cache()
20092043
defis_private(self):
2010-
"""Test if this address is allocated for private networks.
2044+
"""``True`` if the address is defined as not globally reachable by
2045+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2046+
(for IPv6) with the following exceptions:
20112047
2012-
Returns:
2013-
A boolean, True iftheaddressisreserved per
2014-
iana-ipv6-special-registry, or is ipv4_mappedandis
2015-
reserved in the iana-ipv4-special-registry.
2048+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
2049+
* For IPv4-mapped IPv6-addressesthe``is_private`` valueisdetermined by the
2050+
semantics of the underlying IPv4 addressesandthe following condition holds
2051+
(see :attr:`IPv6Address.ipv4_mapped`)::
20162052
2053+
address.is_private == address.ipv4_mapped.is_private
2054+
2055+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
2056+
IPv4 range where they are both ``False``.
20172057
"""
20182058
ipv4_mapped=self.ipv4_mapped
20192059
ifipv4_mappedisnotNone:
20202060
returnipv4_mapped.is_private
2021-
returnany(selfinnetfornetinself._constants._private_networks)
2061+
return (
2062+
any(selfinnetfornetinself._constants._private_networks)
2063+
andall(selfnotinnetfornetinself._constants._private_networks_exceptions)
2064+
)
20222065

20232066
@property
20242067
defis_global(self):
2025-
"""Test if this address is allocated for public networks.
2068+
"""``True`` if the address is defined as globally reachable by
2069+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2070+
(for IPv6) with the following exception:
20262071
2027-
Returns:
2028-
A boolean, true if the address is not reserved per
2029-
iana-ipv6-special-registry.
2072+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2073+
semantics of the underlying IPv4 addresses and the following condition holds
2074+
(see :attr:`IPv6Address.ipv4_mapped`)::
2075+
2076+
address.is_global == address.ipv4_mapped.is_global
20302077
2078+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
2079+
IPv4 range where they are both ``False``.
20312080
"""
20322081
returnnotself.is_private
20332082

@@ -2268,19 +2317,31 @@ class _IPv6Constants:
22682317

22692318
_multicast_network=IPv6Network('ff00::/8')
22702319

2320+
# Not globally reachable address blocks listed on
2321+
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
22712322
_private_networks= [
22722323
IPv6Network('::1/128'),
22732324
IPv6Network('::/128'),
22742325
IPv6Network('::ffff:0:0/96'),
2326+
IPv6Network('64:ff9b:1::/48'),
22752327
IPv6Network('100::/64'),
22762328
IPv6Network('2001::/23'),
2277-
IPv6Network('2001:2::/48'),
22782329
IPv6Network('2001:db8::/32'),
2279-
IPv6Network('2001:10::/28'),
2330+
# IANA says N/A, let's consider it not globally reachable to be safe
2331+
IPv6Network('2002::/16'),
22802332
IPv6Network('fc00::/7'),
22812333
IPv6Network('fe80::/10'),
22822334
]
22832335

2336+
_private_networks_exceptions= [
2337+
IPv6Network('2001:1::1/128'),
2338+
IPv6Network('2001:1::2/128'),
2339+
IPv6Network('2001:3::/32'),
2340+
IPv6Network('2001:4:112::/48'),
2341+
IPv6Network('2001:20::/28'),
2342+
IPv6Network('2001:30::/28'),
2343+
]
2344+
22842345
_reserved_networks= [
22852346
IPv6Network('::/8'),IPv6Network('100::/8'),
22862347
IPv6Network('200::/7'),IPv6Network('400::/6'),

‎Lib/test/test_ipaddress.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,10 @@ def testReservedIpv4(self):
22692269
self.assertEqual(True,ipaddress.ip_address(
22702270
'172.31.255.255').is_private)
22712271
self.assertEqual(False,ipaddress.ip_address('172.32.0.0').is_private)
2272+
self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
2273+
self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
2274+
self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
2275+
self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
22722276

22732277
self.assertEqual(True,
22742278
ipaddress.ip_address('169.254.100.200').is_link_local)
@@ -2294,6 +2298,7 @@ def testPrivateNetworks(self):
22942298
self.assertEqual(True,ipaddress.ip_network("169.254.0.0/16").is_private)
22952299
self.assertEqual(True,ipaddress.ip_network("172.16.0.0/12").is_private)
22962300
self.assertEqual(True,ipaddress.ip_network("192.0.0.0/29").is_private)
2301+
self.assertEqual(False,ipaddress.ip_network("192.0.0.9/32").is_private)
22972302
self.assertEqual(True,ipaddress.ip_network("192.0.0.170/31").is_private)
22982303
self.assertEqual(True,ipaddress.ip_network("192.0.2.0/24").is_private)
22992304
self.assertEqual(True,ipaddress.ip_network("192.168.0.0/16").is_private)
@@ -2310,8 +2315,8 @@ def testPrivateNetworks(self):
23102315
self.assertEqual(True,ipaddress.ip_network("::/128").is_private)
23112316
self.assertEqual(True,ipaddress.ip_network("::ffff:0:0/96").is_private)
23122317
self.assertEqual(True,ipaddress.ip_network("100::/64").is_private)
2313-
self.assertEqual(True,ipaddress.ip_network("2001::/23").is_private)
23142318
self.assertEqual(True,ipaddress.ip_network("2001:2::/48").is_private)
2319+
self.assertEqual(False,ipaddress.ip_network("2001:3::/48").is_private)
23152320
self.assertEqual(True,ipaddress.ip_network("2001:db8::/32").is_private)
23162321
self.assertEqual(True,ipaddress.ip_network("2001:10::/28").is_private)
23172322
self.assertEqual(True,ipaddress.ip_network("fc00::/7").is_private)
@@ -2390,6 +2395,20 @@ def testReservedIpv6(self):
23902395
self.assertEqual(True,ipaddress.ip_address('0::0').is_unspecified)
23912396
self.assertEqual(False,ipaddress.ip_address('::1').is_unspecified)
23922397

2398+
self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
2399+
self.assertFalse(ipaddress.ip_address('2001::').is_global)
2400+
self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
2401+
self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
2402+
self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
2403+
self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
2404+
self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
2405+
self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
2406+
self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
2407+
self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
2408+
self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
2409+
self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
2410+
self.assertFalse(ipaddress.ip_address('2002::').is_global)
2411+
23932412
# some generic IETF reserved addresses
23942413
self.assertEqual(True,ipaddress.ip_address('100::').is_reserved)
23952414
self.assertEqual(True,ipaddress.ip_network('4000::1/128').is_reserved)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Fixed various false positives and false negatives in
2+
3+
*:attr:`ipaddress.IPv4Address.is_private` (see these docs for details)
4+
*:attr:`ipaddress.IPv4Address.is_global`
5+
*:attr:`ipaddress.IPv6Address.is_private`
6+
*:attr:`ipaddress.IPv6Address.is_global`
7+
8+
Also in the corresponding:class:`ipaddress.IPv4Network` and:class:`ipaddress.IPv6Network`
9+
attributes.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp