Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2018 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9 

10import array 

11from six import string_types 

12 

13class IP6_Address: 

14 ADDRESS_BYTE_SIZE = 16 

15 #A Hex Group is a 16-bit unit of the address 

16 TOTAL_HEX_GROUPS = 8 

17 HEX_GROUP_SIZE = 4 #Size in characters 

18 TOTAL_SEPARATORS = TOTAL_HEX_GROUPS - 1 

19 ADDRESS_TEXT_SIZE = (TOTAL_HEX_GROUPS * HEX_GROUP_SIZE) + TOTAL_SEPARATORS 

20 SEPARATOR = ":" 

21 SCOPE_SEPARATOR = "%" 

22 

23############################################################################################################# 

24# Constructor and construction helpers 

25 

26 def __init__(self, address): 

27 #The internal representation of an IP6 address is a 16-byte array 

28 self.__bytes = array.array('B', b'\0' * self.ADDRESS_BYTE_SIZE) 

29 self.__scope_id = "" 

30 

31 #Invoke a constructor based on the type of the argument 

32 if isinstance(address, string_types): 

33 self.__from_string(address) 

34 else: 

35 self.__from_bytes(address) 

36 

37 

38 def __from_string(self, address): 

39 #Separate the Scope ID, if present 

40 if self.__is_a_scoped_address(address): 

41 split_parts = address.split(self.SCOPE_SEPARATOR) 

42 address = split_parts[0] 

43 if split_parts[1] == "": 

44 raise Exception("Empty scope ID") 

45 self.__scope_id = split_parts[1] 

46 

47 #Expand address if it's in compressed form 

48 if self.__is_address_in_compressed_form(address): 

49 address = self.__expand_compressed_address(address) 

50 

51 #Insert leading zeroes where needed  

52 address = self.__insert_leading_zeroes(address) 

53 

54 #Sanity check 

55 if len(address) != self.ADDRESS_TEXT_SIZE: 

56 raise Exception('IP6_Address - from_string - address size != ' + str(self.ADDRESS_TEXT_SIZE)) 

57 

58 #Split address into hex groups 

59 hex_groups = address.split(self.SEPARATOR) 

60 if len(hex_groups) != self.TOTAL_HEX_GROUPS: 

61 raise Exception('IP6_Address - parsed hex groups != ' + str(self.TOTAL_HEX_GROUPS)) 

62 

63 #For each hex group, convert it into integer words 

64 offset = 0 

65 for group in hex_groups: 

66 if len(group) != self.HEX_GROUP_SIZE: 66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true

67 raise Exception('IP6_Address - parsed hex group length != ' + str(self.HEX_GROUP_SIZE)) 

68 

69 group_as_int = int(group, 16) 

70 self.__bytes[offset] = (group_as_int & 0xFF00) >> 8 

71 self.__bytes[offset + 1] = (group_as_int & 0x00FF) 

72 offset += 2 

73 

74 def __from_bytes(self, theBytes): 

75 if len(theBytes) != self.ADDRESS_BYTE_SIZE: 

76 raise Exception ("IP6_Address - from_bytes - array size != " + str(self.ADDRESS_BYTE_SIZE)) 

77 self.__bytes = theBytes 

78 

79############################################################################################################# 

80# Projectors 

81 def as_string(self, compress_address = True, scoped_address = True): 

82 s = "" 

83 for i, v in enumerate(self.__bytes): 

84 s += hex(v)[2:].rjust(2, '0') 

85 if i % 2 == 1: 

86 s += self.SEPARATOR 

87 s = s[:-1].upper() 

88 

89 if compress_address: 

90 s = self.__trim_leading_zeroes(s) 

91 s = self.__trim_longest_zero_chain(s) 

92 

93 if scoped_address and self.get_scope_id() != "": 

94 s += self.SCOPE_SEPARATOR + self.__scope_id 

95 return s 

96 

97 def as_bytes(self): 

98 return self.__bytes 

99 

100 def __str__(self): 

101 return self.as_string() 

102 

103 def get_scope_id(self): 

104 return self.__scope_id 

105 

106 def get_unscoped_address(self): 

107 return self.as_string(True, False) #Compressed address = True, Scoped address = False 

108 

109############################################################################################################# 

110# Semantic helpers 

111 def is_multicast(self): 

112 return self.__bytes[0] == 0xFF 

113 

114 def is_unicast(self): 

115 return self.__bytes[0] == 0xFE 

116 

117 def is_link_local_unicast(self): 

118 return self.is_unicast() and (self.__bytes[1] & 0xC0 == 0x80) 

119 

120 def is_site_local_unicast(self): 

121 return self.is_unicast() and (self.__bytes[1] & 0xC0 == 0xC0) 

122 

123 def is_unique_local_unicast(self): 

124 return self.__bytes[0] == 0xFD 

125 

126 

127 def get_human_readable_address_type(self): 

128 if self.is_multicast(): 

129 return "multicast" 

130 elif self.is_unicast(): 

131 if self.is_link_local_unicast(): 

132 return "link-local unicast" 

133 elif self.is_site_local_unicast(): 

134 return "site-local unicast" 

135 else: 

136 return "unicast" 

137 elif self.is_unique_local_unicast(): 

138 return "unique-local unicast" 

139 else: 

140 return "unknown type" 

141 

142############################################################################################################# 

143#Expansion helpers 

144 

145 #Predicate - returns whether an address is in compressed form 

146 def __is_address_in_compressed_form(self, address): 

147 #Sanity check - triple colon detection (not detected by searches of double colon)  

148 if address.count(self.SEPARATOR * 3) > 0: 

149 raise Exception('IP6_Address - found triple colon') 

150 

151 #Count the double colon marker 

152 compression_marker_count = self.__count_compression_marker(address) 

153 if compression_marker_count == 0: 

154 return False 

155 elif compression_marker_count == 1: 155 ↛ 158line 155 didn't jump to line 158, because the condition on line 155 was never false

156 return True 

157 else: 

158 raise Exception('IP6_Address - more than one compression marker (\"::\") found') 

159 

160 #Returns how many hex groups are present, in a compressed address  

161 def __count_compressed_groups(self, address): 

162 trimmed_address = address.replace(self.SEPARATOR * 2, self.SEPARATOR) #Replace "::" with ":"  

163 return trimmed_address.count(self.SEPARATOR) + 1 

164 

165 #Counts how many compression markers are present 

166 def __count_compression_marker(self, address): 

167 return address.count(self.SEPARATOR * 2) #Count occurrences of "::" 

168 

169 #Inserts leading zeroes in every hex group 

170 def __insert_leading_zeroes(self, address): 

171 hex_groups = address.split(self.SEPARATOR) 

172 

173 new_address = "" 

174 for hex_group in hex_groups: 

175 if len(hex_group) < 4: 

176 hex_group = hex_group.rjust(4, "0") 

177 new_address += hex_group + self.SEPARATOR 

178 

179 return new_address[:-1] #Trim the last colon 

180 

181 

182 #Expands a compressed address 

183 def __expand_compressed_address(self, address): 

184 group_count = self.__count_compressed_groups(address) 

185 groups_to_insert = self.TOTAL_HEX_GROUPS - group_count 

186 

187 pos = address.find(self.SEPARATOR * 2) + 1 

188 while groups_to_insert: 

189 address = address[:pos] + "0000" + self.SEPARATOR + address[pos:] 

190 pos += 5 

191 groups_to_insert -= 1 

192 

193 #Replace the compression marker with a single colon  

194 address = address.replace(self.SEPARATOR * 2, self.SEPARATOR) 

195 return address 

196 

197 

198############################################################################################################# 

199#Compression helpers 

200 

201 def __trim_longest_zero_chain(self, address): 

202 chain_size = 8 

203 

204 while chain_size > 0: 

205 groups = address.split(self.SEPARATOR) 

206 

207 for index, group in enumerate(groups): 

208 #Find the first zero 

209 if group == "0": 

210 start_index = index 

211 end_index = index 

212 #Find the end of this chain of zeroes 

213 while end_index < 7 and groups[end_index + 1] == "0": 

214 end_index += 1 

215 

216 #If the zero chain matches the current size, trim it 

217 found_size = end_index - start_index + 1 

218 if found_size == chain_size: 

219 address = self.SEPARATOR.join(groups[0:start_index]) + self.SEPARATOR * 2 + self.SEPARATOR.join(groups[(end_index+1):]) 

220 return address 

221 

222 #No chain of this size found, try with a lower size  

223 chain_size -= 1 

224 return address 

225 

226 

227 #Trims all leading zeroes from every hex group 

228 def __trim_leading_zeroes(self, theStr): 

229 groups = theStr.split(self.SEPARATOR) 

230 theStr = "" 

231 

232 for group in groups: 

233 group = group.lstrip("0") + self.SEPARATOR 

234 if group == self.SEPARATOR: 

235 group = "0" + self.SEPARATOR 

236 theStr += group 

237 return theStr[:-1] 

238 

239 

240############################################################################################################# 

241 @classmethod 

242 def is_a_valid_text_representation(cls, text_representation): 

243 try: 

244 #Capitalize on the constructor's ability to detect invalid text representations of an IP6 address  

245 IP6_Address(text_representation) 

246 return True 

247 except Exception: 

248 return False 

249 

250 def __is_a_scoped_address(self, text_representation): 

251 return text_representation.count(self.SCOPE_SEPARATOR) == 1