Package netaddr :: Module strategy
[frames] | no frames]

Source Code for Module netaddr.strategy

  1  #!/usr/bin/env python 
  2  #----------------------------------------------------------------------------- 
  3  #   Copyright (c) 2008, David P. D. Moss. All rights reserved. 
  4  # 
  5  #   Released under the BSD license. See the LICENSE file for details. 
  6  #----------------------------------------------------------------------------- 
  7  """ 
  8  network address conversion logic, constants and shared strategy objects. 
  9  """ 
 10  import socket as _socket 
 11  import struct as _struct 
 12  import re as _re 
 13   
 14  USE_IPV4_OPT=True   #: Use optimised IPv4 strategy? Default: True 
 15   
 16  try: 
 17      #   Detects a common bug in various Python implementations. 
 18      _socket.inet_aton('255.255.255.255') 
 19  except: 
 20      USE_IPV4_OPT=False 
 21   
 22  from netaddr import BIG_ENDIAN_PLATFORM, AT_UNSPEC, AT_INET, AT_INET6, \ 
 23                      AT_LINK, AT_EUI64, AT_NAMES, AddrFormatError 
 24   
 25  #----------------------------------------------------------------------------- 
26 -def _BYTES_TO_BITS():
27 """ 28 Generates a 256 element list of 8-bit binary digit strings. List index is 29 equivalent to the bit string value. 30 """ 31 lookup = [] 32 bits_per_byte = range(7, -1, -1) 33 for num in range(256): 34 bits = 8*[None] 35 for i in bits_per_byte: 36 bits[i] = '01'[num&1] 37 num >>= 1 38 lookup.append(''.join(bits)) 39 return lookup
40 41 _BYTES_TO_BITS = _BYTES_TO_BITS() 42 43 #-----------------------------------------------------------------------------
44 -class AddrStrategy(object):
45 """Basic support for common operations performed on each address type""" 46 47 #: Lookup table for struct module format strings. 48 STRUCT_FORMATS = { 49 8 : 'B', # unsigned char 50 16 : 'H', # unsigned short 51 32 : 'I', # unsigned int 52 } 53
54 - def __init__(self, width, word_size, word_sep, word_fmt='%x', 55 addr_type=AT_UNSPEC, word_base=16, uppercase=False):
56 """ 57 Constructor. 58 59 @param width: size of address in bits. 60 (e.g. 32 - IPv4, 48 - MAC, 128 - IPv6) 61 62 @param word_size: size of each word. 63 (e.g. 8 - octets, 16 - hextets) 64 65 @param word_sep: separator between each word. 66 (e.g. '.' - IPv4, ':' - IPv6, '-' - EUI-48) 67 68 @param word_fmt: format string for each word. 69 (Default: '%x') 70 71 @param addr_type: address type. 72 (Default: AT_UNSPEC) 73 74 @param word_base: number base used to convert each word using int(). 75 (Default: 16) 76 77 @param uppercase: uppercase address. 78 (Default: False) 79 """ 80 81 self.width = width 82 self.max_int = 2 ** width - 1 83 self.word_size = word_size 84 self.num_words = width / word_size 85 self.max_word = 2 ** word_size - 1 86 self.word_sep = word_sep 87 self.word_fmt = word_fmt 88 self.word_base = word_base 89 self.addr_type = addr_type 90 self.uppercase = uppercase 91 92 try: 93 self.name = AT_NAMES[addr_type] 94 except KeyError: 95 self.name = AT_NAMES[AT_UNSPEC]
96
97 - def __repr__(self):
98 """@return: executable Python string to recreate equivalent object""" 99 return "%s(%r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, 100 self.width, self.word_size, self.word_sep, self.addr_type, 101 self.word_base, self.uppercase)
102 103 #------------------------------------------------------------------------- 104 # Binary methods. 105 #------------------------------------------------------------------------- 106
107 - def valid_bits(self, bits):
108 """ 109 @param bits: A network address in readable binary form. 110 111 @return: C{True} if network address is valid for this address type, 112 C{False} otherwise. 113 """ 114 if not isinstance(bits, (str, unicode)): 115 return False 116 117 bits = bits.replace(self.word_sep, '') 118 119 if len(bits) != self.width: 120 return False 121 122 try: 123 if 0 <= int(bits, 2) <= self.max_int: 124 return True 125 except ValueError: 126 return False 127 return False
128
129 - def bits_to_int(self, bits):
130 """ 131 @param bits: A network address in readable binary form. 132 133 @return: A network byte order integer that is equivalent to value 134 represented by network address in readable binary form. 135 """ 136 if not self.valid_bits(bits): 137 raise ValueError('%r is not a valid binary form string for ' \ 138 'address type!' % bits) 139 140 word_bits = bits.split(self.word_sep) 141 if len(word_bits) != self.num_words: 142 raise ValueError('invalid number of words within binary form ' \ 143 'string for address type!' % bits) 144 145 words = tuple([int(i, 2) for i in word_bits]) 146 147 return self.words_to_int(words)
148 149 #------------------------------------------------------------------------- 150 # Integer methods. 151 #------------------------------------------------------------------------- 152
153 - def valid_int(self, int_val):
154 """ 155 @param int_val: A network byte order integer. 156 157 @return: C{True} if network byte order integer falls within the 158 boundaries of this address type, C{False} otherwise. 159 """ 160 if not isinstance(int_val, (int, long)): 161 return False 162 163 return 0 <= int_val <= self.max_int
164
165 - def int_to_str(self, int_val):
166 """ 167 @param int_val: A network byte order integer. 168 169 @return: A network address in string form that is equivalent to value 170 represented by a network byte order integer. 171 """ 172 words = self.int_to_words(int_val) 173 tokens = [self.word_fmt % i for i in words] 174 addr = self.word_sep.join(tokens) 175 176 if self.uppercase is True: 177 return addr.upper() 178 179 return addr
180
181 - def int_to_bits(self, int_val):
182 """ 183 @param int_val: A network byte order integer. 184 185 @return: A network address in readable binary form that is equivalent 186 to value represented by a network byte order integer. 187 """ 188 bit_words = [] 189 190 for word in self.int_to_words(int_val): 191 bits = [] 192 while word: 193 bits.append(_BYTES_TO_BITS[word&255]) 194 word >>= 8 195 bits.reverse() 196 bit_str = ''.join(bits) or '0'*self.word_size 197 bits = ('0'*self.word_size+bit_str)[-self.word_size:] 198 bit_words.append(bits) 199 200 return self.word_sep.join(bit_words)
201
202 - def int_to_bin(self, int_val):
203 """ 204 @param int_val: A network byte order integer. 205 206 @return: A network address in standard binary representation format 207 that is equivalent to integer address value. Essentially a back 208 port of the bin() builtin in Python 2.6.x and higher. 209 """ 210 bit_words = [] 211 212 for word in self.int_to_words(int_val): 213 bits = [] 214 while word: 215 bits.append(_BYTES_TO_BITS[word&255]) 216 word >>= 8 217 bits.reverse() 218 bit_str = ''.join(bits) or '0'*self.word_size 219 bits = ('0'*self.word_size+bit_str)[-self.word_size:] 220 bit_words.append(bits) 221 222 return '0b' + ''.join(bit_words)
223
224 - def int_to_words(self, int_val, num_words=None, word_size=None):
225 """ 226 @param int_val: A network byte order integer to be split. 227 228 @param num_words: (optional) number of words expected in return value 229 tuple. Uses address type default if not specified. 230 231 @param word_size: (optional) size/width of individual words (in bits). 232 Uses address type default if not specified. 233 """ 234 if not self.valid_int(int_val): 235 raise IndexError('integer %r is out of bounds!' % hex(int_val)) 236 237 # Set defaults for optional args. 238 if num_words is None: 239 num_words = self.num_words 240 if word_size is None: 241 word_size = self.word_size 242 243 max_word_size = 2 ** word_size - 1 244 245 words = [] 246 for _ in range(num_words): 247 word = int_val & max_word_size 248 words.append(int(word)) 249 int_val >>= word_size 250 words.reverse() 251 252 return tuple(words)
253
254 - def int_to_packed(self, int_val):
255 """ 256 @param int_val: the integer to be packed. 257 258 @return: a packed string that is equivalent to value represented by a 259 network byte order integer. 260 """ 261 262 words = self.int_to_words(int_val, self.num_words, self.word_size) 263 264 try: 265 fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[ 266 self.word_size]) 267 except KeyError: 268 raise ValueError('unsupported word size: %d!' % self.word_size) 269 270 return _struct.pack(fmt, *words)
271 272 273 #------------------------------------------------------------------------- 274 # Packed string methods. 275 #------------------------------------------------------------------------- 276
277 - def packed_to_int(self, packed_int):
278 """ 279 @param packed_int: a packed string containing an unsigned integer. 280 Network byte order is assumed. 281 282 @return: A network byte order integer that is equivalent to value 283 of network address represented by packed binary string. 284 """ 285 try: 286 fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[ 287 self.word_size]) 288 except KeyError: 289 raise ValueError('unsupported word size: %d!' % self.word_size) 290 291 words = list(_struct.unpack(fmt, packed_int)) 292 words.reverse() 293 294 int_val = 0 295 for i, num in enumerate(words): 296 word = num 297 word = word << self.word_size * i 298 int_val = int_val | word 299 300 return int_val
301 302 #------------------------------------------------------------------------- 303 # String methods. 304 #------------------------------------------------------------------------- 305
306 - def valid_str(self, addr):
307 """ 308 @param addr: A network address in string form. 309 310 @return: C{True} if network address in string form is valid for this 311 address type, C{False} otherwise. 312 """ 313 if not isinstance(addr, (str, unicode)): 314 return False 315 316 tokens = addr.split(self.word_sep) 317 if len(tokens) != self.num_words: 318 return False 319 320 try: 321 for token in tokens: 322 if not 0 <= int(token, self.word_base) <= \ 323 self.max_word: 324 return False 325 except TypeError: 326 return False 327 except ValueError: 328 return False 329 return True
330
331 - def str_to_int(self, addr):
332 """ 333 @param addr: A network address in string form. 334 335 @return: A network byte order integer that is equivalent to value 336 represented by network address in string form. 337 """ 338 if not self.valid_str(addr): 339 raise ValueError('%r is not a recognised string representation' \ 340 ' of this address type!' % addr) 341 342 tokens = addr.split(self.word_sep) 343 words = [ int(token, self.word_base) for token in tokens ] 344 345 return self.words_to_int(words)
346 347 #------------------------------------------------------------------------- 348 # Word list methods. 349 #------------------------------------------------------------------------- 350
351 - def valid_words(self, words):
352 """ 353 @param words: A sequence containing integer word values. 354 355 @return: C{True} if word sequence is valid for this address type, 356 C{False} otherwise. 357 """ 358 if not hasattr(words, '__iter__'): 359 return False 360 361 if len(words) != self.num_words: 362 return False 363 364 for i in words: 365 if not isinstance(i, (int, long)): 366 return False 367 368 if not 0 <= i <= self.max_word: 369 return False 370 return True
371
372 - def words_to_int(self, words):
373 """ 374 @param words: A list or tuple containing integer word values. 375 376 @return: A network byte order integer that is equivalent to value 377 represented by word sequence. 378 """ 379 if not self.valid_words(words): 380 raise ValueError('%r is not a valid word list!' % words) 381 382 # tuples have no reverse() method and reversed() is only available 383 # in Python 2.4. Ugly but necessary. 384 if isinstance(words, tuple): 385 words = list(words) 386 words.reverse() 387 388 int_val = 0 389 for i, num in enumerate(words): 390 word = num 391 word = word << self.word_size * i 392 int_val = int_val | word 393 394 return int_val
395 396 #-----------------------------------------------------------------------------
397 -class IPv4StrategyStd(AddrStrategy):
398 """ 399 A 'safe' L{AddrStrategy} for IPv4 addresses. Unlike L{IPv4StrategyOpt}. 400 401 It contains all methods related to IPv4 addresses that the optimised 402 version has, without the reliance on the socket or struct modules. There 403 are several cases where the use of this class are preferable when either 404 the modules mentioned do not exist on certain Python implementations or 405 contain bugs like the infamous inet_aton('255.255.255.254') bug. 406 407 All methods shared between the optimised class and this one should be 408 defined here. 409 """
410 - def __init__(self):
411 """Constructor.""" 412 super(IPv4StrategyStd, self).__init__(width=32, word_size=8, 413 word_fmt='%d', word_sep='.', addr_type=AT_INET, word_base=10)
414
415 - def int_to_arpa(self, int_val):
416 """ 417 @param int_val: A network byte order integer. 418 419 @return: The reverse DNS lookup for an IPv4 address in network byte 420 order integer form. 421 """ 422 words = ["%d" % i for i in self.int_to_words(int_val)] 423 words.reverse() 424 words.extend(['in-addr', 'arpa', '']) 425 return '.'.join(words)
426 427 #-----------------------------------------------------------------------------
428 -class IPv4StrategyOpt(IPv4StrategyStd):
429 """ 430 An optimised L{AddrStrategy} for IPv4 addresses. 431 432 It uses C{pack()} and C{unpack()} from the C{struct} module along with the 433 C{inet_ntoa()} and C{inet_aton()} from the C{socket} module great improve 434 the speed of certain operations (approx. 2.5 times faster than a standard 435 L{AddrStrategy} configured for IPv4). 436 437 However, keep in mind that these modules might not be available everywhere 438 that Python itself is. Runtimes such as Google App Engine gut the 439 C{socket} module. C{struct} is also limited to processing 32-bit integers 440 which is fine for IPv4 but isn't suitable for IPv6. 441 """
442 - def __init__(self):
443 """Constructor.""" 444 super(IPv4StrategyOpt, self).__init__()
445
446 - def valid_str(self, addr):
447 """ 448 @param addr: An IP address in presentation (string) format. 449 450 @return: C{True} if network address in string form is valid for this 451 address type, C{False} otherwise. 452 """ 453 try: 454 # This call handles a lot of older IPv4 address formats that 455 # it would be a lot of work to implement (many edge cases). 456 # Please Note: only this optimised strategy class will have 457 # the ability to parse these less common formats for the 458 # moment. 459 _socket.inet_aton(addr) 460 except _socket.error: 461 return False 462 except TypeError: 463 return False 464 return True
465
466 - def str_to_int(self, addr):
467 """ 468 @param addr: An IPv4 dotted decimal address in string form. 469 470 @return: A network byte order integer that is equivalent to value 471 represented by the IPv4 dotted decimal address string. 472 """ 473 if not self.valid_str(addr): 474 raise ValueError('%r is not a valid IPv4 address string!' \ 475 % addr) 476 return _struct.unpack('>I', _socket.inet_aton(addr))[0]
477
478 - def int_to_str(self, int_val):
479 """ 480 @param int_val: A network byte order integer. 481 482 @return: An IPv4 dotted decimal address string that is equivalent to 483 value represented by a 32 bit integer in network byte order. 484 """ 485 if not self.valid_int(int_val): 486 raise ValueError('%r is not a valid 32-bit integer!' % int_val) 487 return _socket.inet_ntoa(_struct.pack('>I', int_val))
488
489 - def int_to_words(self, int_val, num_words=None, word_size=None):
490 """ 491 @param int_val: A network byte order integer. 492 493 @param num_words: (unused) *** interface compatibility only *** 494 495 @param word_size: (unused) *** interface compatibility only *** 496 497 @return: An integer word (octet) sequence that is equivalent to value 498 represented by network byte order integer. 499 """ 500 if not self.valid_int(int_val): 501 raise ValueError('%r is not a valid integer value supported ' \ 502 'by this address type!' % int_val) 503 return _struct.unpack('4B', _struct.pack('>I', int_val))
504
505 - def words_to_int(self, octets):
506 """ 507 @param octets: A list or tuple containing integer octets. 508 509 @return: A network byte order integer that is equivalent to value 510 represented by word (octet) sequence. 511 """ 512 if not self.valid_words(octets): 513 raise ValueError('%r is not a valid octet list for an IPv4 ' \ 514 'address!' % octets) 515 return _struct.unpack('>I', _struct.pack('4B', *octets))[0]
516 517 #-----------------------------------------------------------------------------
518 -class IPv6Strategy(AddrStrategy):
519 """ 520 Implements the operations that can be performed on an Internet Protocol 521 version 6 network address in accordance with RFC 4291. 522 523 NB - This class would benefit greatly from access to inet_pton/inet_ntop() 524 function calls in Python's socket module. Sadly, they aren't available so 525 we'll have to put up with the pure-Python implementation here (for now at 526 least). 527 """
528 - def __init__(self):
529 """Constructor.""" 530 super(self.__class__, self).__init__(addr_type=AT_INET6, 531 width=128, word_size=16, word_fmt='%x', word_sep=':')
532
533 - def valid_str(self, addr):
534 """ 535 @param addr: An IPv6 address in string form. 536 537 @return: C{True} if IPv6 network address string is valid, C{False} 538 otherwise. 539 """ 540 #TODO: Reduce the length of this method ... 541 if not isinstance(addr, (str, unicode)): 542 return False 543 544 if '::' in addr: 545 # IPv6 compact mode. 546 try: 547 prefix, suffix = addr.split('::') 548 except ValueError: 549 return False 550 551 l_prefix = [] 552 l_suffix = [] 553 554 if prefix != '': 555 l_prefix = prefix.split(':') 556 557 if suffix != '': 558 l_suffix = suffix.split(':') 559 560 # IPv6 compact IPv4 compatibility mode. 561 if len(l_suffix) and '.' in l_suffix[-1]: 562 ipv4_str = l_suffix[-1] 563 if ST_IPV4.valid_str(ipv4_str): 564 ipv4_int = ST_IPV4.str_to_int(ipv4_str) 565 ipv4_words = ST_IPV4.int_to_words(ipv4_int) 566 l_suffix.pop() 567 l_suffix.append( 568 ''.join(["%.2x" % i for i in ipv4_words[0:2]])) 569 l_suffix.append( 570 ''.join(["%.2x" % i for i in ipv4_words[2:4]])) 571 572 token_count = len(l_prefix) + len(l_suffix) 573 574 if not 0 <= token_count <= self.num_words - 1: 575 return False 576 577 try: 578 for token in l_prefix + l_suffix: 579 word = int(token, 16) 580 if not 0 <= word <= self.max_word: 581 return False 582 except ValueError: 583 return False 584 else: 585 # IPv6 verbose mode. 586 if ':' in addr: 587 tokens = addr.split(':') 588 589 if '.' in addr: 590 ipv6_prefix = tokens[:-1] 591 if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']: 592 return False 593 if ipv6_prefix[-1].lower() not in ('0', 'ffff'): 594 return False 595 # IPv6 verbose IPv4 compatibility mode. 596 if len(tokens) != (self.num_words - 1): 597 return False 598 ipv4_str = tokens[-1] 599 if ST_IPV4.valid_str(ipv4_str): 600 ipv4_int = ST_IPV4.str_to_int(ipv4_str) 601 ipv4_words = ST_IPV4.int_to_words(ipv4_int) 602 tokens.pop() 603 tokens.append( 604 ''.join(["%.2x" % i for i in ipv4_words[0:2]])) 605 tokens.append( 606 ''.join(["%.2x" % i for i in ipv4_words[2:4]])) 607 else: 608 # IPv6 verbose mode. 609 if len(tokens) != self.num_words: 610 return False 611 try: 612 for token in tokens: 613 word = int(token, 16) 614 if not 0 <= word <= self.max_word: 615 return False 616 except ValueError: 617 return False 618 else: 619 return False 620 621 return True
622
623 - def str_to_int(self, addr):
624 """ 625 @param addr: An IPv6 address in string form. 626 627 @return: The equivalent network byte order integer for a given IPv6 628 address. 629 """ 630 if not self.valid_str(addr): 631 raise ValueError("'%s' is an invalid IPv6 address!" % addr) 632 633 values = [] 634 635 if addr == '::': 636 # Unspecified address. 637 return 0 638 elif '::' in addr: 639 # Abbreviated form IPv6 address. 640 prefix, suffix = addr.split('::') 641 642 if prefix == '': 643 l_prefix = ['0'] 644 else: 645 l_prefix = prefix.split(':') 646 647 if suffix == '': 648 l_suffix = ['0'] 649 else: 650 l_suffix = suffix.split(':') 651 652 # Check for IPv4 compatibility address form. 653 if len(l_suffix) and '.' in l_suffix[-1]: 654 if len(l_suffix) > 2: 655 return False 656 if len(l_suffix) == 2 and l_suffix[0].lower() != 'ffff': 657 return False 658 659 ipv4_str = l_suffix[-1] 660 if ST_IPV4.valid_str(ipv4_str): 661 ipv4_int = ST_IPV4.str_to_int(ipv4_str) 662 ipv4_words = ST_IPV4.int_to_words(ipv4_int) 663 l_suffix.pop() 664 l_suffix.append( 665 ''.join(["%.2x" % i for i in ipv4_words[0:2]])) 666 l_suffix.append( 667 ''.join(["%.2x" % i for i in ipv4_words[2:4]])) 668 669 gap_size = 8 - ( len(l_prefix) + len(l_suffix) ) 670 671 values = ["%04x" % int(i, 16) for i in l_prefix] \ 672 + ['0000' for i in range(gap_size)] \ 673 + ["%04x" % int(i, 16) for i in l_suffix] 674 else: 675 # Verbose form IPv6 address. 676 if '.' in addr: 677 # IPv4 compatiblility mode. 678 tokens = addr.split(':') 679 ipv4_str = tokens[-1] 680 if ST_IPV4.valid_str(ipv4_str): 681 ipv4_int = ST_IPV4.str_to_int(ipv4_str) 682 ipv4_words = ST_IPV4.int_to_words(ipv4_int) 683 tokens.pop() 684 tokens.append(''.join(["%.2x" % i for i in ipv4_words[0:2]])) 685 tokens.append(''.join(["%.2x" % i for i in ipv4_words[2:4]])) 686 687 values = ["%04x" % int(i, 16) for i in tokens] 688 else: 689 # non IPv4 compatiblility mode. 690 values = ["%04x" % int(i, 16) for i in addr.split(':')] 691 692 value = int(''.join(values), 16) 693 694 return value
695
696 - def int_to_str(self, int_val, compact=True, word_fmt=None):
697 """ 698 @param int_val: A network byte order integer. 699 700 @param compact: (optional) A boolean flag indicating if compact 701 formatting should be used. If True, this method uses the '::' 702 string to represent the first adjacent group of words with a value 703 of zero. Default: True 704 705 @param word_fmt: (optional) The Python format string used to override 706 formatting for each word. 707 708 @return: The IPv6 string form equal to the network byte order integer 709 value provided. 710 """ 711 # Use basic parent class implementation if compact string form is 712 # not required. 713 if not compact: 714 return super(self.__class__, self).int_to_str(int_val) 715 716 _word_fmt = self.word_fmt 717 if word_fmt is not None: 718 _word_fmt = word_fmt 719 720 if not self.valid_int(int_val): 721 raise ValueError('%r is not a valid integer value supported ' \ 722 'by this address type!' % int_val) 723 724 tokens = [] 725 for i in range(self.num_words): 726 word = int_val & (2 ** self.word_size - 1) 727 tokens += [_word_fmt % word] 728 int_val >>= self.word_size 729 730 tokens.reverse() 731 732 if compact == True: 733 new_tokens = [] 734 735 positions = [] 736 within_run = False 737 start_index = None 738 num_tokens = 0 739 740 # Discover all runs of zeros. 741 for idx, token in enumerate(tokens): 742 if token == '0': 743 within_run = True 744 if start_index is None: 745 start_index = idx 746 num_tokens += 1 747 else: 748 if num_tokens > 1: 749 positions.append((num_tokens, start_index)) 750 within_run = False 751 start_index = None 752 num_tokens = 0 753 754 new_tokens.append(token) 755 756 # Store any position not saved before loop exit. 757 if num_tokens > 1: 758 positions.append((num_tokens, start_index)) 759 760 # Replace first longest run with an empty string. 761 if len(positions) != 0: 762 # Locate longest, left-most run of zeros. 763 positions.sort(lambda x, y: cmp(x[1], y[1])) 764 best_position = positions[0] 765 for position in positions: 766 if position[0] > best_position[0]: 767 best_position = position 768 # Replace chosen zero run. 769 (length, start_idx) = best_position 770 new_tokens = new_tokens[0:start_idx] + [''] + \ 771 new_tokens[start_idx+length:] 772 773 # Add start and end blanks so join creates '::'. 774 if new_tokens[0] == '': 775 new_tokens.insert(0, '') 776 777 if new_tokens[-1] == '': 778 new_tokens.append('') 779 780 tokens = new_tokens 781 782 return self.word_sep.join(tokens)
783
784 - def int_to_arpa(self, int_val):
785 """ 786 @param int_val: A network byte order integer. 787 788 @return: The reverse DNS lookup for an IPv6 address in network byte 789 order integer form. 790 """ 791 addr = self.int_to_str(int_val, word_fmt='%04x') 792 tokens = list(addr.replace(':', '')) 793 tokens.reverse() 794 # We won't support ip6.int here - see RFC 3152 for details. 795 tokens = tokens + ['ip6', 'arpa', ''] 796 return '.'.join(tokens)
797 798 #-----------------------------------------------------------------------------
799 -class EUI48Strategy(AddrStrategy):
800 """ 801 Implements the operations that can be performed on an IEEE 48-bit EUI 802 (Extended Unique Identifer) a.k.a. a MAC (Media Access Control) layer 2 803 address. 804 805 Supports all common (and some less common MAC string formats including 806 Cisco's 'triple hextet' format and also bare MACs that contain no 807 delimiters. 808 """ 809 # Regular expression to match the numerous different MAC formats. 810 RE_MAC_FORMATS = ( 811 # 2 bytes x 6 (UNIX, Windows, EUI-48) 812 '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$', 813 '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$', 814 815 # 4 bytes x 3 (Cisco) 816 '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$', 817 '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$', 818 819 # 6 bytes x 2 (PostgreSQL) 820 '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$', 821 '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$', 822 823 # 12 bytes (bare, no delimiters) 824 '^(' + ''.join(['[0-9A-F]'] * 12) + ')$', 825 '^(' + ''.join(['[0-9A-F]'] * 11) + ')$', 826 ) 827 # For efficiency, replace each string regexp with its compiled 828 # equivalent. 829 RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS] 830
831 - def __init__(self, word_fmt='%02x', word_sep='-', uppercase=True):
832 """ 833 Constructor. 834 835 @param word_sep: separator between each word. 836 (Default: '-') 837 838 @param word_fmt: format string for each hextet. 839 (Default: '%02x') 840 841 @param uppercase: return uppercase MAC/EUI-48 addresses. 842 (Default: True) 843 """ 844 super(self.__class__, self).__init__(addr_type=AT_LINK, width=48, 845 word_size=8, word_fmt=word_fmt, word_sep=word_sep, 846 uppercase=uppercase)
847
848 - def reset(self):
849 """ 850 Resets the internal state of this strategy to safe default values. 851 """ 852 # These are the settings for EUI-48 specific formatting. 853 self.width = 48 854 self.max_int = 2 ** self.width - 1 855 self.word_size = 8 856 self.num_words = self.width / self.word_size 857 self.max_word = 2 ** self.word_size - 1 858 self.word_sep = '-' 859 self.word_fmt = '%02x' 860 self.word_base = 16 861 self.addr_type = AT_LINK 862 self.uppercase = True
863
864 - def valid_str(self, addr):
865 """ 866 @param addr: An EUI-48 or MAC address in string form. 867 868 @return: C{True} if MAC address string is valid, C{False} otherwise. 869 """ 870 if not isinstance(addr, (str, unicode)): 871 return False 872 873 for regexp in EUI48Strategy.RE_MAC_FORMATS: 874 match_result = regexp.findall(addr) 875 if len(match_result) != 0: 876 return True 877 return False
878
879 - def str_to_int(self, addr):
880 """ 881 @param addr: An EUI-48 or MAC address in string form. 882 883 @return: A network byte order integer that is equivalent to value 884 represented by EUI-48/MAC string address. 885 """ 886 words = [] 887 if isinstance(addr, (str, unicode)): 888 found_match = False 889 for regexp in EUI48Strategy.RE_MAC_FORMATS: 890 match_result = regexp.findall(addr) 891 if len(match_result) != 0: 892 found_match = True 893 if isinstance(match_result[0], tuple): 894 words = match_result[0] 895 else: 896 words = (match_result[0],) 897 break 898 if not found_match: 899 raise AddrFormatError('%r is not a supported MAC format!' \ 900 % addr) 901 else: 902 raise TypeError('%r is not str() or unicode()!' % addr) 903 904 int_val = None 905 906 if len(words) == 6: 907 # 2 bytes x 6 (UNIX, Windows, EUI-48) 908 int_val = int(''.join(['%02x' % int(w, 16) for w in words]), 16) 909 elif len(words) == 3: 910 # 4 bytes x 3 (Cisco) 911 int_val = int(''.join(['%04x' % int(w, 16) for w in words]), 16) 912 elif len(words) == 2: 913 # 6 bytes x 2 (PostgreSQL) 914 int_val = int(''.join(['%06x' % int(w, 16) for w in words]), 16) 915 elif len(words) == 1: 916 # 12 bytes (bare, no delimiters) 917 int_val = int('%012x' % int(words[0], 16), 16) 918 else: 919 raise AddrFormatError('unexpected word count in MAC address %r!' \ 920 % addr) 921 922 return int_val
923
924 - def int_to_str(self, int_val, word_sep=None, word_fmt=None):
925 """ 926 @param int_val: A network byte order integer. 927 928 @param word_sep: (optional) The separator used between words in an 929 address string. 930 931 @param word_fmt: (optional) A Python format string used to format 932 each word of address. 933 934 @return: A MAC address in string form that is equivalent to value 935 represented by a network byte order integer. 936 """ 937 _word_sep = self.word_sep 938 if word_sep is not None: 939 _word_sep = word_sep 940 941 _word_fmt = self.word_fmt 942 if word_fmt is not None: 943 _word_fmt = word_fmt 944 945 words = self.int_to_words(int_val) 946 tokens = [_word_fmt % i for i in words] 947 addr = _word_sep.join(tokens) 948 949 if self.uppercase: 950 return addr.upper() 951 else: 952 return addr.lower()
953 954 #----------------------------------------------------------------------------- 955 # Shared strategy objects for supported address types. 956 #----------------------------------------------------------------------------- 957 958 #: A shared strategy object supporting all operations on IPv4 addresses. 959 ST_IPV4 = None 960 # Use the right strategy class dependent upon Python implementation (work- 961 # around for various Python bugs). 962 if USE_IPV4_OPT is True: 963 ST_IPV4 = IPv4StrategyOpt() 964 else: 965 ST_IPV4 = IPv4StrategyStd() 966 967 #: A shared strategy object supporting all operations on IPv6 addresses. 968 ST_IPV6 = IPv6Strategy() 969 #: A shared strategy object supporting all operations on EUI-48/MAC addresses. 970 ST_EUI48 = EUI48Strategy() 971 972 #: A shared strategy object supporting all operations on EUI-64 addresses. 973 ST_EUI64 = AddrStrategy(addr_type=AT_EUI64, width=64, word_size=8, \ 974 word_fmt='%02x', word_sep='-', uppercase=True) 975 976 #----------------------------------------------------------------------------- 977 if __name__ == '__main__': 978 pass 979