Visitar URL original
GH-113171: Fix "private" (non-global) IP address ranges by jstasiak · Pull Request #113179 · python/cpython · GitHub
Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Simplify
  • Loading branch information
jstasiak committed Jan 9, 2024
commit 2a8d49b30b9f8f41f7ddab2d067101bf83e91bce
103 changes: 24 additions & 79 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -1341,7 +1341,10 @@ def is_private(self):
iana-ipv4-special-registry.

"""
return any(self in net for net in self._constants._private_networks)
return (
any(self in net for net in self._constants._private_networks)
and all(self not in net for net in self._constants._private_networks_exceptions)
)

@property
@functools.lru_cache()
Expand Down Expand Up @@ -1550,65 +1553,6 @@ def is_global(self):
not self.is_private)


def _address_exclude_many(network, others):
"""
A version of IPv4Network/IPv6Network address_exclude() but for multiple networks
to exclude in the same call.

The following are programming errors and will raise an exception:

* Network type mismatch (IPv4 vs IPv6)
* Networks in `others` overlapping each other
* Networks in `others` not ovarlapping the provided `network`

Returns:
A list of networks left after excluding `others` from `network`.
"""
# Precondition checks
for o in others:
if not network.overlaps(o):
raise AssertionError(f"No overlap between {network} and {o}")

for a, b in combinations(others, 2):
if a.overlaps(b):
raise AssertionError(f"{a} overlaps {b}")

networks = [network]

for o in others:
networks = [
result_network
for input_network in networks
for result_network in (
input_network.address_exclude(o)
if input_network.overlaps(o)
else [input_network]
)
]

# Integrity checks to make sure we haven't done something really wrong
addresses_started_with = network.num_addresses
addresses_excluded = sum(o.num_addresses for o in others)
addresses_left = sum(n.num_addresses for n in networks)
expected_addresses_left = addresses_started_with - addresses_excluded

if addresses_left != expected_addresses_left:
raise AssertionError(
f"Should have {expected_addresses_left} addresses left, got {addresses_left}"
)

for n in networks:
for o in others:
if n.overlaps(o):
raise AssertionError(f"{n} overlaps {o}")

for a, b in combinations(networks, 2):
if a.overlaps(b):
raise AssertionError(f'{a} overlaps {b}')

return networks


class _IPv4Constants:
_linklocal_network = IPv4Network('169.254.0.0/16')

Expand All @@ -1626,13 +1570,7 @@ class _IPv4Constants:
IPv4Network('127.0.0.0/8'),
IPv4Network('169.254.0.0/16'),
IPv4Network('172.16.0.0/12'),
*_address_exclude_many(
IPv4Network('192.0.0.0/24'),
[
IPv4Network('192.0.0.9/32'),
IPv4Network('192.0.0.10/32'),
],
),
IPv4Network('192.0.0.0/24'),
IPv4Network('192.0.0.170/31'),
IPv4Network('192.0.2.0/24'),
IPv4Network('192.168.0.0/16'),
Expand All @@ -1643,6 +1581,11 @@ class _IPv4Constants:
IPv4Network('255.255.255.255/32'),
]

_private_networks_exceptions = [
IPv4Network('192.0.0.9/32'),
IPv4Network('192.0.0.10/32'),
]

_reserved_network = IPv4Network('240.0.0.0/4')

_unspecified_address = IPv4Address('0.0.0.0')
Expand Down Expand Up @@ -2128,7 +2071,10 @@ def is_private(self):
ipv4_mapped = self.ipv4_mapped
if ipv4_mapped is not None:
return ipv4_mapped.is_private
return any(self in net for net in self._constants._private_networks)
return (
any(self in net for net in self._constants._private_networks)
and all(self not in net for net in self._constants._private_networks_exceptions)
)

@property
def is_global(self):
Expand Down Expand Up @@ -2386,24 +2332,23 @@ class _IPv6Constants:
IPv6Network('::ffff:0:0/96'),
IPv6Network('64:ff9b:1::/48'),
IPv6Network('100::/64'),
*_address_exclude_many(
IPv6Network('2001::/23'),
[
IPv6Network('2001:1::1/128'),
IPv6Network('2001:1::2/128'),
IPv6Network('2001:3::/32'),
IPv6Network('2001:4:112::/48'),
IPv6Network('2001:20::/28'),
IPv6Network('2001:30::/28'),
],
),
IPv6Network('2001::/23'),
IPv6Network('2001:db8::/32'),
# IANA says N/A, let's consider it not globally reachable to be safe
IPv6Network('2002::/16'),
IPv6Network('fc00::/7'),
IPv6Network('fe80::/10'),
]

_private_networks_exceptions = [
IPv6Network('2001:1::1/128'),
IPv6Network('2001:1::2/128'),
IPv6Network('2001:3::/32'),
IPv6Network('2001:4:112::/48'),
IPv6Network('2001:20::/28'),
IPv6Network('2001:30::/28'),
]

_reserved_networks = [
IPv6Network('::/8'), IPv6Network('100::/8'),
IPv6Network('200::/7'), IPv6Network('400::/6'),
Expand Down