New submission from Jon Foster: 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. ---------- components: Library (Lib) files: ipaddress_masks_v1.patch keywords: patch messages: 195846 nosy: jongfoster, ncoghlan, pmoody priority: normal severity: normal status: open title: ipaddress netmask/hostmask parsing bugs type: behavior versions: Python 3.3, Python 3.4 Added file: http://bugs.python.org/file31415/ipaddress_masks_v1.patch _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue18805> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com