1
2
3
4
5
6
7 """
8 network address conversion logic, constants and shared strategy objects.
9 """
10 import struct as _struct
11 import re as _re
12 from netaddr.util import BYTES_TO_BITS as _BYTES_TO_BITS
13
14
15 try:
16 import socket as _socket
17
18 _socket.inet_aton('255.255.255.255')
19 from socket import inet_aton as _inet_aton, \
20 inet_ntoa as _inet_ntoa, \
21 AF_INET as _AF_INET
22 except:
23 from netaddr.fallback import inet_aton as _inet_aton, \
24 inet_ntoa as _inet_ntoa, \
25 AF_INET as _AF_INET
26 try:
27 import socket as _socket
28
29 if not _socket.has_ipv6:
30 raise Exception('IPv6 disabled')
31 _socket.inet_pton
32 _socket.AF_INET6
33 from socket import inet_pton as _inet_pton, \
34 inet_ntop as _inet_ntop, \
35 AF_INET6 as _AF_INET6
36 except:
37 from netaddr.fallback import inet_pton as _inet_pton, \
38 inet_ntop as _inet_ntop, \
39 AF_INET6 as _AF_INET6
40
41 from netaddr import BIG_ENDIAN_PLATFORM, AT_UNSPEC, AT_INET, AT_INET6, \
42 AT_LINK, AT_EUI64, AT_NAMES, AddrFormatError
43
44
46 """Basic support for common operations performed on each address type"""
47
48
49 STRUCT_FORMATS = {
50 8 : 'B',
51 16 : 'H',
52 32 : 'I',
53 }
54
55 - def __init__(self, width, word_size, word_sep, word_fmt='%x',
56 addr_type=AT_UNSPEC, word_base=16):
57 """
58 Constructor.
59
60 @param width: size of address in bits.
61 (e.g. 32 - IPv4, 48 - MAC, 128 - IPv6)
62
63 @param word_size: size of each word.
64 (e.g. 8 - octets, 16 - hextets)
65
66 @param word_sep: separator between each word.
67 (e.g. '.' - IPv4, ':' - IPv6, '-' - EUI-48)
68
69 @param word_fmt: format string for each word.
70 (Default: '%x')
71
72 @param addr_type: address type.
73 (Default: AT_UNSPEC)
74
75 @param word_base: number base used to convert each word using int().
76 (Default: 16)
77 """
78
79 self.width = width
80 self.max_int = 2 ** width - 1
81 self.word_size = word_size
82 self.num_words = width / word_size
83 self.max_word = 2 ** word_size - 1
84 self.word_sep = word_sep
85 self.word_fmt = word_fmt
86 self.word_base = word_base
87 self.addr_type = addr_type
88
89 try:
90 self.name = AT_NAMES[addr_type]
91 except KeyError:
92 self.name = AT_NAMES[AT_UNSPEC]
93
95 """@return: executable Python string to recreate equivalent object"""
96 return "%s(%r, %r, %r, %r, %r)" % (self.__class__.__name__,
97 self.width, self.word_size, self.word_sep, self.addr_type,
98 self.word_base)
99
100
101
102
103
105 """
106 @param bits: A network address in readable binary form.
107
108 @return: C{True} if network address is valid for this address type,
109 C{False} otherwise.
110 """
111 if not isinstance(bits, (str, unicode)):
112 return False
113
114 bits = bits.replace(self.word_sep, '')
115
116 if len(bits) != self.width:
117 return False
118
119 try:
120 if 0 <= int(bits, 2) <= self.max_int:
121 return True
122 except ValueError:
123 return False
124 return False
125
127 """
128 @param bits: A network address in readable binary form.
129
130 @return: An unsigned integer that is equivalent to value represented
131 by network address in readable binary form.
132 """
133 if not self.valid_bits(bits):
134 raise ValueError('%r is not a valid binary form string for ' \
135 'address type!' % bits)
136
137 return int(bits.replace(self.word_sep, ''), 2)
138
139
140
141
142
144 """
145 @param int_val: An unsigned integer.
146
147 @return: C{True} if integer falls within the boundaries of this
148 address type, C{False} otherwise.
149 """
150 if not isinstance(int_val, (int, long)):
151 return False
152
153 return 0 <= int_val <= self.max_int
154
156 """
157 @param int_val: An unsigned integer.
158
159 @return: A network address in string form that is equivalent to value
160 represented by an unsigned integer.
161 """
162 words = self.int_to_words(int_val)
163 tokens = [self.word_fmt % i for i in words]
164 addr = self.word_sep.join(tokens)
165
166 return addr
167
169 """
170 @param int_val: An unsigned integer.
171
172 @param word_sep: (optional) the separator to insert between words.
173 Default: None - use default separator for address type.
174
175 @return: A network address in readable binary form that is equivalent
176 to value represented by an unsigned integer.
177 """
178 bit_words = []
179
180 for word in self.int_to_words(int_val):
181 bits = []
182 while word:
183 bits.append(_BYTES_TO_BITS[word&255])
184 word >>= 8
185 bits.reverse()
186 bit_str = ''.join(bits) or '0'*self.word_size
187 bits = ('0'*self.word_size+bit_str)[-self.word_size:]
188 bit_words.append(bits)
189
190 if word_sep is not None:
191
192 if not hasattr(word_sep, 'join'):
193 raise ValueError('Word separator must be a string!')
194 return word_sep.join(bit_words)
195
196
197 return self.word_sep.join(bit_words)
198
200 """
201 @param int_val: An unsigned integer.
202
203 @return: A network address in standard binary representation format
204 that is equivalent to integer address value. Essentially a back
205 port of the bin() builtin in Python 2.6.x and higher.
206 """
207 bit_words = []
208
209 for word in self.int_to_words(int_val):
210 bits = []
211 while word:
212 bits.append(_BYTES_TO_BITS[word&255])
213 word >>= 8
214 bits.reverse()
215 bit_str = ''.join(bits) or '0'*self.word_size
216 bits = ('0'*self.word_size+bit_str)[-self.word_size:]
217 bit_words.append(bits)
218
219 return '0b' + _re.sub(r'^[0]+([01]+)$', r'\1', ''.join(bit_words))
220
221 - def int_to_words(self, int_val, num_words=None, word_size=None):
222 """
223 @param int_val: An unsigned integer to be divided up into words.
224
225 @param num_words: (optional) number of words expected in return value
226 tuple. Uses address type default if not specified.
227
228 @param word_size: (optional) size/width of individual words (in bits).
229 Uses address type default if not specified.
230 """
231 if not self.valid_int(int_val):
232 raise IndexError('integer %r is out of bounds!' % hex(int_val))
233
234
235 if num_words is None:
236 num_words = self.num_words
237 if word_size is None:
238 word_size = self.word_size
239
240 max_word_size = 2 ** word_size - 1
241
242 words = []
243 for _ in range(num_words):
244 word = int_val & max_word_size
245 words.append(int(word))
246 int_val >>= word_size
247
248 return tuple(reversed(words))
249
251 """
252 @param int_val: the integer to be packed.
253
254 @return: a packed string that is equivalent to value represented by an
255 unsigned integer.
256 """
257
258 words = self.int_to_words(int_val, self.num_words, self.word_size)
259
260 try:
261 fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
262 self.word_size])
263 except KeyError:
264 raise ValueError('unsupported word size: %d!' % self.word_size)
265
266 return _struct.pack(fmt, *words)
267
268
269
270
271
272
274 """
275 @param packed_int: a packed string containing an unsigned integer.
276 It is assumed that the string is packed in network byte order.
277
278 @return: An unsigned integer equivalent to value of network address
279 represented by packed binary string.
280 """
281 try:
282 fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
283 self.word_size])
284 except KeyError:
285 raise ValueError('unsupported word size: %d!' % self.word_size)
286
287 words = list(_struct.unpack(fmt, packed_int))
288
289 int_val = 0
290 for i, num in enumerate(reversed(words)):
291 word = num
292 word = word << self.word_size * i
293 int_val = int_val | word
294
295 return int_val
296
297
298
299
300
302 """
303 @param addr: A network address in string form.
304
305 @return: C{True} if network address in string form is valid for this
306 address type, C{False} otherwise.
307 """
308 if not isinstance(addr, (str, unicode)):
309 return False
310
311 tokens = addr.split(self.word_sep)
312 if len(tokens) != self.num_words:
313 return False
314
315 try:
316 for token in tokens:
317 if not 0 <= int(token, self.word_base) <= \
318 self.max_word:
319 return False
320 except TypeError:
321 return False
322 except ValueError:
323 return False
324 return True
325
327 """
328 @param addr: A network address in string form.
329
330 @return: An unsigned integer equivalent to value represented by network
331 address in string form.
332 """
333 if not self.valid_str(addr):
334 raise ValueError('%r is not a recognised string representation' \
335 ' of this address type!' % addr)
336
337 tokens = addr.split(self.word_sep)
338 words = [ int(token, self.word_base) for token in tokens ]
339
340 return self.words_to_int(words)
341
342
343
344
345
347 """
348 @param words: A sequence containing integer word values.
349
350 @return: C{True} if word sequence is valid for this address type,
351 C{False} otherwise.
352 """
353 if not hasattr(words, '__iter__'):
354 return False
355
356 if len(words) != self.num_words:
357 return False
358
359 for i in words:
360 if not isinstance(i, (int, long)):
361 return False
362
363 if not 0 <= i <= self.max_word:
364 return False
365 return True
366
368 """
369 @param words: A list or tuple containing integer word values.
370
371 @return: An unsigned integer that is equivalent to value represented
372 by word sequence.
373 """
374 if not self.valid_words(words):
375 raise ValueError('%r is not a valid word list!' % words)
376
377 int_val = 0
378 for i, num in enumerate(reversed(words)):
379 word = num
380 word = word << self.word_size * i
381 int_val = int_val | word
382
383 return int_val
384
385
387 """An L{AddrStrategy} for IPv4 address processing."""
392
394 """
395 @param addr: An IP address in presentation (string) format.
396
397 @return: C{True} if network address in string form is valid for this
398 address type, C{False} otherwise.
399 """
400 if addr == '':
401 raise AddrFormatError('Empty strings are not supported!')
402
403 try:
404 _inet_aton(addr)
405 except:
406 return False
407 return True
408
410 """
411 @param addr: An IPv4 dotted decimal address in string form.
412
413 @return: An unsigned integer that is equivalent to value represented
414 by the IPv4 dotted decimal address string.
415 """
416 if addr == '':
417 raise AddrFormatError('Empty strings are not supported!')
418 try:
419 return _struct.unpack('>I', _inet_aton(addr))[0]
420 except:
421 raise AddrFormatError('%r is not a valid IPv4 address string!' \
422 % addr)
423
425 """
426 @param int_val: An unsigned integer.
427
428 @return: An IPv4 dotted decimal address string that is equivalent to
429 value represented by a 32 bit unsigned integer.
430 """
431 if self.valid_int(int_val):
432 return _inet_ntoa(_struct.pack('>I', int_val))
433 else:
434 raise ValueError('%r is not a valid 32-bit unsigned integer!' \
435 % int_val)
436
437 - def int_to_words(self, int_val, num_words=None, word_size=None):
438 """
439 @param int_val: An unsigned integer.
440
441 @param num_words: (unused) *** interface compatibility only ***
442
443 @param word_size: (unused) *** interface compatibility only ***
444
445 @return: An integer word (octet) sequence that is equivalent to value
446 represented by an unsigned integer.
447 """
448 if not self.valid_int(int_val):
449 raise ValueError('%r is not a valid integer value supported ' \
450 'by this address type!' % int_val)
451 return _struct.unpack('4B', _struct.pack('>I', int_val))
452
454 """
455 @param octets: A list or tuple containing integer octets.
456
457 @return: An unsigned integer that is equivalent to value represented
458 by word (octet) sequence.
459 """
460 if not self.valid_words(octets):
461 raise ValueError('%r is not a valid octet list for an IPv4 ' \
462 'address!' % octets)
463 return _struct.unpack('>I', _struct.pack('4B', *octets))[0]
464
466 """
467 @param int_val: An unsigned integer.
468
469 @return: The reverse DNS lookup for an IPv4 address in network byte
470 order integer form.
471 """
472 words = ["%d" % i for i in self.int_to_words(int_val)]
473 words.reverse()
474 words.extend(['in-addr', 'arpa', ''])
475 return '.'.join(words)
476
477
479 """
480 An L{AddrStrategy} for IPv6 address processing.
481
482 Implements the operations that can be performed on an IPv6 network address
483 in accordance with RFC 4291.
484 """
489
491 """
492 @param addr: An IPv6 address in string form.
493
494 @return: C{True} if IPv6 network address string is valid, C{False}
495 otherwise.
496 """
497 if addr == '':
498 raise AddrFormatError('Empty strings are not supported!')
499
500 try:
501 _inet_pton(_AF_INET6, addr)
502 except _socket.error:
503 return False
504 except TypeError:
505 return False
506 except ValueError:
507 return False
508 return True
509
511 """
512 @param addr: An IPv6 address in string form.
513
514 @return: The equivalent unsigned integer for a given IPv6 address.
515 """
516 if addr == '':
517 raise AddrFormatError('Empty strings are not supported!')
518 try:
519 packed_int = _inet_pton(_AF_INET6, addr)
520 return self.packed_to_int(packed_int)
521 except Exception, e:
522 raise AddrFormatError('%r is not a valid IPv6 address string!' \
523 % addr)
524
525 - def int_to_str(self, int_val, compact=True, word_fmt=None):
526 """
527 @param int_val: An unsigned integer.
528
529 @param compact: (optional) A boolean flag indicating if compact
530 formatting should be used. If True, this method uses the '::'
531 string to represent the first adjacent group of words with a value
532 of zero. Default: True
533
534 @param word_fmt: (optional) The Python format string used to override
535 formatting for each word. Only applies when compact is False.
536
537 @return: The IPv6 string form equal to the unsigned integer provided.
538 """
539 try:
540 packed_int = self.int_to_packed(int_val)
541 if compact:
542
543 return _inet_ntop(_AF_INET6, packed_int)
544 else:
545
546 if word_fmt is None:
547 word_fmt = self.word_fmt
548 words = list(_struct.unpack('>8H', packed_int))
549 tokens = [word_fmt % word for word in words]
550 return self.word_sep.join(tokens)
551 except Exception, e:
552 raise ValueError('%r is not a valid 128-bit unsigned integer!' \
553 % int_val)
554
556 """
557 @param int_val: the integer to be packed.
558
559 @return: a packed string that is equivalent to value represented by an
560 unsigned integer (in network byte order).
561 """
562
563
564 num_words = 4
565 word_size = 32
566
567 words = self.int_to_words(int_val, num_words, word_size)
568
569 try:
570 fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
571 except KeyError:
572 raise ValueError('unsupported word size: %d!' % word_size)
573
574 return _struct.pack(fmt, *words)
575
577 """
578 @param packed_int: a packed string containing an unsigned integer.
579 It is assumed that string is packed in network byte order.
580
581 @return: An unsigned integer that is equivalent to value of network
582 address represented by packed binary string.
583 """
584
585
586 num_words = 4
587 word_size = 32
588
589 try:
590 fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
591 except KeyError:
592 raise ValueError('unsupported word size: %d!' % word_size)
593
594 words = list(_struct.unpack(fmt, packed_int))
595
596 int_val = 0
597 for i, num in enumerate(reversed(words)):
598 word = num
599 word = word << word_size * i
600 int_val = int_val | word
601
602 return int_val
603
605 """
606 @param int_val: An unsigned integer.
607
608 @return: The reverse DNS lookup for an IPv6 address in network byte
609 order integer form.
610 """
611 addr = self.int_to_str(int_val, compact=False, word_fmt='%.4x')
612 tokens = list(addr.replace(':', ''))
613 tokens.reverse()
614
615 tokens = tokens + ['ip6', 'arpa', '']
616 return '.'.join(tokens)
617
618
620 """
621 Implements the operations that can be performed on an IEEE 48-bit EUI
622 (Extended Unique Identifer) a.k.a. a MAC (Media Access Control) layer 2
623 address.
624
625 Supports all common (and some less common MAC string formats including
626 Cisco's 'triple hextet' format and also bare MACs that contain no
627 delimiters.
628 """
629
630 RE_MAC_FORMATS = (
631
632 '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$',
633 '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$',
634
635
636 '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$',
637 '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$',
638
639
640 '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$',
641 '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$',
642
643
644 '^(' + ''.join(['[0-9A-F]'] * 12) + ')$',
645 '^(' + ''.join(['[0-9A-F]'] * 11) + ')$',
646 )
647
648
649 RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS]
650
651 - def __init__(self, word_fmt='%.2X', word_sep='-'):
652 """
653 Constructor.
654
655 @param word_sep: separator between each word.
656 (Default: '-')
657
658 @param word_fmt: format string for each hextet.
659 (Default: '%02x')
660 """
661 super(self.__class__, self).__init__(addr_type=AT_LINK, width=48,
662 word_size=8, word_fmt=word_fmt, word_sep=word_sep)
663
664
666 """
667 Resets the internal state of this strategy to safe default values.
668 """
669
670 self.width = 48
671 self.max_int = 2 ** self.width - 1
672 self.word_size = 8
673 self.num_words = self.width / self.word_size
674 self.max_word = 2 ** self.word_size - 1
675 self.word_sep = '-'
676 self.word_fmt = '%.2X'
677 self.word_base = 16
678 self.addr_type = AT_LINK
679
681 """
682 @param addr: An EUI-48 or MAC address in string form.
683
684 @return: C{True} if MAC address string is valid, C{False} otherwise.
685 """
686 if not isinstance(addr, (str, unicode)):
687 return False
688
689 for regexp in EUI48Strategy.RE_MAC_FORMATS:
690 match_result = regexp.findall(addr)
691 if len(match_result) != 0:
692 return True
693 return False
694
696 """
697 @param addr: An EUI-48 or MAC address in string form.
698
699 @return: An unsigned integer that is equivalent to value represented
700 by EUI-48/MAC string address.
701 """
702 words = []
703 if isinstance(addr, (str, unicode)):
704 found_match = False
705 for regexp in EUI48Strategy.RE_MAC_FORMATS:
706 match_result = regexp.findall(addr)
707 if len(match_result) != 0:
708 found_match = True
709 if isinstance(match_result[0], tuple):
710 words = match_result[0]
711 else:
712 words = (match_result[0],)
713 break
714 if not found_match:
715 raise AddrFormatError('%r is not a supported MAC format!' \
716 % addr)
717 else:
718 raise TypeError('%r is not str() or unicode()!' % addr)
719
720 int_val = None
721
722 if len(words) == 6:
723
724 int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
725 elif len(words) == 3:
726
727 int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
728 elif len(words) == 2:
729
730 int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16)
731 elif len(words) == 1:
732
733 int_val = int('%012x' % int(words[0], 16), 16)
734 else:
735 raise AddrFormatError('unexpected word count in MAC address %r!' \
736 % addr)
737
738 return int_val
739
740 - def int_to_str(self, int_val, word_sep=None, word_fmt=None):
741 """
742 @param int_val: An unsigned integer.
743
744 @param word_sep: (optional) The separator used between words in an
745 address string.
746
747 @param word_fmt: (optional) A Python format string used to format
748 each word of address.
749
750 @return: A MAC address in string form that is equivalent to value
751 represented by an unsigned integer.
752 """
753 _word_sep = self.word_sep
754 if word_sep is not None:
755 _word_sep = word_sep
756
757 _word_fmt = self.word_fmt
758 if word_fmt is not None:
759 _word_fmt = word_fmt
760
761 words = self.int_to_words(int_val)
762 tokens = [_word_fmt % i for i in words]
763 addr = _word_sep.join(tokens)
764
765 return addr
766
767
768
769
770
771
772 ST_IPV4 = IPv4Strategy()
773
774
775 ST_IPV6 = IPv6Strategy()
776
777
778 ST_EUI48 = EUI48Strategy()
779
780
781 ST_EUI64 = AddrStrategy(addr_type=AT_EUI64, width=64, word_size=8, \
782 word_fmt='%.2X', word_sep='-')
783
784
785 if __name__ == '__main__':
786 pass
787