@@ -456,8 +456,8 @@ def _check_packed_address(self, address, expected_len):
456456 raise AddressValueError (msg % (address , address_len ,
457457 expected_len , self ._version ))
458458
459- def _ip_int_from_prefix (self , prefixlen = None ):
460- """Turn the prefix length netmask into a int for comparison.
459+ def _ip_int_from_prefix (self , prefixlen ):
460+ """Turn the prefix length into a bitwise netmask
461461
462462 Args:
463463 prefixlen: An integer, the prefix length.
@@ -466,36 +466,92 @@ def _ip_int_from_prefix(self, prefixlen=None):
466466 An integer.
467467
468468 """
469- if prefixlen is None :
470- prefixlen = self ._prefixlen
471469 return self ._ALL_ONES ^ (self ._ALL_ONES >> prefixlen )
472470
473- def _prefix_from_ip_int (self , ip_int , mask = 32 ):
474- """Return prefix length from the decimal netmask.
471+ def _prefix_from_ip_int (self , ip_int ):
472+ """Return prefix length from the bitwise netmask.
475473
476474 Args:
477- ip_int: An integer, the IP address.
478- mask: The netmask. Defaults to 32.
475+ ip_int: An integer, the netmask in axpanded bitwise format
476+
477+ Returns:
478+ An integer, the prefix length.
479+
480+ Raises:
481+ ValueError: If the input intermingles zeroes & ones
482+ """
483+ trailing_zeroes = _count_righthand_zero_bits (ip_int ,
484+ self ._max_prefixlen )
485+ prefixlen = self ._max_prefixlen - trailing_zeroes
486+ leading_ones = ip_int >> trailing_zeroes
487+ all_ones = (1 << prefixlen ) - 1
488+ if leading_ones != all_ones :
489+ byteslen = self ._max_prefixlen // 8
490+ details = ip_int .to_bytes (byteslen , 'big' )
491+ msg = 'Netmask pattern %r mixes zeroes & ones'
492+ raise ValueError (msg % details )
493+ return prefixlen
494+
495+ def _report_invalid_netmask (self , netmask_str ):
496+ msg = '%r is not a valid netmask' % netmask_str
497+ raise NetmaskValueError (msg ) from None
498+
499+ def _prefix_from_prefix_string (self , prefixlen_str ):
500+ """Return prefix length from a numeric string
501+
502+ Args:
503+ prefixlen_str: The string to be converted
479504
480505 Returns:
481506 An integer, the prefix length.
482507
508+ Raises:
509+ NetmaskValueError: If the input is not a valid netmask
483510 """
484- return mask - _count_righthand_zero_bits (ip_int , mask )
511+ # int allows a leading +/- as well as surrounding whitespace,
512+ # so we ensure that isn't the case
513+ if not _BaseV4 ._DECIMAL_DIGITS .issuperset (prefixlen_str ):
514+ self ._report_invalid_netmask (prefixlen_str )
515+ try :
516+ prefixlen = int (prefixlen_str )
517+ except ValueError :
518+ self ._report_invalid_netmask (prefixlen_str )
519+ if not (0 <= prefixlen <= self ._max_prefixlen ):
520+ self ._report_invalid_netmask (prefixlen_str )
521+ return prefixlen
485522
486- def _ip_string_from_prefix (self , prefixlen = None ):
487- """Turn a prefix length into a dotted decimal string.
523+ def _prefix_from_ip_string (self , ip_str ):
524+ """Turn a netmask/hostmask string into a prefix length
488525
489526 Args:
490- prefixlen: An integer, the netmask prefix length.
527+ ip_str: The netmask/hostmask to be converted
491528
492529 Returns:
493- A string , the dotted decimal netmask string .
530+ An integer , the prefix length .
494531
532+ Raises:
533+ NetmaskValueError: If the input is not a valid netmask/hostmask
495534 """
496- if not prefixlen :
497- prefixlen = self ._prefixlen
498- return self ._string_from_ip_int (self ._ip_int_from_prefix (prefixlen ))
535+ # Parse the netmask/hostmask like an IP address.
536+ try :
537+ ip_int = self ._ip_int_from_string (ip_str )
538+ except AddressValueError :
539+ self ._report_invalid_netmask (ip_str )
540+
541+ # Try matching a netmask (this would be /1*0*/ as a bitwise regexp).
542+ # Note that the two ambiguous cases (all-ones and all-zeroes) are
543+ # treated as netmasks.
544+ try :
545+ return self ._prefix_from_ip_int (ip_int )
546+ except ValueError :
547+ pass
548+
549+ # Invert the bits, and try matching a /0+1+/ hostmask instead.
550+ ip_int ^= self ._ALL_ONES
551+ try :
552+ return self ._prefix_from_ip_int (ip_int )
553+ except ValueError :
554+ self ._report_invalid_netmask (ip_str )
499555
500556
501557class _BaseAddress (_IPAddressBase ):
@@ -504,7 +560,6 @@ class _BaseAddress(_IPAddressBase):
504560
505561 This IP class contains the version independent methods which are
506562 used by single IP addresses.
507-
508563 """
509564
510565 def __init__ (self , address ):
@@ -873,7 +928,7 @@ def subnets(self, prefixlen_diff=1, new_prefix=None):
873928 raise ValueError ('prefix length diff must be > 0' )
874929 new_prefixlen = self ._prefixlen + prefixlen_diff
875930
876- if not self ._is_valid_netmask ( str ( new_prefixlen )) :
931+ if new_prefixlen > self ._max_prefixlen :
877932 raise ValueError (
878933 'prefix length diff %d is invalid for netblock %s' % (
879934 new_prefixlen , self ))
@@ -1428,33 +1483,16 @@ def __init__(self, address, strict=True):
14281483 self .network_address = IPv4Address (self ._ip_int_from_string (addr [0 ]))
14291484
14301485 if len (addr ) == 2 :
1431- mask = addr [1 ].split ('.' )
1432-
1433- if len (mask ) == 4 :
1434- # We have dotted decimal netmask.
1435- if self ._is_valid_netmask (addr [1 ]):
1436- self .netmask = IPv4Address (self ._ip_int_from_string (
1437- addr [1 ]))
1438- elif self ._is_hostmask (addr [1 ]):
1439- self .netmask = IPv4Address (
1440- self ._ip_int_from_string (addr [1 ]) ^ self ._ALL_ONES )
1441- else :
1442- raise NetmaskValueError ('%r is not a valid netmask'
1443- % addr [1 ])
1444-
1445- self ._prefixlen = self ._prefix_from_ip_int (int (self .netmask ))
1446- else :
1447- # We have a netmask in prefix length form.
1448- if not self ._is_valid_netmask (addr [1 ]):
1449- raise NetmaskValueError ('%r is not a valid netmask'
1450- % addr [1 ])
1451- self ._prefixlen = int (addr [1 ])
1452- self .netmask = IPv4Address (self ._ip_int_from_prefix (
1453- self ._prefixlen ))
1486+ try :
1487+ # Check for a netmask in prefix length form
1488+ self ._prefixlen = self ._prefix_from_prefix_string (addr [1 ])
1489+ except NetmaskValueError :
1490+ # Check for a netmask or hostmask in dotted-quad form.
1491+ # This may raise NetmaskValueError.
1492+ self ._prefixlen = self ._prefix_from_ip_string (addr [1 ])
14541493 else :
14551494 self ._prefixlen = self ._max_prefixlen
1456- self .netmask = IPv4Address (self ._ip_int_from_prefix (
1457- self ._prefixlen ))
1495+ self .netmask = IPv4Address (self ._ip_int_from_prefix (self ._prefixlen ))
14581496
14591497 if strict :
14601498 if (IPv4Address (int (self .network_address ) & int (self .netmask )) !=
@@ -2042,11 +2080,8 @@ def __init__(self, address, strict=True):
20422080 self .network_address = IPv6Address (self ._ip_int_from_string (addr [0 ]))
20432081
20442082 if len (addr ) == 2 :
2045- if self ._is_valid_netmask (addr [1 ]):
2046- self ._prefixlen = int (addr [1 ])
2047- else :
2048- raise NetmaskValueError ('%r is not a valid netmask'
2049- % addr [1 ])
2083+ # This may raise NetmaskValueError
2084+ self ._prefixlen = self ._prefix_from_prefix_string (addr [1 ])
20502085 else :
20512086 self ._prefixlen = self ._max_prefixlen
20522087
@@ -2061,23 +2096,6 @@ def __init__(self, address, strict=True):
20612096 if self ._prefixlen == (self ._max_prefixlen - 1 ):
20622097 self .hosts = self .__iter__
20632098
2064- def _is_valid_netmask (self , prefixlen ):
2065- """Verify that the netmask/prefixlen is valid.
2066-
2067- Args:
2068- prefixlen: A string, the netmask in prefix length format.
2069-
2070- Returns:
2071- A boolean, True if the prefix represents a valid IPv6
2072- netmask.
2073-
2074- """
2075- try :
2076- prefixlen = int (prefixlen )
2077- except ValueError :
2078- return False
2079- return 0 <= prefixlen <= self ._max_prefixlen
2080-
20812099 @property
20822100 def is_site_local (self ):
20832101 """Test if the address is reserved for site-local.
0 commit comments