blob: b54a62c13c90013714c6cd010db4cb830189c2c0 [file] [log] [blame]
## This file is part of Scapy
## See http://www.secdev.org/projects/scapy for more informations
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license
"""
Convert IPv6 addresses between textual representation and binary.
These functions are missing when python is compiled
without IPv6 support, on Windows for instance.
"""
from __future__ import absolute_import
import socket
import re
import binascii
from scapy.modules.six.moves import range
from scapy.compat import *
_IP6_ZEROS = re.compile('(?::|^)(0(?::0)+)(?::|$)')
_INET6_PTON_EXC = socket.error("illegal IP address string passed to inet_pton")
def _inet6_pton(addr):
"""Convert an IPv6 address from text representation into binary form,
used when socket.inet_pton is not available.
"""
joker_pos = None
result = b""
addr = plain_str(addr)
if addr == '::':
return b'\x00' * 16
if addr.startswith('::'):
addr = addr[1:]
if addr.endswith('::'):
addr = addr[:-1]
parts = addr.split(":")
nparts = len(parts)
for i, part in enumerate(parts):
if not part:
# "::" indicates one or more groups of 2 null bytes
if joker_pos is None:
joker_pos = len(result)
else:
# Wildcard is only allowed once
raise _INET6_PTON_EXC
elif i + 1 == nparts and '.' in part:
# The last part of an IPv6 address can be an IPv4 address
if part.count('.') != 3:
# we have to do this since socket.inet_aton('1.2') ==
# b'\x01\x00\x00\x02'
raise _INET6_PTON_EXC
try:
result += socket.inet_aton(part)
except socket.error:
raise _INET6_PTON_EXC
else:
# Each part must be 16bit. Add missing zeroes before decoding.
try:
result += hex_bytes(part.rjust(4, "0"))
except (binascii.Error, TypeError):
raise _INET6_PTON_EXC
# If there's a wildcard, fill up with zeros to reach 128bit (16 bytes)
if joker_pos is not None:
if len(result) == 16:
raise _INET6_PTON_EXC
result = (result[:joker_pos] + b"\x00" * (16 - len(result))
+ result[joker_pos:])
if len(result) != 16:
raise _INET6_PTON_EXC
return result
_INET_PTON = {
socket.AF_INET: socket.inet_aton,
socket.AF_INET6: _inet6_pton,
}
def inet_pton(af, addr):
"""Convert an IP address from text representation into binary form."""
# Will replace Net/Net6 objects
addr = plain_str(addr)
# Use inet_pton if available
try:
return socket.inet_pton(af, addr)
except AttributeError:
try:
return _INET_PTON[af](addr)
except KeyError:
raise socket.error("Address family not supported by protocol")
def _inet6_ntop(addr):
"""Convert an IPv6 address from binary form into text representation,
used when socket.inet_pton is not available.
"""
# IPv6 addresses have 128bits (16 bytes)
if len(addr) != 16:
raise ValueError("invalid length of packed IP address string")
# Decode to hex representation
address = ":".join(bytes_hex(addr[idx:idx + 2]).decode().lstrip('0') or '0'
for idx in range(0, 16, 2))
try:
# Get the longest set of zero blocks. We need to take a look
# at group 1 regarding the length, as 0:0:1:0:0:2:3:4 would
# have two matches: 0:0: and :0:0: where the latter is longer,
# though the first one should be taken. Group 1 is in both
# cases 0:0.
match = max(_IP6_ZEROS.finditer(address),
key=lambda m: m.end(1) - m.start(1))
return '{}::{}'.format(address[:match.start()], address[match.end():])
except ValueError:
return address
_INET_NTOP = {
socket.AF_INET: socket.inet_ntoa,
socket.AF_INET6: _inet6_ntop,
}
def inet_ntop(af, addr):
"""Convert an IP address from binary form into text representation."""
# Use inet_ntop if available
addr = raw(addr)
try:
return socket.inet_ntop(af, addr)
except AttributeError:
try:
return _INET_NTOP[af](addr)
except KeyError:
raise ValueError("unknown address family %d" % af)