1 """ IPy - class and tools for handling of IPv4 and IPv6 Addresses and Networks.
2
3 Copyright (c) 2006, INL
4 Copyright (c) 2001-2005, Maximillian Dornseif
5 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of IPy nor the names of its contributors may be used
15 to endorse or promote products derived from this software without
16 specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
19 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
30 Presentation of the API
31 =======================
32
33 The IP class allows a comfortable parsing and handling for most
34 notations in use for IPv4 and IPv6 Addresses and Networks. It was
35 greatly inspired bei RIPE's Perl module NET::IP's interface but
36 doesn't share the Implementation. It doesn't share non-CIDR netmasks,
37 so funky stuff lixe a netmask 0xffffff0f can't be done here.
38
39 >>> from IPy import IP
40 >>> ip = IP('127.0.0.0/30')
41 >>> for x in ip:
42 ... print x
43 ...
44 127.0.0.0
45 127.0.0.1
46 127.0.0.2
47 127.0.0.3
48 >>> ip2 = IP('0x7f000000/30')
49 >>> ip == ip2
50 1
51 >>> ip.reverseNames()
52 ['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.', '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
53 >>> ip.reverseName()
54 '0-3.0.0.127.in-addr.arpa.'
55 >>> ip.iptype()
56 'PRIVATE'
57
58
59 Support all IP addresses
60 ========================
61
62 It can detect about a dozen different ways of expressing IP addresses
63 and networks, parse them and distinguish between IPv4 and IPv6 addresses:
64
65 >>> IP('10.0.0.0/8').version()
66 4
67 >>> IP('::1').version()
68 6
69
70 IPv4 addresses
71 --------------
72
73 >>> print IP(0x7f000001)
74 127.0.0.1
75 >>> print IP('0x7f000001')
76 127.0.0.1
77 >>> print IP('127.0.0.1')
78 127.0.0.1
79 >>> print IP('10')
80 10.0.0.0
81
82 IPv6 addresses
83 --------------
84
85 >>> print IP('1080:0:0:0:8:800:200C:417A')
86 1080:0000:0000:0000:0008:0800:200c:417a
87 >>> print IP('1080::8:800:200C:417A')
88 1080:0000:0000:0000:0008:0800:200c:417a
89 >>> print IP('::1')
90 0000:0000:0000:0000:0000:0000:0000:0001
91 >>> print IP('::13.1.68.3')
92 0000:0000:0000:0000:0000:0000:0d01:4403
93
94 Network mask
95 ------------
96
97 >>> print IP('127.0.0.0/8')
98 127.0.0.0/8
99 >>> print IP('127.0.0.0/255.0.0.0')
100 127.0.0.0/8
101 >>> print IP('127.0.0.0-127.255.255.255')
102 127.0.0.0/8
103
104
105 Option check_addr_prefixlen
106 ===========================
107
108 By default, IPy rejects uncommon netmask like 172.30.1.0/22:
109
110 >>> import IPy
111 >>> IPy.check_addr_prefixlen = True # default value
112 >>> ips = IP('172.30.1.0/22')
113 Traceback (most recent call last):
114 ...
115 ValueError: IP('172.30.1.0/22') has invalid prefix length (22)
116
117 You can change this behaviour with global option check_addr_prefixlen:
118
119 >>> IPy.check_addr_prefixlen = False # disable
120 >>> ips = IP('172.30.1.0/22')
121 >>> len(ips)
122 1024
123
124
125 Convert address to string
126 =========================
127
128 Nearly all class methods which return a string have an optional
129 parameter 'wantprefixlen' which controlles if the prefixlen or netmask
130 is printed. Per default the prefilen is always shown if the net
131 contains more than one address::
132
133 wantprefixlen == 0 / None don't return anything 1.2.3.0
134 wantprefixlen == 1 /prefix 1.2.3.0/24
135 wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0
136 wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255
137
138 You can also change the defaults on an per-object basis by fiddeling with the class members:
139
140 * NoPrefixForSingleIp
141 * WantPrefixLen
142
143 Examples of string conversions:
144
145 >>> IP('10.0.0.0/32').strNormal()
146 '10.0.0.0'
147 >>> IP('10.0.0.0/24').strNormal()
148 '10.0.0.0/24'
149 >>> IP('10.0.0.0/24').strNormal(0)
150 '10.0.0.0'
151 >>> IP('10.0.0.0/24').strNormal(1)
152 '10.0.0.0/24'
153 >>> IP('10.0.0.0/24').strNormal(2)
154 '10.0.0.0/255.255.255.0'
155 >>> IP('10.0.0.0/24').strNormal(3)
156 '10.0.0.0-10.0.0.255'
157 >>> ip = IP('10.0.0.0')
158 >>> print ip
159 10.0.0.0
160 >>> ip.NoPrefixForSingleIp = None
161 >>> print ip
162 10.0.0.0/32
163 >>> ip.WantPrefixLen = 3
164 >>> print ip
165 10.0.0.0-10.0.0.0
166
167
168 What's new?
169 ===========
170
171 Changes between version 0.51 and 0.52:
172
173 * Fix strCompressed() for IPv6 "ffff:ffff:ffff:ffff:ffff:f:f:fffc/127"
174
175 Changes between version 0.5 and 0.51:
176
177 * Use real name of IPy author
178 * Use version "0.51" to help packaging since 0.5 was smaller than 0.42
179 * Fix unit test for Python 2.3 (don't use doctest.testfile)
180 * Fix unit test for Python 2.5 (problem of hex() lower case)
181 * IPy now works on Python 2.2 to 2.5
182
183 Changes between version 0.42 and 0.5: Fix all known bugs:
184
185 * Apply Jean Gillaux's patch for netmask "/0.0.0.0" bug
186 * Apply William McVey's patch for __nonzero__() bug
187 * Apply Victor Stinner patch: setup.py can use setuptools and fix URLs
188 * Allow "172.30.1.0/22" with new option IPy.check_addr_prefixlen=False
189
190 Other changes:
191
192 * Add regression tests
193 * Create AUTHORS file
194
195
196 Compatibility and links
197 =======================
198
199 IPy works on Python version 2.2 to 2.5.
200
201 This Python module is under BSD license: see COPYING file.
202
203 Further Information might be available at: http://software.inl.fr/trac/trac.cgi/wiki/IPy
204
205 TODO
206 ====
207
208 * better comparison (__cmp__ and friends)
209 * tests for __cmp__
210 * always write hex values lowercase
211 * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64
212 * move size in bits into class variables to get rid of some "if self._ipversion ..."
213 * support for base85 encoding
214 * support for output of IPv6 encoded IPv4 Addresses
215 * update address type tables
216 * first-last notation should be allowed for IPv6
217 * add IPv6 docstring examples
218 * check better for negative parameters
219 * add addition / aggregation
220 * move reverse name stuff out of the classes and refactor it
221 * support for aggregation of more than two nets at once
222 * support for aggregation with "holes"
223 * support for finding common prefix
224 * '>>' and '<<' for prefix manipulation
225 * add our own exceptions instead ValueError all the time
226 * rename checkPrefix to checkPrefixOk
227 * add more documentation and doctests
228 * refactor
229 """
230
231
232
233
234 __rcsid__ = '$Id$'
235 __version__ = '0.51'
236
237 import types
238
239
240
241 check_addr_prefixlen = 1
242
243
244
245
246 IPv4ranges = {
247 '0': 'PUBLIC',
248 '00000000': 'PRIVATE',
249 '00001010': 'PRIVATE',
250 '01111111': 'PRIVATE',
251 '1': 'PUBLIC',
252 '1010100111111110': 'PRIVATE',
253 '101011000001': 'PRIVATE',
254 '1100000010101000': 'PRIVATE',
255 '11011111': 'RESERVED',
256 '111': 'RESERVED'
257 }
258
259
260
261
262
263
264 IPv6ranges = {
265 '00000000' : 'RESERVED',
266 '00000001' : 'UNASSIGNED',
267 '0000001' : 'NSAP',
268 '0000010' : 'IPX',
269 '0000011' : 'UNASSIGNED',
270 '00001' : 'UNASSIGNED',
271 '0001' : 'UNASSIGNED',
272 '0010000000000000' : 'RESERVED',
273 '0010000000000001' : 'ASSIGNABLE',
274 '00100000000000010000000': 'ASSIGNABLE IANA',
275 '00100000000000010000001': 'ASSIGNABLE APNIC',
276 '00100000000000010000010': 'ASSIGNABLE ARIN',
277 '00100000000000010000011': 'ASSIGNABLE RIPE',
278 '0010000000000010' : '6TO4',
279 '0011111111111110' : '6BONE',
280 '0011111111111111' : 'RESERVED',
281 '010' : 'GLOBAL-UNICAST',
282 '011' : 'UNASSIGNED',
283 '100' : 'GEO-UNICAST',
284 '101' : 'UNASSIGNED',
285 '110' : 'UNASSIGNED',
286 '1110' : 'UNASSIGNED',
287 '11110' : 'UNASSIGNED',
288 '111110' : 'UNASSIGNED',
289 '1111110' : 'UNASSIGNED',
290 '111111100' : 'UNASSIGNED',
291 '1111111010' : 'LINKLOCAL',
292 '1111111011' : 'SITELOCAL',
293 '11111111' : 'MULTICAST',
294 '0' * 96 : 'IPV4COMP',
295 '0' * 80 + '1' * 16 : 'IPV4MAP',
296 '0' * 128 : 'UNSPECIFIED',
297 '0' * 127 + '1' : 'LOOPBACK'
298 }
299
300
302 """Handling of IP addresses returning integers.
303
304 Use class IP instead because some features are not implemented for
305 IPint."""
306
307 - def __init__(self, data, ipversion = 0):
308 """Create an instance of an IP object.
309
310 Data can be a network specification or a single IP. IP
311 Addresses can be specified in all forms understood by
312 parseAddress.() the size of a network can be specified as
313
314 /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64
315 -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
316 /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6
317
318 If no size specification is given a size of 1 address (/32 for
319 IPv4 and /128 for IPv6) is assumed.
320
321 >>> print IP('127.0.0.0/8')
322 127.0.0.0/8
323 >>> print IP('127.0.0.0/255.0.0.0')
324 127.0.0.0/8
325 >>> print IP('127.0.0.0-127.255.255.255')
326 127.0.0.0/8
327
328 See module documentation for more examples.
329 """
330
331 self.NoPrefixForSingleIp = 1
332 self.WantPrefixLen = None
333
334 netbits = 0
335 prefixlen = -1
336
337
338 if type(data) == types.IntType or type(data) == types.LongType:
339 self.ip = long(data)
340 if ipversion == 0:
341 if self.ip < 0x100000000L:
342 ipversion = 4
343 else:
344 ipversion = 6
345 if ipversion == 4:
346 prefixlen = 32
347 elif ipversion == 6:
348 prefixlen = 128
349 else:
350 raise ValueError, "only IPv4 and IPv6 supported"
351 self._ipversion = ipversion
352 self._prefixlen = prefixlen
353
354 elif isinstance(data, IPint):
355 self._ipversion = data._ipversion
356 self._prefixlen = data._prefixlen
357 self.ip = data.ip
358 else:
359
360
361 x = data.split('-')
362 if len(x) == 2:
363
364 (ip, last) = x
365 (self.ip, parsedVersion) = parseAddress(ip)
366 if parsedVersion != 4:
367 raise ValueError, "first-last notation only allowed for IPv4"
368 (last, lastversion) = parseAddress(last)
369 if lastversion != 4:
370 raise ValueError, "last address should be IPv4, too"
371 if last < self.ip:
372 raise ValueError, "last address should be larger than first"
373 size = last - self.ip
374 netbits = _count1Bits(size)
375 elif len(x) == 1:
376 x = data.split('/')
377
378 if len(x) == 1:
379 ip = x[0]
380 prefixlen = -1
381 elif len(x) > 2:
382 raise ValueError, "only one '/' allowed in IP Address"
383 else:
384 (ip, prefixlen) = x
385 if prefixlen.find('.') != -1:
386
387
388 (netmask, vers) = parseAddress(prefixlen)
389 if vers != 4:
390 raise ValueError, "netmask must be IPv4"
391 prefixlen = _netmaskToPrefixlen(netmask)
392 elif len(x) > 2:
393 raise ValueError, "only one '-' allowed in IP Address"
394 else:
395 raise ValueError, "can't parse"
396
397 (self.ip, parsedVersion) = parseAddress(ip)
398 if ipversion == 0:
399 ipversion = parsedVersion
400 if prefixlen == -1:
401 if ipversion == 4:
402 prefixlen = 32 - netbits
403 elif ipversion == 6:
404 prefixlen = 128 - netbits
405 else:
406 raise ValueError, "only IPv4 and IPv6 supported"
407 self._ipversion = ipversion
408 self._prefixlen = int(prefixlen)
409
410 if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion):
411 raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen)
412
413
415 """Return the first / base / network addess as an (long) integer.
416
417 The same as IP[0].
418
419 >>> "%X" % IP('10.0.0.0/8').int()
420 'A000000'
421 """
422 return self.ip
423
425 """Return the IP version of this Object.
426
427 >>> IP('10.0.0.0/8').version()
428 4
429 >>> IP('::1').version()
430 6
431 """
432 return self._ipversion
433
435 """Returns Network Prefixlen.
436
437 >>> IP('10.0.0.0/8').prefixlen()
438 8
439 """
440 return self._prefixlen
441
443 """Return the base (first) address of a network as an (long) integer."""
444
445 return self.int()
446
448 """Return the broadcast (last) address of a network as an (long) integer.
449
450 The same as IP[-1]."""
451 return self.int() + self.len() - 1
452
454 """Prints Prefixlen/Netmask.
455
456 Not really. In fact it is our universal Netmask/Prefixlen printer.
457 This is considered an internel function.
458
459 want == 0 / None don't return anything 1.2.3.0
460 want == 1 /prefix 1.2.3.0/24
461 want == 2 /netmask 1.2.3.0/255.255.255.0
462 want == 3 -lastip 1.2.3.0-1.2.3.255
463 """
464
465 if (self._ipversion == 4 and self._prefixlen == 32) or \
466 (self._ipversion == 6 and self._prefixlen == 128):
467 if self.NoPrefixForSingleIp:
468 want = 0
469 if want == None:
470 want = self.WantPrefixLen
471 if want == None:
472 want = 1
473 if want:
474 if want == 2:
475
476 netmask = self.netmask()
477 if type(netmask) != types.IntType and type(netmask) != types.LongType:
478 netmask = netmask.int()
479 return "/%s" % (intToIp(netmask, self._ipversion))
480 elif want == 3:
481 return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
482 else:
483
484 return "/%d" % (self._prefixlen)
485 else:
486 return ''
487
488
489
490
491
492
493
494
495 - def strBin(self, wantprefixlen = None):
496 """Return a string representation as a binary value.
497
498 >>> print IP('127.0.0.1').strBin()
499 01111111000000000000000000000001
500 """
501
502
503 if self._ipversion == 4:
504 bits = 32
505 elif self._ipversion == 6:
506 bits = 128
507 else:
508 raise ValueError, "only IPv4 and IPv6 supported"
509
510 if self.WantPrefixLen == None and wantprefixlen == None:
511 wantprefixlen = 0
512 ret = _intToBin(self.ip)
513 return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
514
516 """Return a string representation in compressed format using '::' Notation.
517
518 >>> IP('127.0.0.1').strCompressed()
519 '127.0.0.1'
520 >>> IP('2001:0658:022a:cafe:0200::1').strCompressed()
521 '2001:658:22a:cafe:200::1'
522 >>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed()
523 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127'
524 """
525
526 if self.WantPrefixLen == None and wantprefixlen == None:
527 wantprefixlen = 1
528
529 if self._ipversion == 4:
530 return self.strFullsize(wantprefixlen)
531 else:
532
533 hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
534
535
536 followingzeros = [0] * 8
537 for i in range(len(hextets)):
538 followingzeros[i] = _countFollowingZeros(hextets[i:])
539
540 compressionpos = followingzeros.index(max(followingzeros))
541 if max(followingzeros) > 1:
542
543
544 hextets = [x for x in self.strNormal(0).split(':')]
545 while compressionpos < len(hextets) and hextets[compressionpos] == '0':
546 del(hextets[compressionpos])
547 hextets.insert(compressionpos, '')
548 if compressionpos + 1 >= len(hextets):
549 hextets.append('')
550 if compressionpos == 0:
551 hextets = [''] + hextets
552 return ':'.join(hextets) + self._printPrefix(wantprefixlen)
553 else:
554 return self.strNormal(0) + self._printPrefix(wantprefixlen)
555
557 """Return a string representation in the usual format.
558
559 >>> print IP('127.0.0.1').strNormal()
560 127.0.0.1
561 >>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
562 2001:658:22a:cafe:200:0:0:1
563 """
564
565 if self.WantPrefixLen == None and wantprefixlen == None:
566 wantprefixlen = 1
567
568 if self._ipversion == 4:
569 ret = self.strFullsize(0)
570 elif self._ipversion == 6:
571 ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
572 else:
573 raise ValueError, "only IPv4 and IPv6 supported"
574
575
576
577 return ret + self._printPrefix(wantprefixlen)
578
580 """Return a string representation in the non mangled format.
581
582 >>> print IP('127.0.0.1').strFullsize()
583 127.0.0.1
584 >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
585 2001:0658:022a:cafe:0200:0000:0000:0001
586 """
587
588 if self.WantPrefixLen == None and wantprefixlen == None:
589 wantprefixlen = 1
590
591 return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
592
593 - def strHex(self, wantprefixlen = None):
594 """Return a string representation in hex format in lower case.
595
596 >>> IP('127.0.0.1').strHex()
597 '0x7f000001'
598 >>> IP('2001:0658:022a:cafe:0200::1').strHex()
599 '0x20010658022acafe0200000000000001'
600 """
601
602 if self.WantPrefixLen == None and wantprefixlen == None:
603 wantprefixlen = 0
604
605 x = hex(self.ip)
606 if x[-1] == 'L':
607 x = x[:-1]
608 return x.lower() + self._printPrefix(wantprefixlen)
609
610 - def strDec(self, wantprefixlen = None):
611 """Return a string representation in decimal format.
612
613 >>> print IP('127.0.0.1').strDec()
614 2130706433
615 >>> print IP('2001:0658:022a:cafe:0200::1').strDec()
616 42540616829182469433547762482097946625
617 """
618
619 if self.WantPrefixLen == None and wantprefixlen == None:
620 wantprefixlen = 0
621
622 x = str(self.ip)
623 if x[-1] == 'L':
624 x = x[:-1]
625 return x + self._printPrefix(wantprefixlen)
626
628 """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
629
630 >>> print IP('127.0.0.1').iptype()
631 PRIVATE
632 >>> print IP('192.168.1.1').iptype()
633 PRIVATE
634 >>> print IP('195.185.1.2').iptype()
635 PUBLIC
636 >>> print IP('::1').iptype()
637 LOOPBACK
638 >>> print IP('2001:0658:022a:cafe:0200::1').iptype()
639 ASSIGNABLE RIPE
640
641 The type information for IPv6 is out of sync with reality.
642 """
643
644
645
646 if self._ipversion == 4:
647 iprange = IPv4ranges
648 elif self._ipversion == 6:
649 iprange = IPv6ranges
650 else:
651 raise ValueError, "only IPv4 and IPv6 supported"
652
653 bits = self.strBin()
654 for i in range(len(bits), 0, -1):
655 if iprange.has_key(bits[:i]):
656 return iprange[bits[:i]]
657 return "unknown"
658
659
661 """Return netmask as an integer.
662
663 >>> "%X" % IP('195.185.0.0/16').netmask().int()
664 'FFFF0000'
665 """
666
667
668 if self._ipversion == 4:
669 locallen = 32 - self._prefixlen
670 elif self._ipversion == 6:
671 locallen = 128 - self._prefixlen
672 else:
673 raise ValueError, "only IPv4 and IPv6 supported"
674
675 return ((2L ** self._prefixlen) - 1) << locallen
676
677
679 """Return netmask as an string. Mostly useful for IPv6.
680
681 >>> print IP('195.185.0.0/16').strNetmask()
682 255.255.0.0
683 >>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
684 /64
685 """
686
687
688 if self._ipversion == 4:
689 locallen = 32 - self._prefixlen
690 return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
691 elif self._ipversion == 6:
692 locallen = 128 - self._prefixlen
693 return "/%d" % self._prefixlen
694 else:
695 raise ValueError, "only IPv4 and IPv6 supported"
696
698 """Return the length of an subnet.
699
700 >>> print IP('195.185.1.0/28').len()
701 16
702 >>> print IP('195.185.1.0/24').len()
703 256
704 """
705
706 if self._ipversion == 4:
707 locallen = 32 - self._prefixlen
708 elif self._ipversion == 6:
709 locallen = 128 - self._prefixlen
710 else:
711 raise ValueError, "only IPv4 and IPv6 supported"
712
713 return 2L ** locallen
714
715
717 """All IPy objects should evaluate to true in boolean context.
718 Ordinarily, they do, but if handling a default route expressed as
719 0.0.0.0/0, the __len__() of the object becomes 0, which is used
720 as the boolean value of the object.
721 """
722 return 1
723
724
726 """Return the length of an subnet.
727
728 Called to implement the built-in function len().
729 It breaks with IPv6 Networks. Anybody knows how to fix this."""
730
731
732
733
734 return int(self.len())
735
736
738 """Called to implement evaluation of self[key].
739
740 >>> ip=IP('127.0.0.0/30')
741 >>> for x in ip:
742 ... print repr(x)
743 ...
744 IP('127.0.0.0')
745 IP('127.0.0.1')
746 IP('127.0.0.2')
747 IP('127.0.0.3')
748 >>> ip[2]
749 IP('127.0.0.2')
750 >>> ip[-1]
751 IP('127.0.0.3')
752 """
753
754 if type(key) != types.IntType and type(key) != types.LongType:
755 raise TypeError
756 if abs(key) >= self.len():
757 raise IndexError
758 if key < 0:
759 key = self.len() - abs(key)
760
761 return self.ip + long(key)
762
763
764
766 """Called to implement membership test operators.
767
768 Should return true if item is in self, false otherwise. Item
769 can be other IP-objects, strings or ints.
770
771 >>> IP('195.185.1.1').strHex()
772 '0xc3b90101'
773 >>> 0xC3B90101L in IP('195.185.1.0/24')
774 1
775 >>> '127.0.0.1' in IP('127.0.0.0/24')
776 1
777 >>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
778 0
779 """
780
781 item = IP(item)
782 if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
783 return 1
784 else:
785 return 0
786
787
789 """Check if two IP address ranges overlap.
790
791 Returns 0 if the two ranged don't overlap, 1 if the given
792 range overlaps at the end and -1 if it does at the beginning.
793
794 >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
795 1
796 >>> IP('192.168.0.0/23').overlaps('192.168.1.255')
797 1
798 >>> IP('192.168.0.0/23').overlaps('192.168.2.0')
799 0
800 >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
801 -1
802 """
803
804 item = IP(item)
805 if item.ip >= self.ip and item.ip < self.ip + self.len():
806 return 1
807 elif self.ip >= item.ip and self.ip < item.ip + item.len():
808 return -1
809 else:
810 return 0
811
812
814 """Dispatch to the prefered String Representation.
815
816 Used to implement str(IP)."""
817
818 return self.strFullsize()
819
820
822 """Print a representation of the Object.
823
824 Used to implement repr(IP). Returns a string which evaluates
825 to an identical Object (without the wnatprefixlen stuff - see
826 module docstring.
827
828 >>> print repr(IP('10.0.0.0/24'))
829 IP('10.0.0.0/24')
830 """
831
832 return("IPint('%s')" % (self.strCompressed(1)))
833
834
836 """Called by comparison operations.
837
838 Should return a negative integer if self < other, zero if self
839 == other, a positive integer if self > other.
840
841 Networks with different prefixlen are considered non-equal.
842 Networks with the same prefixlen and differing addresses are
843 considered non equal but are compared by thair base address
844 integer value to aid sorting of IP objects.
845
846 The Version of Objects is not put into consideration.
847
848 >>> IP('10.0.0.0/24') > IP('10.0.0.0')
849 1
850 >>> IP('10.0.0.0/24') < IP('10.0.0.0')
851 0
852 >>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
853 1
854 >>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
855 0
856
857 """
858
859
860 if self._prefixlen < other.prefixlen():
861 return (other.prefixlen() - self._prefixlen)
862 elif self._prefixlen > other.prefixlen():
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879 return (self._prefixlen - other.prefixlen()) * -1
880 else:
881 if self.ip < other.ip:
882 return -1
883 elif self.ip > other.ip:
884 return 1
885 else:
886 return 0
887
888
890 """Called for the key object for dictionary operations, and by
891 the built-in function hash() Should return a 32-bit integer
892 usable as a hash value for dictionary operations. The only
893 required property is that objects which compare equal have the
894 same hash value
895
896 >>> IP('10.0.0.0/24').__hash__()
897 -167772185
898 """
899
900 thehash = int(-1)
901 ip = self.ip
902 while ip > 0:
903 thehash = thehash ^ (ip & 0x7fffffff)
904 ip = ip >> 32
905 thehash = thehash ^ self._prefixlen
906 return int(thehash)
907
908
910 """Class for handling IP Addresses and Networks."""
911
913 """Return the base (first) address of a network as an IP object.
914
915 The same as IP[0].
916
917 >>> IP('10.0.0.0/8').net()
918 IP('10.0.0.0')
919 """
920 return IP(IPint.net(self))
921
923 """Return the broadcast (last) address of a network as an IP object.
924
925 The same as IP[-1].
926
927 >>> IP('10.0.0.0/8').broadcast()
928 IP('10.255.255.255')
929 """
930 return IP(IPint.broadcast(self))
931
933 """Return netmask as an IP object.
934
935 >>> IP('10.0.0.0/8').netmask()
936 IP('255.0.0.0')
937 """
938 return IP(IPint.netmask(self))
939
940
942 """Return a list with values forming the reverse lookup.
943
944 >>> IP('213.221.113.87/32').reverseNames()
945 ['87.113.221.213.in-addr.arpa.']
946 >>> IP('213.221.112.224/30').reverseNames()
947 ['224.112.221.213.in-addr.arpa.', '225.112.221.213.in-addr.arpa.', '226.112.221.213.in-addr.arpa.', '227.112.221.213.in-addr.arpa.']
948 >>> IP('127.0.0.0/24').reverseNames()
949 ['0.0.127.in-addr.arpa.']
950 >>> IP('127.0.0.0/23').reverseNames()
951 ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
952 >>> IP('127.0.0.0/16').reverseNames()
953 ['0.127.in-addr.arpa.']
954 >>> IP('127.0.0.0/15').reverseNames()
955 ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
956 >>> IP('128.0.0.0/8').reverseNames()
957 ['128.in-addr.arpa.']
958 >>> IP('128.0.0.0/7').reverseNames()
959 ['128.in-addr.arpa.', '129.in-addr.arpa.']
960
961 """
962
963 if self._ipversion == 4:
964 ret =[]
965
966 if self.len() < 2**8:
967 for x in self:
968 ret.append(x.reverseName())
969 elif self.len() < 2**16L:
970 for i in range(0, self.len(), 2**8):
971 ret.append(self[i].reverseName()[2:])
972 elif self.len() < 2**24L:
973 for i in range(0, self.len(), 2**16):
974 ret.append(self[i].reverseName()[4:])
975 else:
976 for i in range(0, self.len(), 2**24):
977 ret.append(self[i].reverseName()[6:])
978 return ret
979 elif self._ipversion == 6:
980 s = hex(self.ip)[2:].lower()
981 if s[-1] == 'l':
982 s = s[:-1]
983 if self._prefixlen % 4 != 0:
984 raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
985 s = list(s)
986 s.reverse()
987 s = '.'.join(s)
988 first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
989 return ["%s.ip6.int." % s[first_nibble_index:]]
990 else:
991 raise ValueError, "only IPv4 and IPv6 supported"
992
993
994
996 """Return the value for reverse lookup/PTR records as RfC 2317 look alike.
997
998 RfC 2317 is an ugly hack which only works for sub-/24 e.g. not
999 for /23. Do not use it. Better set up a Zone for every
1000 address. See reverseName for a way to arcive that.
1001
1002 >>> print IP('195.185.1.1').reverseName()
1003 1.1.185.195.in-addr.arpa.
1004 >>> print IP('195.185.1.0/28').reverseName()
1005 0-15.1.185.195.in-addr.arpa.
1006 """
1007
1008 if self._ipversion == 4:
1009 s = self.strFullsize(0)
1010 s = s.split('.')
1011 s.reverse()
1012 first_byte_index = int(4 - (self._prefixlen / 8))
1013 if self._prefixlen % 8 != 0:
1014 nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
1015 if nibblepart[-1] == 'l':
1016 nibblepart = nibblepart[:-1]
1017 nibblepart += '.'
1018 else:
1019 nibblepart = ""
1020
1021 s = '.'.join(s[first_byte_index:])
1022 return "%s%s.in-addr.arpa." % (nibblepart, s)
1023
1024 elif self._ipversion == 6:
1025 s = hex(self.ip)[2:].lower()
1026 if s[-1] == 'l':
1027 s = s[:-1]
1028 if self._prefixlen % 4 != 0:
1029 nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
1030 if nibblepart[-1] == 'l':
1031 nibblepart = nibblepart[:-1]
1032 nibblepart += '.'
1033 else:
1034 nibblepart = ""
1035 s = list(s)
1036 s.reverse()
1037 s = '.'.join(s)
1038 first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
1039 return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:])
1040 else:
1041 raise ValueError, "only IPv4 and IPv6 supported"
1042
1044 """Called to implement evaluation of self[key].
1045
1046 >>> ip=IP('127.0.0.0/30')
1047 >>> for x in ip:
1048 ... print str(x)
1049 ...
1050 127.0.0.0
1051 127.0.0.1
1052 127.0.0.2
1053 127.0.0.3
1054 >>> print str(ip[2])
1055 127.0.0.2
1056 >>> print str(ip[-1])
1057 127.0.0.3
1058 """
1059 return IP(IPint.__getitem__(self, key))
1060
1062 """Print a representation of the Object.
1063
1064 >>> IP('10.0.0.0/8')
1065 IP('10.0.0.0/8')
1066 """
1067
1068 return("IP('%s')" % (self.strCompressed(1)))
1069
1071 """Emulate numeric objects through network aggregation"""
1072 if self.prefixlen() != other.prefixlen():
1073 raise ValueError, "Only networks with the same prefixlen can be added."
1074 if self.prefixlen < 1:
1075 raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
1076 if self.version() != other.version():
1077 raise ValueError, "Only networks with the same IP version can be added."
1078 if self > other:
1079
1080 return other.__add__(self)
1081 else:
1082 ret = IP(self.int())
1083 ret._prefixlen = self.prefixlen() - 1
1084 return ret
1085
1087 """Parse a string and return the corrospondending IPaddress and the a guess of the IP version.
1088
1089 Following Forms ar recorgnized:
1090 0x0123456789abcdef # IPv4 if <= 0xffffffff else IPv6
1091 123.123.123.123 # IPv4
1092 123.123 # 0-padded IPv4
1093 1080:0000:0000:0000:0008:0800:200C:417A
1094 1080:0:0:0:8:800:200C:417A
1095 1080:0::8:800:200C:417A
1096 ::1
1097 ::
1098 0:0:0:0:0:FFFF:129.144.52.38
1099 ::13.1.68.3
1100 ::FFFF:129.144.52.38
1101 """
1102
1103
1104 if ipstr.startswith('0x'):
1105 ret = long(ipstr[2:], 16)
1106 if ret > 0xffffffffffffffffffffffffffffffffL:
1107 raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
1108 if ret < 0x100000000L:
1109 return (ret, 4)
1110 else:
1111 return (ret, 6)
1112
1113 if ipstr.find(':') != -1:
1114
1115 if ipstr.find(':::') != -1:
1116 raise ValueError, "%r: IPv6 Address can't contain ':::'" % (ipstr)
1117 hextets = ipstr.split(':')
1118 if ipstr.find('.') != -1:
1119
1120 (v4, foo) = parseAddress(hextets[-1])
1121 assert foo == 4
1122 del(hextets[-1])
1123 hextets.append(hex(v4 >> 16)[2:-1])
1124 hextets.append(hex(v4 & 0xffff)[2:-1])
1125 if len(hextets) > 8:
1126 raise ValueError, "%r: IPv6 Address with more than 8 hexletts" % (ipstr)
1127 if len(hextets) < 8:
1128 if '' not in hextets:
1129 raise ValueError, "%r IPv6 Address with less than 8 hexletts and without '::'" % (ipstr)
1130
1131 if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
1132 hextets.remove('')
1133
1134 if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
1135 hextets.remove('')
1136
1137 for foo in range(9-len(hextets)):
1138 hextets.insert(hextets.index(''), '0')
1139 hextets.remove('')
1140 if '' in hextets:
1141 raise ValueError, "%r IPv6 Address may contain '::' only once" % (ipstr)
1142 if '' in hextets:
1143 raise ValueError, "%r IPv6 Address may contain '::' only if it has less than 8 hextets" % (ipstr)
1144 num = ''
1145 for x in hextets:
1146 if len(x) < 4:
1147 x = ((4 - len(x)) * '0') + x
1148 if int(x, 16) < 0 or int(x, 16) > 0xffff:
1149 raise ValueError, "%r: single hextet must be 0 <= hextet <= 0xffff which isn't true for %s" % (ipstr, x)
1150 num += x
1151 return (long(num, 16), 6)
1152
1153 elif len(ipstr) == 32:
1154
1155 return (long(ipstr, 16), 6)
1156
1157 elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
1158
1159 bytes = ipstr.split('.')
1160 if len(bytes) > 4:
1161 raise ValueError, "IPv4 Address with more than 4 bytes"
1162 bytes += ['0'] * (4 - len(bytes))
1163 bytes = [long(x) for x in bytes]
1164 for x in bytes:
1165 if x > 255 or x < 0:
1166 raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
1167 return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
1168
1169 else:
1170
1171
1172
1173 ret = long(ipstr)
1174 if ret > 0xffffffffffffffffffffffffffffffffL:
1175 raise ValueError, "IP Address cant be bigger than 2^128"
1176 if ret <= 0xffffffffL:
1177 return (ret, 4)
1178 else:
1179 return (ret, 6)
1180
1181
1183 """Transform an integer string into an IP address."""
1184
1185
1186 ip = long(ip)
1187
1188 if ip < 0:
1189 raise ValueError, "IPs can't be negative: %d" % (ip)
1190
1191 ret = ''
1192 if version == 4:
1193 if ip > 0xffffffffL:
1194 raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
1195 for l in range(4):
1196 ret = str(ip & 0xffL) + '.' + ret
1197 ip = ip >> 8;
1198 ret = ret[:-1]
1199 elif version == 6:
1200 if ip > 0xffffffffffffffffffffffffffffffffL:
1201 raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
1202 l = '0' * 32 + hex(ip)[2:-1]
1203 for x in range(1,33):
1204 ret = l[-x] + ret
1205 if x % 4 == 0:
1206 ret = ':' + ret
1207 ret = ret[1:]
1208 else:
1209 raise ValueError, "only IPv4 and IPv6 supported"
1210
1211 return ret;
1212
1214 """Return number of bits in address for a certain IP version.
1215
1216 >>> _ipVersionToLen(4)
1217 32
1218 >>> _ipVersionToLen(6)
1219 128
1220 >>> _ipVersionToLen(5)
1221 Traceback (most recent call last):
1222 File "<stdin>", line 1, in ?
1223 File "IPy.py", line 1076, in _ipVersionToLen
1224 raise ValueError, "only IPv4 and IPv6 supported"
1225 ValueError: only IPv4 and IPv6 supported
1226 """
1227
1228 if version == 4:
1229 return 32
1230 elif version == 6:
1231 return 128
1232 else:
1233 raise ValueError, "only IPv4 and IPv6 supported"
1234
1235
1237 """Return Nr. of elements containing 0 at the beginning th the list."""
1238 if len(l) == 0:
1239 return 0
1240 elif l[0] != 0:
1241 return 0
1242 else:
1243 return 1 + _countFollowingZeros(l[1:])
1244
1245
1246 _BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
1247 '4': '0100', '5': '0101', '6': '0110', '7': '0111',
1248 '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
1249 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
1250
1252 """Return the binary representation of an integer as string."""
1253
1254 if val < 0:
1255 raise ValueError, "Only positive Values allowed"
1256 s = hex(val).lower()
1257 ret = ''
1258 if s[-1] == 'l':
1259 s = s[:-1]
1260 for x in s[2:]:
1261 if __debug__:
1262 if not _BitTable.has_key(x):
1263 raise AssertionError, "hex() returned strange result"
1264 ret += _BitTable[x]
1265
1266 while ret[0] == '0' and len(ret) > 1:
1267 ret = ret[1:]
1268 return ret
1269
1271 """Find the highest bit set to 1 in an integer."""
1272 ret = 0
1273 while num > 0:
1274 num = num >> 1
1275 ret += 1
1276 return ret
1277
1279 """Find the highest bit set to 0 in an integer."""
1280
1281
1282 num = long(num)
1283 if num < 0:
1284 raise ValueError, "Only positive Numbers please: %s" % (num)
1285 ret = 0
1286 while num > 0:
1287 if num & 1 == 1:
1288 break
1289 num = num >> 1
1290 ret += 1
1291 return ret
1292
1293
1295 """Check the validity of a prefix
1296
1297 Checks if the variant part of a prefix only has 0s, and the length is
1298 correct.
1299
1300 >>> _checkPrefix(0x7f000000L, 24, 4)
1301 1
1302 >>> _checkPrefix(0x7f000001L, 24, 4)
1303 0
1304 >>> repr(_checkPrefix(0x7f000001L, -1, 4))
1305 'None'
1306 >>> repr(_checkPrefix(0x7f000001L, 33, 4))
1307 'None'
1308 """
1309
1310
1311 bits = _ipVersionToLen(version)
1312
1313 if prefixlen < 0 or prefixlen > bits:
1314 return None
1315
1316 if ip == 0:
1317 zbits = bits + 1
1318 else:
1319 zbits = _count0Bits(ip)
1320 if zbits < bits - prefixlen:
1321 return 0
1322 else:
1323 return 1
1324
1325
1327 """Checks if a netmask is expressable as e prefixlen."""
1328
1329 num = long(netmask)
1330 bits = masklen
1331
1332
1333 while (num & 1) == 0 and bits != 0:
1334 num = num >> 1
1335 bits -= 1
1336 if bits == 0:
1337 break
1338
1339 while bits > 0:
1340 if (num & 1) == 0:
1341 raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
1342 num = num >> 1
1343 bits -= 1
1344
1345
1360
1361
1372
1373
1375 """Return a mask of n bits as a long integer.
1376
1377 From 'IP address conversion functions with the builtin socket module' by Alex Martelli
1378 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
1379 """
1380 if prefixlen == 0:
1381 return 0
1382 elif prefixlen < 0:
1383 raise ValueError, "Prefixlen must be > 0"
1384 return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
1385
1386
1388 import doctest, IPy
1389 return doctest.testmod(IPy)
1390
1391 if __name__ == "__main__":
1392 _test()
1393
1394 t = [0xf0, 0xf00, 0xff00, 0xffff00, 0xffffff00L]
1395 o = []
1396 for x in t:
1397 pass
1398 x = 0L
1399