Skip to content

Commit

Permalink
Fix issue secdev#359
Browse files Browse the repository at this point in the history
Handle ipv6 addresses according to RFC 5952
Add regression tests
  • Loading branch information
Benjamin committed Jan 10, 2017
1 parent 0d401eb commit f25f641
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 21 deletions.
70 changes: 49 additions & 21 deletions scapy/pton_ntop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -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
63 changes: 63 additions & 0 deletions test/regression.uts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

0 comments on commit f25f641

Please sign in to comment.