Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/pton_ntop.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

77 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Philippe Biondi <phil@secdev.org> 

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 

13import socket 

14import re 

15import binascii 

16from scapy.compat import plain_str, hex_bytes, bytes_encode, bytes_hex 

17 

18# Typing imports 

19from typing import Union 

20 

21_IP6_ZEROS = re.compile('(?::|^)(0(?::0)+)(?::|$)') 

22_INET6_PTON_EXC = socket.error("illegal IP address string passed to inet_pton") 

23 

24 

25def _inet6_pton(addr): 

26 # type: (str) -> bytes 

27 """Convert an IPv6 address from text representation into binary form, 

28used when socket.inet_pton is not available. 

29 

30 """ 

31 joker_pos = None 

32 result = b"" 

33 addr = plain_str(addr) 

34 if addr == '::': 

35 return b'\x00' * 16 

36 if addr.startswith('::'): 

37 addr = addr[1:] 

38 if addr.endswith('::'): 

39 addr = addr[:-1] 

40 parts = addr.split(":") 

41 nparts = len(parts) 

42 for i, part in enumerate(parts): 

43 if not part: 

44 # "::" indicates one or more groups of 2 null bytes 

45 if joker_pos is None: 

46 joker_pos = len(result) 

47 else: 

48 # Wildcard is only allowed once 

49 raise _INET6_PTON_EXC 

50 elif i + 1 == nparts and '.' in part: 

51 # The last part of an IPv6 address can be an IPv4 address 

52 if part.count('.') != 3: 

53 # we have to do this since socket.inet_aton('1.2') == 

54 # b'\x01\x00\x00\x02' 

55 raise _INET6_PTON_EXC 

56 try: 

57 result += socket.inet_aton(part) 

58 except socket.error: 

59 raise _INET6_PTON_EXC 

60 else: 

61 # Each part must be 16bit. Add missing zeroes before decoding. 

62 try: 

63 result += hex_bytes(part.rjust(4, "0")) 

64 except (binascii.Error, TypeError): 

65 raise _INET6_PTON_EXC 

66 # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) 

67 if joker_pos is not None: 

68 if len(result) == 16: 

69 raise _INET6_PTON_EXC 

70 result = (result[:joker_pos] + b"\x00" * (16 - len(result)) + 

71 result[joker_pos:]) 

72 if len(result) != 16: 

73 raise _INET6_PTON_EXC 

74 return result 

75 

76 

77_INET_PTON = { 

78 socket.AF_INET: socket.inet_aton, 

79 socket.AF_INET6: _inet6_pton, 

80} 

81 

82 

83def inet_pton(af, addr): 

84 # type: (socket.AddressFamily, Union[bytes, str]) -> bytes 

85 """Convert an IP address from text representation into binary form.""" 

86 # Will replace Net/Net6 objects 

87 addr = plain_str(addr) 

88 # Use inet_pton if available 

89 try: 

90 if not socket.has_ipv6: 

91 raise AttributeError 

92 return socket.inet_pton(af, addr) 

93 except AttributeError: 

94 try: 

95 return _INET_PTON[af](addr) 

96 except KeyError: 

97 raise socket.error("Address family not supported by protocol") 

98 

99 

100def _inet6_ntop(addr): 

101 # type: (bytes) -> str 

102 """Convert an IPv6 address from binary form into text representation, 

103used when socket.inet_pton is not available. 

104 

105 """ 

106 # IPv6 addresses have 128bits (16 bytes) 

107 if len(addr) != 16: 

108 raise ValueError("invalid length of packed IP address string") 

109 

110 # Decode to hex representation 

111 address = ":".join(plain_str(bytes_hex(addr[idx:idx + 2])).lstrip('0') or '0' # noqa: E501 

112 for idx in range(0, 16, 2)) 

113 

114 try: 

115 # Get the longest set of zero blocks. We need to take a look 

116 # at group 1 regarding the length, as 0:0:1:0:0:2:3:4 would 

117 # have two matches: 0:0: and :0:0: where the latter is longer, 

118 # though the first one should be taken. Group 1 is in both 

119 # cases 0:0. 

120 match = max(_IP6_ZEROS.finditer(address), 

121 key=lambda m: m.end(1) - m.start(1)) 

122 return '{}::{}'.format(address[:match.start()], address[match.end():]) 

123 except ValueError: 

124 return address 

125 

126 

127_INET_NTOP = { 

128 socket.AF_INET: socket.inet_ntoa, 

129 socket.AF_INET6: _inet6_ntop, 

130} 

131 

132 

133def inet_ntop(af, addr): 

134 # type: (socket.AddressFamily, bytes) -> str 

135 """Convert an IP address from binary form into text representation.""" 

136 # Use inet_ntop if available 

137 addr = bytes_encode(addr) 

138 try: 

139 if not socket.has_ipv6: 

140 raise AttributeError 

141 return socket.inet_ntop(af, addr) 

142 except AttributeError: 

143 try: 

144 return _INET_NTOP[af](addr) 

145 except KeyError: 

146 raise ValueError("unknown address family %d" % af)