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