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