1
2
3
4
5
6
7 """
8 network address type logic, constants used to identify them and shared
9 strategy objects.
10 """
11 import socket as _socket
12 import struct as _struct
13
14 from netaddr import BIG_ENDIAN_PLATFORM, AT_UNSPEC, AT_INET, AT_INET6, \
15 AT_LINK, AT_EUI64, AT_DESCR
16
17
19 """
20 Generates a 256 element list of 8-bit binary digit strings. List index is
21 equivalent to the bit string value.
22 """
23 lookup = []
24 bits_per_byte = range(7, -1, -1)
25 for num in range(256):
26 bits = 8*[None]
27 for i in bits_per_byte:
28 bits[i] = '01'[num&1]
29 num >>= 1
30 lookup.append(''.join(bits))
31 return lookup
32
33 _BYTES_TO_BITS = _BYTES_TO_BITS()
34
35
37 """
38 Very basic support for all common operations performed on each network
39 type.
40
41 There are usually subclasses for each address type that over-ride methods
42 implemented here to optimise their performance and add additional
43 features.
44 """
45 - def __init__(self, width, word_size, delimiter, word_fmt='%x',
46 addr_type=AT_UNSPEC, hex_words=True, to_upper=False):
47
48 self.width = width
49 self.min_int = 0
50 self.max_int = 2 ** width - 1
51 self.word_size = word_size
52 self.word_count = width / word_size
53 self.min_word = 0
54 self.max_word = 2 ** word_size - 1
55 self.delimiter = delimiter
56 self.word_fmt = word_fmt
57 self.hex_words = hex_words
58 self.word_base = 16
59 self.addr_type = addr_type
60 self.to_upper = to_upper
61
62 if self.hex_words is False:
63 self.word_base = 10
64
65 try:
66 self.name = AT_DESCR[addr_type]
67 except KeyError:
68 self.name = AT_DESCR[AT_UNSPEC]
69
71 """
72 @return: An executable Python statement that can recreate an object
73 with an equivalent state.
74 """
75 return "netaddr.address.%s(%r, %r, %r, %r, %r, %r)" % \
76 (self.__class__.__name__, self.width, self.word_size,
77 self.delimiter, self.addr_type, self.hex_words, self.to_upper)
78
79
80
81
82
84 """
85 @param bits: A network address in readable binary form.
86
87 @return: C{True} if network address is valid for this address type,
88 C{False} otherwise.
89 """
90 if not isinstance(bits, (str, unicode)):
91 return False
92
93 bits = bits.replace(self.delimiter, '')
94
95 if len(bits) != self.width:
96 return False
97
98 try:
99 if self.min_int <= int(bits, 2) <= self.max_int:
100 return True
101 except ValueError:
102 return False
103 return False
104
106 """
107 @param bits: A network address in readable binary form.
108
109 @return: A network byte order integer that is equivalent to value
110 represented by network address in readable binary form.
111 """
112 words = self.bits_to_words(bits)
113 return self.words_to_int(words)
114
116 """
117 @param bits: A network address in readable binary form.
118
119 @return: A network address in string form that is equivalent to value
120 represented by network address in readable binary form.
121 """
122 words = self.bits_to_words(bits)
123 return self.words_to_str(words)
124
126 """
127 @param bits: A network address in readable binary form.
128
129 @return: An integer word sequence that is equivalent to value
130 represented by network address in readable binary form.
131 """
132 if not self.valid_bits(bits):
133 raise Exception('%r is not a valid readable binary form string' \
134 ' for address type!' % bits)
135
136 word_bits = bits.split(self.delimiter)
137 if len(word_bits) != self.word_count:
138 raise Exception('invalid number of words found in binary form ' \
139 'string for address type!' % bits)
140
141 return tuple([int(i, 2) for i in word_bits])
142
143
144
145
146
148 """
149 @param int_val: A network byte order integer.
150
151 @return: C{True} if network byte order integer falls within the
152 boundaries of this address type, C{False} otherwise.
153 """
154 if not isinstance(int_val, (int, long)):
155 return False
156
157 if self.min_int <= int_val <= self.max_int:
158 return True
159
160 return False
161
163 """
164 @param int_val: A network byte order integer.
165
166 @return: A network address in string form that is equivalent to value
167 represented by a network byte order integer.
168 """
169 words = self.int_to_words(int_val)
170 tokens = [self.word_fmt % i for i in words]
171 addr = self.delimiter.join(tokens)
172
173 if self.to_upper is True:
174 return addr.upper()
175
176 return addr
177
179 """
180 @param int_val: A network byte order integer.
181
182 @return: A network address in readable binary form that is equivalent
183 to value represented by a network byte order integer.
184 """
185 bit_words = []
186 for word in self.int_to_words(int_val):
187 bits = self.word_to_bits(word)
188 bit_words.append(bits)
189
190 return self.delimiter.join(bit_words)
191
193 """
194 @param int_val: A network byte order integer.
195
196 @return: An integer word sequence that is equivalent to value
197 represented by a network byte order integer.
198 """
199 if not self.valid_int(int_val):
200 raise Exception('%r is not a valid int/long value supported ' \
201 'by this address type!' % int_val)
202
203 words = []
204 for i in range(self.word_count):
205 word = int_val & (2 ** self.word_size - 1)
206 words.append(int(word))
207 int_val >>= self.word_size
208
209 words.reverse()
210 return tuple(words)
211
212
213
214
215
217 """
218 @param addr: A network address in string form.
219
220 @return: C{True} if network address in string form is valid for this
221 address type, C{False} otherwise.
222 """
223 if not isinstance(addr, (str, unicode)):
224 return False
225
226 tokens = addr.split(self.delimiter)
227 if len(tokens) != self.word_count:
228 return False
229
230 try:
231 for token in tokens:
232 int_val = int(token, self.word_base)
233 if not self.min_word <= int_val <= self.max_word:
234 return False
235 except TypeError:
236 return False
237 except ValueError:
238 return False
239 return True
240
242 """
243 @param addr: A network address in string form.
244
245 @return: A network byte order integer that is equivalent to value
246 represented by network address in string form.
247 """
248 words = self.str_to_words(addr)
249 return self.words_to_int(words)
250
252 """
253 @param addr: A network address in string form.
254
255 @return: A network address in readable binary form that is equivalent
256 to value represented by network address in string form.
257 """
258 words = self.str_to_words(addr)
259 return self.words_to_bits(words)
260
262 """
263 @param addr: A network address in string form.
264
265 @return: An integer word sequence that is equivalent in value to the
266 network address in string form.
267 """
268 if not self.valid_str(addr):
269 raise Exception('%r is not a recognised string representation' \
270 ' of this address type!' % addr)
271
272 words = addr.split(self.delimiter)
273 return tuple([ int(word, self.word_base) for word in words ])
274
275
276
277
278
280 """
281 @param words: A list or tuple containing integer word values.
282
283 @return: C{True} if word sequence is valid for this address type,
284 C{False} otherwise.
285 """
286 if not isinstance(words, (list, tuple)):
287 return False
288
289 if len(words) != self.word_count:
290 return False
291
292 for i in words:
293 if not isinstance(i, (int, long)):
294 return False
295
296 if not self.min_word <= i <= self.max_word:
297 return False
298 return True
299
301 """
302 @param words: A list or tuple containing integer word values.
303
304 @return: A network byte order integer that is equivalent to value
305 represented by word sequence.
306 """
307 if not self.valid_words(words):
308 raise Exception('%r is not a valid word list!' % words)
309
310
311
312 if isinstance(words, tuple):
313 words = list(words)
314 words.reverse()
315
316 int_val = 0
317 for i, num in enumerate(words):
318 word = num
319 word = word << self.word_size * i
320 int_val = int_val | word
321
322 return int_val
323
325 """
326 @param words: A list or tuple containing integer word values.
327
328 @return: A network address in string form that is equivalent to value
329 represented by word sequence.
330 """
331 if not self.valid_words(words):
332 raise Exception('%r is not a valid word list!' % words)
333
334 tokens = [self.word_fmt % i for i in words]
335 addr = self.delimiter.join(tokens)
336 return addr
337
339 """
340 @param words: A list or tuple containing integer word values.
341
342 @return: A network address in readable binary form that is equivalent
343 to value represented by word sequence.
344 """
345 if not self.valid_words(words):
346 raise Exception('%r is not a valid word list!' % words)
347
348 bit_words = []
349 for word in words:
350 bits = self.word_to_bits(word)
351 bit_words.append(bits)
352
353 return self.delimiter.join(bit_words)
354
355
356
357
358
360 """
361 @param int_val: An individual integer word value.
362
363 @return: An integer word value for this address type in a fixed width
364 readable binary form.
365 """
366 bits = []
367
368 while int_val:
369 bits.append(_BYTES_TO_BITS[int_val&255])
370 int_val >>= 8
371
372 bits.reverse()
373 bit_str = ''.join(bits) or '0'*self.word_size
374 return ('0'*self.word_size+bit_str)[-self.word_size:]
375
377 """
378 @return: String detailing setup of this L{AddrStrategy} instance.
379 Useful for debugging.
380 """
381 tokens = []
382 for k in sorted(self.__dict__):
383 v = self.__dict__[k]
384 if isinstance(v, bool):
385 tokens.append("%s: %r" % (k, v))
386 elif isinstance(v, (int, long)):
387 tokens.append(
388 "%s: %r (%s)" % (k, v, hex(v).rstrip('L').lower()))
389 else:
390 tokens.append("%s: %r" % (k, v))
391 return "\n".join(tokens)
392
393
395 """
396 An optimised L{AddrStrategy} for IPv4 addresses.
397
398 It uses C{pack()} and C{unpack()} from the C{struct} module along with the
399 C{inet_ntoa()} and C{inet_aton()} functions from the C{socket} module.
400 This makes it approx. 2.5 times faster than a standard L{AddrStrategy}
401 configured for IPv4.
402
403 However, keep in mind that these modules might not be available everywhere
404 that Python itself is. Runtimes such as Google App Engine gut the
405 C{socket} module. C{struct} is also limited to processing 32-bit integers
406 which is fine for IPv4 but isn't suitable for IPv6.
407 """
409 """Constructor."""
410 super(self.__class__, self).__init__(width=32, word_size=8,
411 word_fmt='%d', delimiter='.', addr_type=AT_INET, hex_words=False)
412
414 """
415 @param addr: An IPv4 dotted decimal address in string form.
416
417 @return: A network byte order integer that is equivalent to value
418 represented by the IPv4 dotted decimal address string.
419 """
420 if not self.valid_str(addr):
421 raise Exception('%r is not a valid IPv4 dotted decimal' \
422 ' address string.!' % addr)
423 return _struct.unpack('>I', _socket.inet_aton(addr))[0]
424
426 """
427 @param int_val: A network byte order integer.
428
429 @return: An IPv4 dotted decimal address string that is equivalent to
430 value represented by a 32 bit integer in network byte order.
431 """
432 if not self.valid_int(int_val):
433 raise Exception('%r is not a valid 32-bit int or long!' % int_val)
434 return _socket.inet_ntoa(_struct.pack('>I', int_val))
435
437 """
438 @param int_val: A network byte order integer.
439
440 @return: An integer word (octet) sequence that is equivalent to value
441 represented by network byte order integer.
442 """
443 if not self.valid_int(int_val):
444 raise Exception('%r is not a valid int/long value supported ' \
445 'by this address type!' % int_val)
446 return _struct.unpack('4B', _struct.pack('>I', int_val))
447
449 """
450 @param octets: A list or tuple containing integer octets.
451
452 @return: A network byte order integer that is equivalent to value
453 represented by word (octet) sequence.
454 """
455 if not self.valid_words(octets):
456 raise Exception('%r is not a valid octet list for an IPv4 ' \
457 'address!' % octets)
458 return _struct.unpack('>I', _struct.pack('4B', *octets))[0]
459
461 """
462 @param int_val: A network byte order integer.
463
464 @return: The reverse DNS lookup for an IPv4 address in network byte
465 order integer form.
466 """
467 words = ["%d" % i for i in self.int_to_words(int_val)]
468 words.reverse()
469 words.extend(['in-addr', 'arpa'])
470 return '.'.join(words)
471
472
474 """
475 Implements the operations that can be performed on an Internet Protocol
476 version 6 network address in accordance with RFC 4291.
477
478 NB - This class would benefit greatly from access to inet_pton/inet_ntop()
479 function calls in Python's socket module. Sadly, they aren't available so
480 we'll have to put up with the pure-Python implementation here (for now at
481 least).
482 """
484 """Constructor."""
485 super(self.__class__, self).__init__(addr_type=AT_INET6,
486 width=128, word_size=16, word_fmt='%x', delimiter=':')
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
496 if not isinstance(addr, (str, unicode)):
497 return False
498
499 if '::' in addr:
500
501 try:
502 prefix, suffix = addr.split('::')
503 except ValueError:
504 return False
505
506 l_prefix = []
507 l_suffix = []
508
509 if prefix != '':
510 l_prefix = prefix.split(':')
511
512 if suffix != '':
513 l_suffix = suffix.split(':')
514
515
516 if len(l_suffix) and '.' in l_suffix[-1]:
517 ipv4_str = l_suffix[-1]
518 if ST_IPV4.valid_str(ipv4_str):
519 ipv4_int = ST_IPV4.str_to_int(ipv4_str)
520 ipv4_words = ST_IPV4.int_to_words(ipv4_int)
521 l_suffix.pop()
522 l_suffix.append(
523 ''.join(["%x" % i for i in ipv4_words[0:2]]))
524 l_suffix.append(
525 ''.join(["%x" % i for i in ipv4_words[2:]]))
526
527 token_count = len(l_prefix) + len(l_suffix)
528
529 if not 0 <= token_count <= self.word_count - 1:
530 return False
531
532 try:
533 for token in l_prefix + l_suffix:
534 word = int(token, 16)
535 if not self.min_word <= word <= self.max_word:
536 return False
537 except ValueError:
538 return False
539 else:
540
541 if ':' in addr:
542 tokens = addr.split(':')
543
544 if '.' in addr:
545 ipv6_prefix = tokens[:-1]
546 if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']:
547 return False
548 if ipv6_prefix[-1].lower() not in ('0', 'ffff'):
549 return False
550
551 if len(tokens) != (self.word_count - 1):
552 return False
553 ipv4_str = tokens[-1]
554 if ST_IPV4.valid_str(ipv4_str):
555 ipv4_int = ST_IPV4.str_to_int(ipv4_str)
556 ipv4_words = ST_IPV4.int_to_words(ipv4_int)
557 tokens.pop()
558 tokens.append(
559 ''.join(["%x" % i for i in ipv4_words[0:2]]))
560 tokens.append(
561 ''.join(["%x" % i for i in ipv4_words[2:]]))
562 else:
563
564 if len(tokens) != self.word_count:
565 return False
566 try:
567 for token in tokens:
568 word = int(token, 16)
569 if not self.min_word <= word <= self.max_word:
570 return False
571 except ValueError:
572 return False
573 else:
574 return False
575
576 return True
577
579 """
580 @param addr: An IPv6 address in string form.
581
582 @return: The equivalent network byte order integer for a given IPv6
583 address.
584 """
585 if not self.valid_str(addr):
586 raise Exception("'%s' is an invalid IPv6 address!" % addr)
587
588 values = []
589
590 if addr == '::':
591
592 return 0
593 elif '::' in addr:
594
595 prefix, suffix = addr.split('::')
596
597 if prefix == '':
598 l_prefix = ['0']
599 else:
600 l_prefix = prefix.split(':')
601
602 if suffix == '':
603 l_suffix = ['0']
604 else:
605 l_suffix = suffix.split(':')
606
607
608 if len(l_suffix) and '.' in l_suffix[-1]:
609 if len(l_suffix) > 2:
610 return False
611 if len(l_suffix) == 2 and l_suffix[0].lower() != 'ffff':
612 return False
613
614 ipv4_str = l_suffix[-1]
615 if ST_IPV4.valid_str(ipv4_str):
616 ipv4_int = ST_IPV4.str_to_int(ipv4_str)
617 ipv4_words = ST_IPV4.int_to_words(ipv4_int)
618 l_suffix.pop()
619 l_suffix.append(
620 ''.join(["%x" % i for i in ipv4_words[0:2]]))
621 l_suffix.append(
622 ''.join(["%x" % i for i in ipv4_words[2:]]))
623
624 gap_size = 8 - ( len(l_prefix) + len(l_suffix) )
625
626 values = ["%04x" % int(i, 16) for i in l_prefix] \
627 + ['0000' for i in range(gap_size)] \
628 + ["%04x" % int(i, 16) for i in l_suffix]
629 else:
630
631 if '.' in addr:
632
633 tokens = addr.split(':')
634 ipv4_str = tokens[-1]
635 if ST_IPV4.valid_str(ipv4_str):
636 ipv4_int = ST_IPV4.str_to_int(ipv4_str)
637 ipv4_words = ST_IPV4.int_to_words(ipv4_int)
638 tokens.pop()
639 tokens.append(''.join(["%x" % i for i in ipv4_words[0:2]]))
640 tokens.append(''.join(["%x" % i for i in ipv4_words[2:]]))
641
642 values = ["%04x" % int(i, 16) for i in tokens]
643 else:
644
645 values = ["%04x" % int(i, 16) for i in addr.split(':')]
646
647 value = int(''.join(values), 16)
648
649 return value
650
651 - def int_to_str(self, int_val, compact=True, word_fmt=None):
652 """
653 @param int_val: A network byte order integer.
654
655 @param compact: (optional) A boolean flag indicating if compact
656 formatting should be used. If True, this method uses the '::'
657 string to represent the first adjacent group of words with a value
658 of zero. Default: True
659
660 @param word_fmt: (optional) The Python format string used to override
661 formatting for each word.
662
663 @return: The IPv6 string form equal to the network byte order integer
664 value provided.
665 """
666
667
668 if not compact:
669 return super(self.__class__, self).int_to_str(int_val)
670
671 the_word_fmt = self.word_fmt
672 if word_fmt is not None:
673 the_word_fmt = word_fmt
674
675 if not self.valid_int(int_val):
676 raise Exception('%r is not a valid int/long value supported ' \
677 'by this address type!' % int_val)
678
679 tokens = []
680 for i in range(self.word_count):
681 word = int_val & (2 ** self.word_size - 1)
682 tokens += [the_word_fmt % word]
683 int_val >>= self.word_size
684
685 tokens.reverse()
686
687
688 if compact == True:
689 new_tokens = []
690 compact_start = False
691 compact_end = False
692 for token in tokens:
693 if token == '0':
694 if compact_start == False and compact_end == False:
695 new_tokens += ['']
696 compact_start = True
697 elif compact_start == True and compact_end == False:
698 pass
699 else:
700 new_tokens += ['0']
701 else:
702 if compact_start == True:
703 compact_end = True
704 new_tokens += [token]
705
706
707 if len(new_tokens) == 1 and new_tokens[0] == '':
708 new_tokens += ['', '']
709 elif new_tokens[-1] == '':
710 new_tokens += ['']
711 elif new_tokens[0] == '':
712 new_tokens.insert(0, '')
713
714 tokens = new_tokens
715
716 return ':'.join(tokens)
717
719 """
720 @param int_val: A network byte order integer.
721
722 @return: The reverse DNS lookup for an IPv6 address in network byte
723 order integer form.
724 """
725 addr = self.int_to_str(int_val, word_fmt='%04x')
726 tokens = list(addr.replace(':', ''))
727 tokens.reverse()
728
729 tokens = tokens + ['ip6', 'arpa']
730 return '.'.join(tokens)
731
732
734 """
735 Implements the operations that can be performed on an IEEE 48-bit EUI
736 (Extended Unique Identifer). For all intents and purposes here, a MAC
737 address.
738
739 Supports most common MAC address formats including Cisco's string format.
740 """
742 """Constructor."""
743 super(self.__class__, self).__init__(addr_type=AT_LINK, width=48,
744 word_size=8, word_fmt='%02x', delimiter='-', to_upper=True)
745
747 """
748 @param addr: An EUI-48 or MAC address in string form.
749
750 @return: C{True} if MAC address string is valid, C{False} otherwise.
751 """
752 if not isinstance(addr, (str, unicode)):
753 return False
754
755 try:
756 if '.' in addr:
757
758 words = [int("0x%s" % i, 0) for i in addr.split('.')]
759 if len(words) != 3:
760 return False
761 for i in words:
762 if not (0 <= i <= 0xffff):
763 return False
764 else:
765 if '-' in addr:
766
767 words = [int("0x%s" % i, 0) for i in addr.split('-')]
768 elif ':' in addr:
769
770 words = [int("0x%s" % i, 0) for i in addr.split(':')]
771 else:
772 return False
773 if len(words) != 6:
774 return False
775 for i in words:
776 if not (0 <= i <= 0xff):
777 return False
778 except TypeError:
779 return False
780 except ValueError:
781 return False
782
783 return True
784
786 """
787 @param addr: An EUI-48 or MAC address in string form.
788
789 Returns an integer word sequence that is equivalent in value to MAC
790 address in string form.
791 """
792 if not self.valid_str(addr):
793 raise Exception('%r is not a recognised string representation' \
794 ' of this address type!' % addr)
795
796 if ':' in addr:
797
798 words = addr.split(':')
799 return tuple([ int(word, self.word_base) for word in words ])
800 elif '-' in addr:
801
802 words = addr.split('-')
803 return tuple([ int(word, self.word_base) for word in words ])
804 elif '.' in addr:
805
806 words = []
807 for num in addr.split('.'):
808 octets = []
809 int_val = int(num, 16)
810 for i in range(2):
811 word = int_val & 0xff
812 octets.append(int(word))
813 int_val >>= 8
814 octets.reverse()
815 words.extend(octets)
816 return tuple(words)
817
818 - def int_to_str(self, int_val, delimiter=None, word_fmt=None,
819 to_upper=True):
820 """
821 @param int_val: A network byte order integer.
822
823 @param delimiter: (optional) A delimiter string override to be used
824 instead of the default between words in string value returned.
825
826 @param word_fmt: (optional) A Python format string override used to
827 format each word of address instead of the default.
828
829 @return: A MAC address in string form that is equivalent to value
830 represented by a network byte order integer.
831 """
832 the_delimiter = self.delimiter
833 if delimiter is not None:
834 the_delimiter = delimiter
835
836 the_word_fmt = self.word_fmt
837 if word_fmt is not None:
838 the_word_fmt = word_fmt
839
840 the_to_upper = self.to_upper
841 if to_upper is not True:
842 the_to_upper = to_upper
843
844 words = self.int_to_words(int_val)
845 tokens = [the_word_fmt % i for i in words]
846 addr = the_delimiter.join(tokens)
847
848 if the_to_upper is True:
849 return addr.upper()
850
851 return addr
852
853
854
855
856
857
858
859
860
861
862 ST_IPV4 = IPv4Strategy()
863
864 ST_IPV6 = IPv6Strategy()
865
866 ST_EUI48 = EUI48Strategy()
867
868
869
870
871
872
873 ST_EUI64 = AddrStrategy(addr_type=AT_EUI64, width=64, word_size=8,
874 word_fmt='%02x', delimiter='-', to_upper=True)
875
876
877 if __name__ == '__main__':
878 pass
879