This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author jongfoster
Recipients jongfoster, ncoghlan, pmoody
Date 2013-08-22.00:38:36
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1377131918.88.0.806374780855.issue18805@psf.upfronthosting.co.za>
In-reply-to
Content
Short version:
Validation of netmasks and hostmasks in IPv4Network is wrong: it rejects many
valid netmasks, it accepts many invalid netmasks and hostmasks, and it sometimes
throws the wrong exception.  Patch attached.


Long version:


Wrongly rejecting hostmasks
---------------------------

Creating IPv4Network objects using a hostmask only works for 3 of the 31 possible hostmasks.
It works fine for /8, /16, and /24 networks, but anything else fails.  E.g. first let's
calculate the hostmask for a /21 network:

  >>> from ipaddress import IPv4Network
  >>> IPv4Network('0.0.0.0/21').hostmask
  IPv4Address('0.0.7.255')

Then try using it:
  >> IPv4Network('0.0.0.0/0.0.7.255')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "c:\Python33\lib\ipaddress.py", line 1443, in __init__
      % addr[1])
  ipaddress.NetmaskValueError: '0.0.7.255' is not a valid netmask

The problem is that there is a list of "_valid_mask_octets".  Although the values listed
are correct for netmasks, they are wrong for host masks.  In binary, a netmask has 1s
in the most significant <prefixlen> bits and 0s everywhere else; a hostmask has 0s
in the most significant <prefixlen> bits and 1s everywhere else.  So netmasks have
octet values 0b11111111, 0b11111110, 0b11111100, etc, whereas hostmasks have
octet values 0b11111111, 0b01111111, 0b00111111, etc.


Wrongly accepting hostmasks
---------------------------

Once the individual octets have been validated, the hostmask validation just checks
the first octet is less than the last octet.  This accepts values like "0.255.0.255",
which is not a valid hostmask.  The IPv4Network object then has wierd nonsensical
values.


Wrongly accepting netmasks
---------------------------

Once the individual octets have been validated, the netmask validation just checks
each octet is no greater than the one before.  This accepts values like "254.192.128.0",
which is not a valid netmask.  The IPv4Network object then has wierd nonsensical
values.


Inconsistent parsing
--------------------

The existing validation code includes its own parsing code.  If the netmask/hostmask
passes that vaildation, it then goes into _ip_int_from_string() to be parsed and
used.  _ip_int_from_string() checks things that aren't caught by the validation
code, leading to AddressValueError being thrown when NetmaskValueError was expected:

  >>> IPv4Network('1.2.0.0/0.0.0255.255')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "c:\Python33\lib\ipaddress.py", line 1440, in __init__
      self._ip_int_from_string(addr[1]) ^ self._ALL_ONES)
    File "c:\Python33\lib\ipaddress.py", line 1055, in _ip_int_from_string
      raise AddressValueError("%s in %r" % (exc, ip_str)) from None
  ipaddress.AddressValueError: At most 3 characters permitted in '0255' in '0.0.0255.255'

The correct fix for this one is obviously to use the same parser in all the places
we parse the netmask/hostmask.


The patch
---------

I'm attaching a patch, with tests, that fixes these issues.  Reusing the existing
_ip_int_from_string() function to parse the netmask/hostmask simplified the
validation code a lot.

My hope is that this patch is suitable for a backport to 3.3, as well as trunk.
History
Date User Action Args
2013-08-22 00:38:38jongfostersetrecipients: + jongfoster, ncoghlan, pmoody
2013-08-22 00:38:38jongfostersetmessageid: <1377131918.88.0.806374780855.issue18805@psf.upfronthosting.co.za>
2013-08-22 00:38:38jongfosterlinkissue18805 messages
2013-08-22 00:38:37jongfostercreate