From f25f641513540845470ce34c489bea1cd8fecb93 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 31 Dec 2016 14:46:52 +0100 Subject: [PATCH] Fix issue #359 Handle ipv6 addresses according to RFC 5952 Add regression tests --- scapy/pton_ntop.py | 70 +++++++++++++++++++++++++++++++-------------- test/regression.uts | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/scapy/pton_ntop.py b/scapy/pton_ntop.py index 25f7ef98eff..64a5af41592 100644 --- a/scapy/pton_ntop.py +++ b/scapy/pton_ntop.py @@ -10,7 +10,7 @@ without IPv6 support, on Windows for instance. """ -import socket,struct +import socket,struct,re def inet_pton(af, addr): """Convert an IP address from text representation into binary form""" @@ -77,25 +77,53 @@ def inet_ntop(af, addr): except AttributeError: pass - # IPv6 addresses have 128bits (16 bytes) - if len(addr) != 16: + return _ipv6_bin_to_str(addr) + else: + raise Exception("Address family not supported yet") + + +def _ipv6_bin_to_str(addr): + # IPv6 addresses have 128bits (16 bytes) + if len(addr) != 16: + raise Exception("Illegal syntax for IP address") + parts = [] + for left in [0, 2, 4, 6, 8, 10, 12, 14]: + try: + value = struct.unpack("!H", addr[left:left + 2])[0] + hexstr = hex(value)[2:] + except TypeError: raise Exception("Illegal syntax for IP address") - parts = [] - for left in [0, 2, 4, 6, 8, 10, 12, 14]: - try: - value = struct.unpack("!H", addr[left:left+2])[0] - hexstr = hex(value)[2:] - except TypeError: - raise Exception("Illegal syntax for IP address") - parts.append(hexstr.lstrip("0").lower()) - result = ":".join(parts) - while ":::" in result: - result = result.replace(":::", "::") - # Leaving out leading and trailing zeros is only allowed with :: - if result.endswith(":") and not result.endswith("::"): - result = result + "0" - if result.startswith(":") and not result.startswith("::"): - result = "0" + result - return result + parts.append(hexstr.lower()) + + address = ":".join(parts) + + # Find all consecutive zero blocks + matches = re.findall('(?::|^)(0(?::0)+)(?::|$)', address) + if matches: + # If multiple consecutive blocks have the same length, take the leftmost + match = max(matches) + if address.startswith(match): + leftidx = 0 + else: + leftidx = address.find(':' + match) + 1 + left = address[:leftidx] + rightidx = leftidx + len(match) + + # Adrress is like abcd:ef01:: + if len(address) == rightidx: + compact_address = left + ":" + + # Adrress is like ::abcd:ef01 + elif leftidx == 0: + compact_address = ":" + address[rightidx:] + + # Adrress is like abcd::ef01 + else: + compact_address = left + address[rightidx:] + + # Special case: address full of zeros + if compact_address == ":": + compact_address = "::" else: - raise Exception("Address family not supported yet") + compact_address = address + return compact_address diff --git a/test/regression.uts b/test/regression.uts index 41c640a052f..a8cee7f9ee1 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -6897,3 +6897,66 @@ x = f.i2repr(mp, {'*', '+', 'bit 2'}) assert(re.match(r'^.*Star \(\*\).*$', x) is not None) assert(re.match(r'^.*Plus \(\+\).*$', x) is not None) assert(re.match(r'^.*bit 2.*$', x) is not None) + +########################################################################################################### ++ Test correct conversion from binary to string of IPv6 addresses + += IPv6 bin to string conversion - all zero bytes +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # All zero +compressed = _ipv6_bin_to_str(address) +assert(compressed == '::') + += IPv6 bin to string conversion - non-compressable +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88' # Not compressable +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1111:2222:3333:4444:5555:6666:7777:8888') + += IPv6 bin to string conversion - Zero-block right +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x00\x00\x00\x00\x00\x00' # Zeroblock right +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1111:2222:3333:4444:5555::') + += IPv6 bin to string conversion - Zero-block left +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x00\x00\x00\x00\x00\x00\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88' # Zeroblock left +compressed = _ipv6_bin_to_str(address) +assert(compressed == '::4444:5555:6666:7777:8888') + += IPv6 bin to string conversion - Two zero-block with different length +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x00\x00\x00\x00\x33\x33\x44\x44\x00\x00\x00\x00\x00\x00\x88\x88' # Short and long zero block +compressed = _ipv6_bin_to_str(address) +assert(compressed == '0:0:3333:4444::8888') + += IPv6 bin to string conversion - Address 1:: +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # only 1 on the left +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1::') + += IPv6 bin to string conversion - Address ::1 +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' # loopback +compressed = _ipv6_bin_to_str(address) +assert(compressed == '::1') + += IPv6 bin to string conversion - Zero-block of length 1 +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x00\x00\x88\x88' # only one zero block +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1111:2222:3333:4444:5555:6666:0:8888') + += IPv6 bin to string conversion - Two zero-blocks with equal length +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x11\x11\x00\x00\x00\x00\x44\x44\x00\x00\x00\x00\x77\x77\x88\x88' # two zero blocks of equal length +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1111::4444:0:0:7777:8888') + += IPv6 bin to string conversion - Leading zero suppression +from scapy.pton_ntop import _ipv6_bin_to_str +address=b'\x10\x00\x02\x00\x00\x30\x00\x04\x00\x05\x00\x60\x07\x00\x80\x00' # Leading zero suppression +compressed = _ipv6_bin_to_str(address) +assert(compressed == '1000:200:30:4:5:60:700:8000') \ No newline at end of file