chiark / gitweb /
Import release 0.1.3
[secnet.git] / ipaddr.py
1 # ipaddr.py -- handle IP addresses and set of IP addresses.
2 # Copyright (C) 1996-2000 Cendio Systems AB
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18 """IP address manipulation.
19
20 This module is useful if you need to manipulate IP addresses or sets
21 of IP addresses.
22
23 The main classes are:
24
25     ipaddr   -- a single IP address.
26     netmask  -- a netmask.
27     network  -- an IP address/netmask combination.  It is often, but
28                 not always, better to use the ip_set class instead.
29     ip_set   -- a set of IP addresses, that may or may not be adjacent.
30
31 So, what can you do with this module?  As a simple example of the kind
32 of things this module can do, this code computes the set of all IP
33 addresses except 127.0.0.0/8 and prints it, expressed as a union of
34 network/netmask pairs.
35
36     import ipaddr
37
38     s = ipaddr.ip_set()
39     s.add_network(ipaddr.network('127.0.0.0', '255.0.0.0',
40                                  ipaddr.DEMAND_FILTER))
41     for nw in s.complement().as_list_of_networks():
42         print nw.ip_str() + '/' + nw.mask.netmask_bits_str
43
44 Errors are reported by raising an exception from the following
45 exception hierarcy:
46
47 Exception       # The standard Python base exception class.
48  |
49  +-- BadType    # Only raised if the programmer makes an error.
50  +-- IpError    # Base class for errors that depend on the data.
51       |
52       +-- SetNotRepresentable
53       +-- BrokenIpAddress
54       |    |
55       |    +-- PartNegative
56       |    +-- PartOverflow
57       |
58       +-- BrokenNetmask
59       |    |
60       |    +-- NeedOneBit
61       |    +-- NeedMoreBits
62       |    +-- NeedLessBits
63       |
64       +-- BrokenNetwork
65             |
66             +-- EmptyIpAddress
67             +-- EmptyNetmask
68             +-- BrokenNetworkAddress
69             +-- NetworkAddressClash
70             +-- BroadcastAddressClash
71   
72 BadType may be raised at any time if the programmer makes an error
73 (such as passing a dictionary to a function that expects a string).
74 SetNotRepresentable may be raised by ip_set.as_str_range().  All other
75 exceptions are raised from the constructors and helper functions only.
76
77 The following constants are present in this module:
78
79     DEMAND_NONE         See class network.
80     DEMAND_FILTER       See class network.
81     DEMAND_NETWORK      See class network.
82     DEMAND_INTERFACE    See class network.
83
84     hostmask            A netmask object with all 32 bits set.
85     complete_network    A network object representing all IP addresses.
86     complete_set        An ip_set object representing all IP addresses.
87     broadcast_network   A network object representing 255.255.255.255.
88     broadcast_set       An ip_set object representing 255.255.255.255.
89     
90 The as_ipaddr function can be used when you have an object that you
91 know are an ipaddr or network, and you want to get the ipaddr part.
92
93 All the other functions in this module are internal helper functions,
94 and they should not be used.
95
96 The internal representation used for IP addresses is currently a long
97 number.  That may change in the future, so where the internal
98 representation is visible, you should do nothing with it except
99 compare it to None.
100
101 This module was developed by Cendio Systems AB for use in the Fuego
102 Firewall.  Bug reports can be sent to Per Cederqvist <ceder@cendio.se>
103 who is currently acting as maintainer for this module.
104
105 Brief history:
106     1997-03-11      Module created, and used internally.
107     2000-03-09 1.0: First non-public beta release outside of Cendio Systems.
108     2000-03-17 1.1: First public release under the GNU GPL license.
109
110 """
111
112
113 import copy
114 import string
115 import types
116
117 # The error messages are marked with a call to this function, so that
118 # they can easily be found and translated.
119 def _(s):
120     return s
121
122 # The exception hierarchy.
123 class IpError(Exception):
124     """Base class for errors that are cause by errors in input data.
125     """
126     def __str__(self):
127         return self.format % self.args
128
129 class SetNotRepresentable(IpError):
130     format = _("The set of IP addresses cannot be represented "
131                "as a single network range")
132
133 class BrokenIpAddress(IpError):
134     format = _("Felaktigt IP-nummer")
135
136 class PartNegative(BrokenIpAddress):
137     format = _("En komponent i IP-numret är negativ")
138
139 class PartOverflow(BrokenIpAddress):
140     format = _("En komponent i IP-numret är större än 255")
141
142 class BrokenNetmask(IpError):
143     format = _("Felaktig nätmask")
144
145 class NeedOneBit(BrokenNetmask):
146     format = _("Minst en bit måste vara ettställd")
147
148 class NeedMoreBits(BrokenNetmask):
149     format = _("Minst %d bitar måste vara ettställda")
150
151 class NeedLessBits(BrokenNetmask):
152     format = _("Högst %d bitar får vara ettställda")
153
154 class BrokenNetwork(IpError):
155     """Base class for errors regarding network objects.
156     """
157
158 class EmptyIpAddress(BrokenNetwork):
159     format = _("IP-nummer ej ifyllt")
160
161 class EmptyNetmask(BrokenNetwork):
162     format = _("Nätmask ej ifylld")
163
164 class BrokenNetworkAddress(BrokenNetwork):
165     format = _("Med denna nätmask är %s ett otillåtet nätverksnummer; "
166                "menar du %s?")
167
168 class NetworkAddressClash(BrokenNetwork):
169     format = _("Med denna nätmask krockar Fuegons adress med nätnumret")
170
171 class BroadcastAddressClash(BrokenNetwork):
172     format = _("Med denna nätmask krockar Fuegons adress "
173                "med broadcastadressen")
174
175 class BadType(Exception):
176     """An object of an unexpected type was passed to a function.
177     """
178     pass
179
180 # These constants are used with netmasks and networks to specify what
181 # the code expects.
182 #
183 #  DEMAND_NONE: netmask 0-32 (inclusive)
184 #  DEMAND_FILTER: netmask 0-32, the host part must be all zeroes
185 #  DEMAND_NETWORK: netmask 1-32, the host part must be all zeroes
186 #  DEMAND_INTERFACE: netmask 1-30, the host part must *not* be all zeroes
187
188 DEMAND_NONE = 1
189 DEMAND_FILTER = 2
190 DEMAND_NETWORK = 3
191 DEMAND_INTERFACE = 4
192
193 def bits_to_intrep(bits):
194     """Convert BITS to the internal representation.
195
196     BITS should be a number in the range 0-32 (inclusive).
197
198     """
199     return pow(2L, 32) - pow(2L, 32-bits)
200
201
202 def intrep_with_bit_set(bit):
203     """Return an internal representation with bit BIT set.
204
205     BIT should be a number in the range 1-32, where bit 1 is the
206     leftmost.  Examples:
207
208       intrep_with_bit_set(1) --> the internal representation of 128.0.0.0
209       intrep_with_bit_set(32) --> the internal representation of 0.0.0.1
210     """
211     assert 0 < bit and bit <= 32
212
213     return pow(2L, 32-bit)
214
215
216 __ONES = {0:0, 128:1, 192:2, 224:3,
217           240:4, 248:5, 252:6, 254:7}
218
219 def tuple_to_bits(mask):
220     """Convert MASK to bits.
221
222     MASK should be a tuple of four integers in the range 0-255 (inclusive).
223
224     Raises BrokenNetmask if MASK is not a valid netmask.
225     """
226
227     if mask == None:
228         return None
229     else:
230         (a, b, c, d) = mask
231
232         if a == 255 and b == 255 and c == 255 and d == 255:
233             return 32
234
235         try:
236             if a == 255 and b == 255 and c == 255:
237                 return 24 + __ONES[d]
238             elif a == 255 and b == 255  and d == 0:
239                 return 16 + __ONES[c]
240             elif a == 255 and c == 0  and d == 0:
241                 return 8 + __ONES[b]
242             elif b == 0 and c == 0  and d == 0:
243                 return __ONES[a]
244         except KeyError:
245             pass
246
247         raise BrokenNetmask()
248
249
250 def intrep_to_dotted_decimal(t):
251     """Convert T to dotted-decimal notation.
252
253     T should be the internal representation used py ipaddr.py.
254     """
255
256     return (str(int(t>>24)) + '.' + str(int((t>>16) & 255))
257             + '.' + str(int((t>>8) & 255)) + '.' + str(int(t & 255)))
258
259
260 def as_ipaddr(nwip):
261     """Return the IP address object of NWIP.
262
263     NWIP may be an ipaddr object, which is returned unchanged,
264     or a network object, in which case the ipaddr part of it is
265     returned.
266     """
267
268     if isinstance(nwip, ipaddr):
269         return nwip
270     elif isinstance(nwip, network):
271         return nwip.ip
272     else:
273         raise BadType('Expected a network or ipaddr object', nwip)
274
275
276 class ipaddr:
277     """Handle IP addresses.
278
279     Sample use:
280
281         ip1 = ipaddr('12.3.5.1')
282         ip2 = ipaddr([12, 3, 5, 1])
283         print ip1.ip_str()
284         >>> '12.3.5.1'
285         print ip1.intrep
286         >>> 201524481L
287         print ip2.ip_str()
288         >>> '12.3.5.1'
289         print ip2.intrep
290         >>> 201524481L
291
292     An ipaddr object can have two states: empty or good.
293     The status can be examined like this:
294
295         if ip.intrep == None:
296             handle_empty(m.user_input())
297         else:
298             handle_good(ip)
299
300     All other members should only be used in the good state.  The
301     value stored in the intrep member should only be compared against
302     None.  The type and value of it is an internal detail that may
303     change in the future.
304
305     """
306
307     def __init__(self, ip):
308         """Create an ipaddr from IP (a string, tuple or list).
309
310         The empty string or None may be given; it is handled as the
311         empty IP number.
312         """
313
314         if type(ip) == types.StringType:
315             self.__user_input = ip
316             ip = string.strip(ip)
317         else:
318             self.__user_input = None
319
320         # The empty IP number?
321
322         if ip == '' or ip == None:
323             self.__ip_str = ''
324             self.intrep = None
325             if ip == None:
326                 self.__user_input = ''
327             return
328
329         if type(ip) == types.StringType:
330
331             # Convert a string.
332
333             try:
334                 [a, b, c, d] = map(string.atoi, string.splitfields(ip, '.'))
335             except:
336                 raise BrokenIpAddress()
337
338             if a < 0 or b < 0 or c < 0 or d < 0:
339                 raise PartNegative()
340
341             if a > 255 or b > 255 or c > 255 or d > 255:
342                 raise PartOverflow()
343
344             self.intrep = (long(a) << 24) + (b << 16) + (c << 8) + d
345
346         else:
347             assert type(ip) == types.LongType
348             self.intrep = ip
349
350         self.__ip_str = None
351
352     def ip_str(self):
353         if self.__ip_str == None:
354             self.__ip_str = intrep_to_dotted_decimal(self.intrep)
355         return self.__ip_str
356
357     def user_input(self):
358         if self.__user_input == None:
359             # This object was constructed from a tuple.  Generate a string.
360             self.__user_input = self.ip_str()
361         return self.__user_input
362
363     def compare(self, other):
364         """Compare this IP address with OTHER.
365
366         Returns -1, 0 or 1 if this IP address is less than, equal to,
367         or greater than OTHER (which should be an ipaddr object).
368         """
369         # FIXME: should we rename this __cmp__?  It needs to handle
370         # other types of the OTHER argument first.
371
372         if self.intrep == other.intrep:
373             return 0
374         if self.intrep < other.intrep:
375             return -1
376         else:
377             return 1
378
379     def __str__(self):
380         if self.intrep is None:
381             return "<ipaddr empty>"
382         else:
383             return "<ipaddr %s>" % self.ip_str()
384
385     def __repr__(self):
386         if self.intrep is None:
387             return "ipaddr.ipaddr('')"
388         else:
389             return "ipaddr.ipaddr('%s')" % self.ip_str()
390
391
392 class netmask:
393     """Handle netmasks.
394
395     Sample use:
396
397         # Four ways to initialize a netmask.
398         nm1 = netmask('255.255.128.0', DEMAND_NONE)
399         nm2 = netmask([255, 255, 128, 0], DEMAND_NONE)
400         nm3 = netmask('17', DEMAND_NONE)
401         nm4 = netmask(17, DEMAND_NONE)
402         print nm1.netmask_str()
403         >>> '255.255.128.0'
404         print nm1.intrep
405         >>> (255, 255, 128, 0)
406         print nm1.netmask_bits
407         >>> 17
408         print nm1.netmask_bits_str
409         >>> '17'
410
411     A netmask can have two states: empty or good.  The state
412     can be examined like this:
413
414         if m.intrep == None:
415             handle_empty(m.user_input())
416         else:
417             handle_good(m)
418
419     All other members should be used only in the good state.
420
421     """
422
423     def __check_range(self, bits, minbits, maxbits):
424         if bits < minbits:
425             if minbits == 1:
426                 raise NeedOneBit()
427             else:
428                 raise NeedMoreBits(minbits)
429         elif bits > maxbits:
430             raise NeedLessBits(maxbits)
431
432
433     def __set_from_bits(self, bits, minbits, maxbits):
434         self.__check_range(bits, minbits, maxbits)
435         self.intrep = bits_to_intrep(bits)
436         self.netmask_bits = bits
437
438
439     def __set_from_tuple(self, tpl, minbits, maxbits):
440         bits = tuple_to_bits(tpl)
441         self.__check_range(bits, minbits, maxbits)
442         self.intrep = bits_to_intrep(bits)
443         self.netmask_bits = bits
444
445     DEMANDS = {DEMAND_NONE:(0,32),
446                DEMAND_FILTER:(0,32),
447                DEMAND_NETWORK:(1,32),
448                DEMAND_INTERFACE:(1,30)}
449
450     def __init__(self, mask, demand):
451         """Create a netmask from MASK (a string, tuple or number) and DEMAND.
452
453         The empty string or None may be given; it is handled as the
454         empty netmask.
455
456         See class network for a description of the DEMAND parameter.
457         """
458
459         (minbits, maxbits) = self.DEMANDS[demand]
460         self.demand = demand
461
462         if type(mask) == types.StringType:
463             self.__user_input = mask
464             mask = string.strip(mask)
465         else:
466             self.__user_input = None
467
468         if mask == '' or mask == None:
469
470             # Handle empty netmasks.
471
472             self.__netmask_str = ''
473             self.intrep = None
474             self.netmask_bits_str = ''
475             self.netmask_bits = None
476             if self.__user_input == None:
477                 self.input = ''
478             return
479
480         # Decode the MASK argument and set self.netmask_bits
481         # and self.intrep.
482
483         if type(mask) == types.StringType:
484
485             # Is this a string containing a single number?
486             try:
487                 bits = string.atoi(mask)
488             except (OverflowError, ValueError):
489                 bits = None
490
491             if bits != None:
492
493                 # This is a string containing a single number.
494
495                 self.__set_from_bits(bits, minbits, maxbits)
496
497             else:
498
499                 # Interpret the netmask as a dotted four-tuple.
500                 try:
501                     [a, b, c, d] = map(string.atoi,
502                                        string.splitfields(mask, '.'))
503                 except:
504                     raise BrokenNetmask()
505
506                 self.__set_from_tuple((a, b, c, d), minbits, maxbits)
507
508         elif type(mask) == types.IntType:
509
510             # This is a number, representing the number of bits in the mask.
511
512             self.__set_from_bits(mask, minbits, maxbits)
513
514         else:
515
516             # This is a tuple or list.
517
518             if len(mask) != 4:
519                 raise BadType('Wrong len of tuple/list')
520
521             (a, b, c, d) = (mask[0], mask[1], mask[2], mask[3])
522
523             self.__set_from_tuple((a, b, c, d), minbits, maxbits)
524
525         self.__netmask_str = None
526         self.netmask_bits_str = repr(self.netmask_bits)
527
528     def netmask_str(self):
529         if self.__netmask_str == None:
530             self.__netmask_str = intrep_to_dotted_decimal(self.intrep)
531         return self.__netmask_str
532
533     def user_input(self):
534         if self.__user_input == None:
535             # This object was constructed from a tuple or an integer.
536             self.__user_input = self.ip_str()
537         return self.__user_input
538
539     def __str__(self):
540         if self.intrep is None:
541             return "<netmask empty>"
542         else:
543             return "<netmask /%d>" % self.netmask_bits
544
545     def __repr__(self):
546         if self.intrep is None:
547             return "ipaddr.netmask('')"
548         else:
549             return "ipaddr.netmask(%d, %d)" % (self.netmask_bits, self.demand)
550
551
552 hostmask = netmask(32, DEMAND_NONE)
553         
554
555 class network:
556     """Designate a network or host.
557
558     The constructor takes three arguments: the IP number part, the
559     netmask part, and a demand parameter.  See class ipaddr and class
560     netmask for a description of the first two arguments.  The demand
561     argument can be one of the following constants:
562
563     DEMAND_NONE
564         No special demands.
565     DEMAND_FILTER
566         The host part must be all zeroes.
567     DEMAND_NETWORK
568         The netmask must be 1-32
569         The host part must be all zeroes.
570     DEMAND_INTERFACE
571         The netmask must be 1-30
572         The host part must *not* be all zeroes (the network address)
573         or all ones (the broadcast address).
574
575     The following members exist and are set by the constructor:
576
577       ip.user_input()           # a caching function
578       ip_str()                  # a caching function
579       ip.intrep
580       mask.user_input()         # a caching function
581       mask.netmask_str()        # a caching function
582       mask.intrep
583       mask.netmask_bits
584       mask.netmask_bits_str
585       network_str()             # a caching function
586       network_intrep
587       broadcast_str()           # a caching function
588       broadcast_intrep
589       host_part_str()           # a caching function
590       host_part_intrep
591
592     """
593
594     def __init__(self, ip, mask, demand):
595         self.ip = ipaddr(ip)
596         self.mask = netmask(mask, demand)
597
598         if self.ip.intrep == None:
599             raise EmptyIpAddress()
600
601         if self.mask.intrep == None:
602             raise EmptyNetmask()
603
604         self._precompute()
605
606     def _precompute(self):
607         self.__lower_str = None
608         self.__upper_str = None
609
610         self.network_intrep = self.ip.intrep & self.mask.intrep
611         self.broadcast_intrep = (self.network_intrep |
612                                 (pow(2L, 32)-1-self.mask.intrep))
613         self.host_part_intrep = self.ip.intrep - self.network_intrep
614
615         self.__network_str = None
616         self.__broadcast_str = None
617         self.__host_part_str = None
618
619         demand = self.mask.demand
620
621         if demand == DEMAND_NONE:
622             pass
623         elif demand == DEMAND_FILTER or demand == DEMAND_NETWORK:
624             if self.host_part_intrep != 0L:
625                 raise BrokenNetworkAddress(self.ip_str(), self.network_str())
626         elif demand == DEMAND_INTERFACE:
627             if self.host_part_intrep == 0L:
628                 raise NetworkAddressClash()
629             elif self.broadcast_intrep == self.ip.intrep:
630                 raise BroadcastAddressClash()
631         else:
632             raise BadType('Bad value for the demand parameter', demand)
633
634     def network_str(self):
635         if self.__network_str == None:
636             self.__network_str = intrep_to_dotted_decimal(self.network_intrep)
637         return self.__network_str
638
639     def broadcast_str(self):
640         if self.__broadcast_str == None:
641             self.__broadcast_str = intrep_to_dotted_decimal(
642                 self.broadcast_intrep)
643         return self.__broadcast_str
644
645     def host_part_str(self):
646         if self.__host_part_str == None:
647             self.__host_part_str = intrep_to_dotted_decimal(
648                 self.host_part_intrep)
649         return self.__host_part_str
650
651     def overlaps(self, other):
652         """Returns true if the network overlaps with OTHER.
653
654         OTHER must be a network object or an ipaddr object.  If it
655         is empty this method will always return false.
656
657         """
658
659         if self.network_intrep == None:
660             return 0
661
662         if isinstance(other, ipaddr):
663
664             if other.intrep == None:
665                 return 0
666
667             return (self.mask.intrep & other.intrep) == self.network_intrep
668         else:
669             if other.network_intrep == None:
670                 return 0
671
672             mask = self.mask.intrep & other.mask.intrep
673             return (mask & self.ip.intrep) == (mask & other.ip.intrep)
674
675     def intersection(self, other):
676         """Return the intersection of the network and OTHER.
677
678         The return value is a network object with DEMAND_FILTER.  If
679         the intersection is empty this method will return None.
680
681         OTHER must be a network object or an ipaddr object.  The
682         intersection will be empty if it is empty.
683         """
684
685         if self.network_intrep == None:
686             return None
687
688         if isinstance(other, ipaddr):
689
690             if other.intrep == None:
691                 return None
692
693             prefix_mask = self.mask.intrep
694             short_net = self.network_intrep
695             long_ip = other.intrep
696             result = network(other.intrep, 32, DEMAND_FILTER)
697         else:
698             if other.network_intrep == None:
699                 return None
700             
701             if self.mask.netmask_bits < other.mask.netmask_bits:
702                 prefix_mask = self.mask.intrep
703                 short_net = self.network_intrep
704                 long_ip = other.network_intrep
705                 result = network(other.network_intrep, other.mask.netmask_bits,
706                                  DEMAND_FILTER)
707             else:
708                 prefix_mask = other.mask.intrep
709                 short_net = other.network_intrep
710                 long_ip = self.network_intrep
711                 result = network(self.network_intrep, self.mask.netmask_bits,
712                                  DEMAND_FILTER)
713
714         if (long_ip & prefix_mask) != (short_net & prefix_mask):
715             return None
716
717         return result
718
719     def is_subset(self, nwip):
720         """Return true if NWIP is a subset of this network.
721
722         NWIP must be a network object or an ipaddr object.
723         """
724
725         if not self.overlaps(nwip):
726             return 0
727
728         if isinstance(nwip, ipaddr):
729             return 1
730
731         return nwip.mask.netmask_bits <= self.mask.netmask_bits
732
733     def is_same_set(self, nwip):
734         """Return true if NWIP contains the same set as this network.
735
736         NWIP must be a network object or an ipaddr object.
737         """
738
739         if isinstance(nwip, ipaddr):
740             return (self.mask.netmask_bits == 32
741                     and self.ip.intrep == nwip.intrep)
742         else:
743             return (self.mask.netmask_bits == nwip.mask.netmask_bits
744                     and self.network_intrep == nwip.network_intrep)
745
746     def subtract(self, nwip):
747         """Create a list of new network objects by subtracting NWIP from self.
748
749         The result consists of networks that together span all
750         IP addresses that are present in self, except those that are
751         present in NWIP.  (The result may be empty or contain several
752         disjoint network objects.)
753
754         Don't use this!  This method is slow.  The ip_set class can do
755         this kind of things in a more efficient way.
756         """
757
758         if not self.overlaps(nwip):
759             # No overlap at all, so NWIP cannot affect the result.
760             return [self]
761
762         if isinstance(nwip, ipaddr):
763             bits = 32
764             intrep = nwip.intrep
765         else:
766             assert isinstance(nwip, network)
767             bits = nwip.mask.netmask_bits
768             intrep = nwip.ip.intrep
769         nets = []
770         while bits > self.mask.netmask_bits:
771             nets.append(network(compute_neighbor(intrep, bits),
772                                 bits, DEMAND_FILTER))
773             bits = bits - 1
774         return nets
775
776     def subtract_nwips(self, nwips):
777         """Create a list of new network objects by subtracting NWIPS.
778
779         The result consists of networks that together span all
780         IP addresses that are present in self, except those that are
781         present in NWIPS.  (The result may be empty or contain
782         several disjoint network objects.)  NWIPS should be a list
783         of network or ipaddr objects.
784
785         Don't use this!  This method is slow.  The ip_set class can do
786         this kind of things in a more efficient way.
787         """
788
789         subtracted = [self]
790         for s in nwips:
791             # precondition<A>: SUBTRACTED is a list of networks
792             tmp = []
793             for nw in subtracted:
794                 tmp = tmp + nw.subtract(s)
795             subtracted = tmp
796             # postcondition: SUBTRACTED is a list of networks that
797             # spans all IP addresses that were present in
798             # precondition<A>, except those that are present in S.
799
800         return subtracted
801
802     def __compute_lower_upper(self):
803         if self.__lower_str != None:
804             return
805         assert self.network_intrep != None and self.broadcast_intrep != None
806
807         self.__lower_str = intrep_to_dotted_decimal(self.network_intrep + 1)
808         self.__upper_str = intrep_to_dotted_decimal(self.broadcast_intrep - 1)
809
810     def lower_host(self):
811         self.__compute_lower_upper()
812         return self.__lower_str
813
814     def upper_host(self):
815         self.__compute_lower_upper()
816         return self.__upper_str
817
818     def __repr__(self):
819         return _("{network %s/%d}") % (self.ip_str(), self.mask.netmask_bits)
820
821     def ip_str(self):
822         return self.ip.ip_str()
823
824
825 class ip_set:
826     def __init__(self, nwip=None):
827         """Create an ip_set.
828
829         If the optional argument NWIP is supplied, the set is
830         initialized to it, otherwise the created set will be empty.
831         NWIP must be a network or ipaddr object.
832         """
833
834         # [[0L, 3L], [5L, 7L]] means 0.0.0.0/29 \ 0.0.0.4/32
835         self.__set = []
836
837         if nwip != None:
838             self.append(nwip)
839
840     def subtract_set(self, other):
841         """Remove all IP-numbers in OTHER from this.
842
843         OTHER should be an ip_set object.
844         """
845
846         self.subtract_list(other.__set)
847
848     def subtract_ips(self, ips):
849         """Remove all IP-numbers in IPS from this.
850
851         IPS should be a list of ipaddr objects.
852         """
853
854         for ip in ips:
855             self.subtract_list([[ip.intrep, ip.intrep]])
856
857     def subtract_list(self, other):
858         # Don't use this method directly, unless you are the test suite.
859         ix = 0
860         iy = 0
861         while ix < len(self.__set) and iy < len(other):
862             if self.__set[ix][1] < other[iy][0]:
863                 # The entire range survived.
864                 ix = ix + 1
865             elif self.__set[ix][0] > other[iy][1]:
866                 # The entire other range is unused, so discard it.
867                 iy = iy + 1
868             elif self.__set[ix][0] >= other[iy][0]:
869                 if self.__set[ix][1] <= other[iy][1]:
870                     # The entire range is subtracted.
871                     del self.__set[ix]
872                 else:
873                     # The start of the range is subtracted, but
874                     # the rest of the range may survive.  (As a matter
875                     # of fact, at least one number *will* survive,
876                     # since there should be a gap between other[iy][1]
877                     # and other[iy+1][0], but we don't use that fact.)
878                     self.__set[ix][0] = other[iy][1] + 1
879                     iy = iy + 1
880             else:
881                 # The first part of the range survives.
882                 end = self.__set[ix][1]
883                 assert self.__set[ix][1] >= other[iy][0]
884                 self.__set[ix][1] = other[iy][0] - 1
885                 ix = ix + 1
886                 if end > other[iy][1]:
887                     # The part that extends past the subtractor may survive.
888                     self.__set[ix:ix] = [[other[iy][1] + 1, end]]
889                 # Retain the subtractor -- it may still kill some
890                 # other range.
891
892     def add_set(self, other):
893         """Add all IP-numbers in OTHER to this.
894
895         OTHER should be an ip_set object.
896         """
897
898         self.add_list(other.__set)
899
900     def add_list(self, other):
901         # Don't use this method directly, unless you are the test suite.
902         ix = 0
903         iy = 0
904         res = []
905         while ix < len(self.__set) or iy < len(other):
906             # Remove the first range
907             if ix < len(self.__set):
908                 if iy < len(other):
909                     if self.__set[ix][0] < other[iy][0]:
910                         rng = self.__set[ix]
911                         ix = ix + 1
912                     else:
913                         rng = other[iy]
914                         iy = iy + 1
915                 else:
916                     rng = self.__set[ix]
917                     ix = ix + 1
918             else:
919                 rng = other[iy]
920                 iy = iy + 1
921
922             # Join this range to the list we already have collected.
923             if len(res) == 0:
924                 # This is the first element.
925                 res.append(rng)
926             elif rng[0] <= res[-1][1] + 1:
927                 # This extends (or is consumed by) the last range.
928                 res[-1][1] = max(res[-1][1], rng[1])
929             else:
930                 # There is a gap between the previous range and this one.
931                 res.append(rng)
932
933         self.__set = res
934
935     def append(self, nwip):
936         """Add NWIP to this.
937
938         NWIP should be a network object or ipaddr object.
939         """
940
941         if isinstance(nwip, network):
942             self.add_network(nwip)
943         else:
944             self.add_ipaddr(nwip)
945
946     def add_network(self, nw):
947         """Add NW to this.
948
949         NW should be a network object.
950         """
951         self.add_list([[nw.network_intrep, nw.broadcast_intrep]])
952
953     def add_range(self, lo_ip, hi_ip):
954         """Add the range of IP numbers specified by LO_IP and HI_IP to this.
955
956         LO_IP and HI_IP should be ipaddr objects.  They specify a
957         range of IP numbers.  Both LO_IP and HI_IP are included in the
958         range.
959         """
960
961         assert lo_ip.intrep != None
962         assert hi_ip.intrep != None
963         assert lo_ip.intrep <= hi_ip.intrep
964         self.add_list([[lo_ip.intrep, hi_ip.intrep]])
965
966     def add_ipaddr(self, ip):
967         """Add IP to this.
968
969         IP should be an ipaddr object.
970         """
971
972         assert ip.intrep != None
973         self.add_list([[ip.intrep, ip.intrep]])
974
975     def complement(self):
976         """Return everything not contained in this ip_set.
977
978         The return value is a new ip_set.  This is not modified.
979         """
980
981         pre = -1L
982         lst = []
983         for [lo, hi] in self.__set:
984             if lo != 0:
985                 lst.append([pre+1, lo-1])
986             pre = hi
987         if pre < pow(2L, 32) - 1:
988             lst.append([pre+1, pow(2L, 32) - 1])
989         res = ip_set()
990         res.add_list(lst)
991         return res
992
993     def intersection(self, other):
994         """Return the intersection of this and OTHER.
995
996         The return value is a new ip_set.  This is not modified.
997         OTHER should be an ip_set, network or ipaddr object.
998         """
999
1000         res = []
1001         ix = 0
1002         iy = 0
1003         x = copy.deepcopy(self.__set)
1004
1005         if isinstance(other, ip_set):
1006             y = copy.deepcopy(other.__set)
1007         elif isinstance(other, network):
1008             y = [[other.network_intrep, other.broadcast_intrep]]
1009         elif isinstance(other, ipaddr):
1010             y = [[other.intrep, other.intrep]]
1011         else:
1012             raise BadType('expected an ip_set, network or ipaddr argument')
1013
1014         while ix < len(x) and iy < len(y):
1015             if x[ix][1] < y[iy][0]:
1016                 # The first entry on x doesn't overlap with anything on y.
1017                 ix = ix + 1
1018             elif x[ix][0] > y[iy][1]:
1019                 # The first entry on y doesn't overlap with anything on x.
1020                 iy = iy + 1
1021             else:
1022                 # Some overlap exists.
1023
1024                 # Trim away any leading edges.
1025                 if x[ix][0] < y[iy][0]:
1026                     # x starts before y
1027                     x[ix][0] = y[iy][0]
1028                 elif x[ix][0] > y[iy][0]:
1029                     # y starts before x
1030                     y[iy][0] = x[ix][0]
1031
1032                 # The ranges start at the same point (at least after
1033                 # the trimming).
1034                 if x[ix][1] == y[iy][1]:
1035                     # The ranges are equal.
1036                     res.append(x[ix])
1037                     ix = ix + 1
1038                     iy = iy + 1
1039                 elif x[ix][1] < y[iy][1]:
1040                     # x is the smaller range
1041                     res.append(x[ix])
1042                     ix = ix + 1
1043                 else:
1044                     # y is the smaller range
1045                     res.append(y[iy])
1046                     iy = iy + 1
1047
1048         result = ip_set()
1049         result.add_list(res)
1050         return result
1051
1052
1053     def as_list_of_networks(self):
1054         """Return this set as a list of networks.
1055
1056         The returned value is a list of network objects, that are
1057         created with DEMAND_FILTER.  This method may be expensive, so
1058         it should only be used when necessary.
1059         """
1060
1061         bm = []
1062         for [a, b] in self.__set:
1063
1064             lomask = 1L
1065             lobit = 1L
1066             himask = pow(2L, 32)-2
1067             bits = 32
1068             while a <= b:
1069                 if a & lomask != 0L:
1070                     bm.append((bits, a))
1071                     a = a + lobit
1072                 elif b & lomask != lomask:
1073                     bm.append((bits, b & himask))
1074                     b = b - lobit
1075                 else:
1076                     lomask = (lomask << 1) | 1
1077                     lobit = lobit << 1
1078                     himask = himask ^ lobit
1079                     bits = bits - 1
1080                     assert(bits >= 0)
1081         bm.sort()
1082         res = []
1083         for (mask, ip) in bm:
1084             res.append(network(ip, mask, DEMAND_FILTER))
1085         return res
1086
1087     def as_list_of_ranges(self):
1088         """Return the set of IP addresses as a list of ranges.
1089
1090         Each range is a list of two long numbers.  Sample return
1091         value: [[1L, 3L], [0x7f000001L, 0x7f000001L]], meaning
1092         the set 0.0.0.1, 0.0.0.2, 0.0.0.3, 127.0.0.1.
1093         """
1094
1095         # This method is currently very cheap, since this is the
1096         # current internal representation.
1097
1098         return self.__set
1099
1100     def as_str_range(self):
1101         """Return the set as a string, such as "1.2.3.4-1.2.3.8".
1102
1103         The returned value always has the form a.b.c.d-e.f.g.h.
1104         Raises SetNotRepresentable if the set cannot be represented as a
1105         single interval, or if it is the empty set.
1106         """
1107         if len(self.__set) != 1:
1108             raise SetNotRepresentable()
1109         return "%s-%s" % (intrep_to_dotted_decimal(self.__set[0][0]),
1110                           intrep_to_dotted_decimal(self.__set[0][1]))
1111
1112     def contains(self, ip):
1113         """Return true if IP is contained in the set.
1114
1115         IP should be an ipaddr object.  The empty ipaddr is never contained.
1116         """
1117
1118         if ip.intrep == None:
1119             return 0
1120
1121         for [lo, hi] in self.__set:
1122             if lo <= ip.intrep <= hi:
1123                 return 1
1124         return 0
1125
1126     def overlaps(self, nwip):
1127         """Return true if NWIP overlaps the set of IP addresses.
1128
1129         NWIP may be an ipaddr, network or ip_set object.
1130         """
1131
1132         if isinstance(nwip, ipaddr):
1133             return self.contains(nwip)
1134         elif isinstance(nwip, ip_set):
1135             # This could be optimized -- we don't really need
1136             # to compute the intersection.
1137             return not self.intersection(nwip).is_empty()
1138         elif isinstance(nwip, network):
1139             wanted_low = nwip.network_intrep
1140             wanted_high = nwip.broadcast_intrep
1141             if wanted_low == None or wanted_high == None:
1142                 return 0
1143             for [lo, hi] in self.__set:
1144                 if lo > wanted_high:
1145                     # We are past the interresting interval.
1146                     return 0
1147                 if lo >= wanted_low or hi >= wanted_low:
1148                     return 1
1149             return 0
1150         else:
1151             raise BadType('Expected an ipaddr, ip_set or network instance')
1152
1153     def is_empty(self):
1154         """Return true if this ip_set is empty.
1155         """
1156
1157         return len(self.__set) == 0
1158
1159     def any_ip(self):
1160         """Return one of the IP addresses contained in ip_set.
1161
1162         This method may only be called if the set is non-empty.  You
1163         can use the is_empty method to test for emptiness.
1164
1165         This picks an IP address from the set and returns it as an
1166         ipaddr object.  Given the same set of IP addresses, this
1167         method will always return the same IP address, but which IP
1168         address it chooses is explicitly undocumented and may change
1169         if the underlying implementation of ip_set ever changes.
1170         """
1171
1172         assert not self.is_empty()
1173         return ipaddr(self.__set[0][0])
1174
1175     def __str__(self):
1176         res = []
1177         for rng in self.__set:
1178             if rng[0] == rng[1]:
1179                 res.append(intrep_to_dotted_decimal(rng[0]))
1180             else:
1181                 res.append('%s-%s' % (intrep_to_dotted_decimal(rng[0]),
1182                                       intrep_to_dotted_decimal(rng[1])))
1183         return '<ipaddr.ip_set(%s)>' % string.join(res, ', ')
1184
1185 complete_network = network(0L, 0, DEMAND_FILTER)
1186 complete_set = ip_set(complete_network)
1187 broadcast_network = network('255.255.255.255', 32, DEMAND_FILTER)
1188 broadcast_set = ip_set(broadcast_network)
1189
1190 def compute_neighbor(intrep, bits):
1191     xor_mask = intrep_with_bit_set(bits)
1192     and_mask = bits_to_intrep(bits)
1193     return (intrep ^ xor_mask) & and_mask
1194
1195
1196 if __name__ == '__main__':
1197     # Test/demo code.  With no arguments, this will print a page
1198     # of data that can be useful when trying to interpret an
1199     # ipnumber/netmask pair.  With two arguments, it will print some
1200     # information about the IP number and netmask that was entered.
1201
1202     import sys
1203     if len(sys.argv) == 1:
1204         print "Netmasks\n========"
1205         for i in range(0, 17):
1206             if i != 16:
1207                 print '%2d' % i,
1208                 print '%-13s' % netmask(i, DEMAND_NONE).netmask_str(),
1209             else:
1210                 print ' ' * 16,
1211             print i + 16, '%-16s' % netmask(i + 16, DEMAND_NONE).netmask_str()
1212         print _("\n\nIP intervals\n============")
1213         for i in range(9):
1214             for j in range(0, 4):
1215                 print '%2d' % (8*j + i),
1216             print '%3d' % (netmask(i, DEMAND_NONE).intrep >> 24),
1217             x = 0
1218             need_break = 0
1219             if i < 8:
1220                 for j in range(0, 256, pow(2, 8-i)):
1221                     if need_break:
1222                         print
1223                         print ' ' * 15,
1224                         need_break = 0
1225                     print '%3d-%-3d' % (j, j + pow(2, 8-i)-1),
1226                     x = x + 1
1227                     if x % 8 == 0:
1228                         need_break = 1
1229             else:
1230                 print '0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13...',
1231             print
1232         sys.exit(0)
1233
1234     if len(sys.argv) != 3:
1235         sys.stderr.write(_("Usage: python ipaddr.py IP_ADDRESS NETMASK\n"))
1236         sys.exit(1)
1237     nw = network(sys.argv[1], sys.argv[2], DEMAND_NONE)
1238     print nw
1239     print "IP address:       ", nw.ip.ip_str()
1240     print "Netmask:          ", nw.mask.netmask_str(),
1241     print " (/" + nw.mask.netmask_bits_str + ")"
1242     print "Network address:  ", nw.network_str()
1243     print "Broadcast address:", nw.broadcast_str()