xref: /aosp_15_r20/external/scapy/scapy/pton_ntop.py (revision 7dc08ffc4802948ccbc861daaf1e81c405c2c4bd)
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <[email protected]>
4## This program is published under a GPLv2 license
5
6"""
7Convert IPv6 addresses between textual representation and binary.
8
9These functions are missing when python is compiled
10without IPv6 support, on Windows for instance.
11"""
12
13from __future__ import absolute_import
14import socket
15import re
16import binascii
17from scapy.modules.six.moves import range
18from scapy.compat import *
19
20_IP6_ZEROS = re.compile('(?::|^)(0(?::0)+)(?::|$)')
21_INET6_PTON_EXC = socket.error("illegal IP address string passed to inet_pton")
22
23def _inet6_pton(addr):
24    """Convert an IPv6 address from text representation into binary form,
25used when socket.inet_pton is not available.
26
27    """
28    joker_pos = None
29    result = b""
30    addr = plain_str(addr)
31    if addr == '::':
32        return b'\x00' * 16
33    if addr.startswith('::'):
34        addr = addr[1:]
35    if addr.endswith('::'):
36        addr = addr[:-1]
37    parts = addr.split(":")
38    nparts = len(parts)
39    for i, part in enumerate(parts):
40        if not part:
41            # "::" indicates one or more groups of 2 null bytes
42            if joker_pos is None:
43                joker_pos = len(result)
44            else:
45                # Wildcard is only allowed once
46                raise _INET6_PTON_EXC
47        elif i + 1 == nparts and '.' in part:
48            # The last part of an IPv6 address can be an IPv4 address
49            if part.count('.') != 3:
50                # we have to do this since socket.inet_aton('1.2') ==
51                # b'\x01\x00\x00\x02'
52                raise _INET6_PTON_EXC
53            try:
54                result += socket.inet_aton(part)
55            except socket.error:
56                raise _INET6_PTON_EXC
57        else:
58            # Each part must be 16bit. Add missing zeroes before decoding.
59            try:
60                result += hex_bytes(part.rjust(4, "0"))
61            except (binascii.Error, TypeError):
62                raise _INET6_PTON_EXC
63    # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes)
64    if joker_pos is not None:
65        if len(result) == 16:
66            raise _INET6_PTON_EXC
67        result = (result[:joker_pos] + b"\x00" * (16 - len(result))
68                  + result[joker_pos:])
69    if len(result) != 16:
70        raise _INET6_PTON_EXC
71    return result
72
73
74_INET_PTON = {
75    socket.AF_INET: socket.inet_aton,
76    socket.AF_INET6: _inet6_pton,
77}
78
79
80def inet_pton(af, addr):
81    """Convert an IP address from text representation into binary form."""
82    # Will replace Net/Net6 objects
83    addr = plain_str(addr)
84    # Use inet_pton if available
85    try:
86        return socket.inet_pton(af, addr)
87    except AttributeError:
88        try:
89            return _INET_PTON[af](addr)
90        except KeyError:
91            raise socket.error("Address family not supported by protocol")
92
93
94def _inet6_ntop(addr):
95    """Convert an IPv6 address from binary form into text representation,
96used when socket.inet_pton is not available.
97
98    """
99    # IPv6 addresses have 128bits (16 bytes)
100    if len(addr) != 16:
101        raise ValueError("invalid length of packed IP address string")
102
103    # Decode to hex representation
104    address = ":".join(bytes_hex(addr[idx:idx + 2]).decode().lstrip('0') or '0'
105                       for idx in range(0, 16, 2))
106
107    try:
108        # Get the longest set of zero blocks. We need to take a look
109        # at group 1 regarding the length, as 0:0:1:0:0:2:3:4 would
110        # have two matches: 0:0: and :0:0: where the latter is longer,
111        # though the first one should be taken. Group 1 is in both
112        # cases 0:0.
113        match = max(_IP6_ZEROS.finditer(address),
114                    key=lambda m: m.end(1) - m.start(1))
115        return '{}::{}'.format(address[:match.start()], address[match.end():])
116    except ValueError:
117        return address
118
119
120_INET_NTOP = {
121    socket.AF_INET: socket.inet_ntoa,
122    socket.AF_INET6: _inet6_ntop,
123}
124
125
126def inet_ntop(af, addr):
127    """Convert an IP address from binary form into text representation."""
128    # Use inet_ntop if available
129    addr = raw(addr)
130    try:
131        return socket.inet_ntop(af, addr)
132    except AttributeError:
133        try:
134            return _INET_NTOP[af](addr)
135        except KeyError:
136            raise ValueError("unknown address family %d" % af)
137