~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/IPy.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
IPy - class and tools for handling of IPv4 and IPv6 addresses and networks.
 
3
See README file for learn how to use IPy.
 
4
 
 
5
Further Information might be available at:
 
6
http://software.inl.fr/trac/trac.cgi/wiki/IPy
 
7
"""
 
8
 
 
9
# $HeadURL: https://svn.inl.fr/inl-svn/src/tools/ipy/tags/IPy-0.70/IPy.py $
 
10
# $Id: IPy.py 19309 2009-10-29 10:21:13Z haypo $
 
11
 
 
12
__rcsid__ = '$Id: IPy.py 19309 2009-10-29 10:21:13Z haypo $'
 
13
__version__ = '0.70'
 
14
 
 
15
import types
 
16
 
 
17
# Definition of the Ranges for IPv4 IPs
 
18
# this should include www.iana.org/assignments/ipv4-address-space
 
19
# and www.iana.org/assignments/multicast-addresses
 
20
IPv4ranges = {
 
21
    '0':                'PUBLIC',   # fall back
 
22
    '00000000':         'PRIVATE',  # 0/8
 
23
    '00001010':         'PRIVATE',  # 10/8
 
24
    '01111111':         'PRIVATE',  # 127.0/8
 
25
    '1':                'PUBLIC',   # fall back
 
26
    '1010100111111110': 'PRIVATE',  # 169.254/16
 
27
    '101011000001':     'PRIVATE',  # 172.16/12
 
28
    '1100000010101000': 'PRIVATE',  # 192.168/16
 
29
    '11011111':         'RESERVED', # 223/8
 
30
    '111':              'RESERVED'  # 224/3
 
31
    }
 
32
 
 
33
# Definition of the Ranges for IPv6 IPs
 
34
# see also www.iana.org/assignments/ipv6-address-space,
 
35
# www.iana.org/assignments/ipv6-tla-assignments,
 
36
# www.iana.org/assignments/ipv6-multicast-addresses,
 
37
# www.iana.org/assignments/ipv6-anycast-addresses
 
38
IPv6ranges = {
 
39
    '00000000'              : 'RESERVED',       # ::/8
 
40
    '00000001'              : 'UNASSIGNED',     # 100::/8
 
41
    '0000001'               : 'NSAP',           # 200::/7
 
42
    '0000010'               : 'IPX',            # 400::/7
 
43
    '0000011'               : 'UNASSIGNED',     # 600::/7
 
44
    '00001'                 : 'UNASSIGNED',     # 800::/5
 
45
    '0001'                  : 'UNASSIGNED',     # 1000::/4
 
46
    '0010000000000000'      : 'RESERVED',       # 2000::/16 Reserved
 
47
    '0010000000000001'      : 'ASSIGNABLE',     # 2001::/16 Sub-TLA Assignments [RFC2450]
 
48
    '00100000000000010000000': 'ASSIGNABLE IANA',  # 2001:0000::/29 - 2001:01F8::/29 IANA
 
49
    '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC
 
50
    '00100000000000010000010': 'ASSIGNABLE ARIN',  # 2001:0400::/29 - 2001:05F8::/29 ARIN
 
51
    '00100000000000010000011': 'ASSIGNABLE RIPE',  # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC
 
52
    '0010000000000010'      : '6TO4',           # 2002::/16 "6to4" [RFC3056]
 
53
    '0011111111111110'      : '6BONE',          # 3FFE::/16 6bone Testing [RFC2471]
 
54
    '0011111111111111'      : 'RESERVED',       # 3FFF::/16 Reserved
 
55
    '010'                   : 'GLOBAL-UNICAST', # 4000::/3
 
56
    '011'                   : 'UNASSIGNED',     # 6000::/3
 
57
    '100'                   : 'GEO-UNICAST',    # 8000::/3
 
58
    '101'                   : 'UNASSIGNED',     # A000::/3
 
59
    '110'                   : 'UNASSIGNED',     # C000::/3
 
60
    '1110'                  : 'UNASSIGNED',     # E000::/4
 
61
    '11110'                 : 'UNASSIGNED',     # F000::/5
 
62
    '111110'                : 'UNASSIGNED',     # F800::/6
 
63
    '1111110'               : 'UNASSIGNED',     # FC00::/7
 
64
    '111111100'             : 'UNASSIGNED',     # FE00::/9
 
65
    '1111111010'            : 'LINKLOCAL',      # FE80::/10
 
66
    '1111111011'            : 'SITELOCAL',      # FEC0::/10
 
67
    '11111111'              : 'MULTICAST',      # FF00::/8
 
68
    '0' * 96                : 'IPV4COMP',       # ::/96
 
69
    '0' * 80 + '1' * 16     : 'IPV4MAP',        # ::FFFF:0:0/96
 
70
    '0' * 128               : 'UNSPECIFIED',    # ::/128
 
71
    '0' * 127 + '1'         : 'LOOPBACK'        # ::1/128
 
72
    }
 
73
 
 
74
 
 
75
class IPint:
 
76
    """Handling of IP addresses returning integers.
 
77
 
 
78
    Use class IP instead because some features are not implemented for
 
79
    IPint."""
 
80
 
 
81
    def __init__(self, data, ipversion=0, make_net=0):
 
82
        """Create an instance of an IP object.
 
83
 
 
84
        Data can be a network specification or a single IP. IP
 
85
        addresses can be specified in all forms understood by
 
86
        parseAddress(). The size of a network can be specified as
 
87
 
 
88
        /prefixlen        a.b.c.0/24               2001:658:22a:cafe::/64
 
89
        -lastIP           a.b.c.0-a.b.c.255        2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
 
90
        /decimal netmask  a.b.c.d/255.255.255.0    not supported for IPv6
 
91
 
 
92
        If no size specification is given a size of 1 address (/32 for
 
93
        IPv4 and /128 for IPv6) is assumed.
 
94
 
 
95
        If make_net is True, an IP address will be transformed into the network
 
96
        address by applying the specified netmask.
 
97
 
 
98
        >>> print IP('127.0.0.0/8')
 
99
        127.0.0.0/8
 
100
        >>> print IP('127.0.0.0/255.0.0.0')
 
101
        127.0.0.0/8
 
102
        >>> print IP('127.0.0.0-127.255.255.255')
 
103
        127.0.0.0/8
 
104
        >>> print IP('127.0.0.1/255.0.0.0', make_net=True)
 
105
        127.0.0.0/8
 
106
 
 
107
        See module documentation for more examples.
 
108
        """
 
109
 
 
110
        # Print no Prefixlen for /32 and /128
 
111
        self.NoPrefixForSingleIp = 1
 
112
 
 
113
        # Do we want prefix printed by default? see _printPrefix()
 
114
        self.WantPrefixLen = None
 
115
 
 
116
        netbits = 0
 
117
        prefixlen = -1
 
118
 
 
119
        # handling of non string values in constructor
 
120
        if type(data) == types.IntType or type(data) == types.LongType:
 
121
            self.ip = long(data)
 
122
            if ipversion == 0:
 
123
                if self.ip < 0x100000000L:
 
124
                    ipversion = 4
 
125
                else:
 
126
                    ipversion = 6
 
127
            if ipversion == 4:
 
128
                prefixlen = 32
 
129
            elif ipversion == 6:
 
130
                prefixlen = 128
 
131
            else:
 
132
                raise ValueError, "only IPv4 and IPv6 supported"
 
133
            self._ipversion = ipversion
 
134
            self._prefixlen = prefixlen
 
135
        # handle IP instance as an parameter
 
136
        elif isinstance(data, IPint):
 
137
            self._ipversion = data._ipversion
 
138
            self._prefixlen = data._prefixlen
 
139
            self.ip = data.ip
 
140
        else:
 
141
            # TODO: refactor me!
 
142
            # splitting of a string into IP and prefixlen et. al.
 
143
            x = data.split('-')
 
144
            if len(x) == 2:
 
145
                # a.b.c.0-a.b.c.255 specification ?
 
146
                (ip, last) = x
 
147
                (self.ip, parsedVersion) = parseAddress(ip)
 
148
                if parsedVersion != 4:
 
149
                    raise ValueError, "first-last notation only allowed for IPv4"
 
150
                (last, lastversion) = parseAddress(last)
 
151
                if lastversion != 4:
 
152
                    raise ValueError, "last address should be IPv4, too"
 
153
                if last < self.ip:
 
154
                    raise ValueError, "last address should be larger than first"
 
155
                size = last - self.ip
 
156
                netbits = _count1Bits(size)
 
157
                # make sure the broadcast is the same as the last ip
 
158
                # otherwise it will return /16 for something like:
 
159
                # 192.168.0.0-192.168.191.255
 
160
                if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last:
 
161
                    raise ValueError, \
 
162
                        "the range %s is not on a network boundary." % data
 
163
            elif len(x) == 1:
 
164
                x = data.split('/')
 
165
                # if no prefix is given use defaults
 
166
                if len(x) == 1:
 
167
                    ip = x[0]
 
168
                    prefixlen = -1
 
169
                elif len(x) > 2:
 
170
                    raise ValueError, "only one '/' allowed in IP Address"
 
171
                else:
 
172
                    (ip, prefixlen) = x
 
173
                    if prefixlen.find('.') != -1:
 
174
                        # check if the user might have used a netmask like
 
175
                        # a.b.c.d/255.255.255.0
 
176
                        (netmask, vers) = parseAddress(prefixlen)
 
177
                        if vers != 4:
 
178
                            raise ValueError, "netmask must be IPv4"
 
179
                        prefixlen = _netmaskToPrefixlen(netmask)
 
180
            elif len(x) > 2:
 
181
                raise ValueError, "only one '-' allowed in IP Address"
 
182
            else:
 
183
                raise ValueError, "can't parse"
 
184
 
 
185
            (self.ip, parsedVersion) = parseAddress(ip)
 
186
            if ipversion == 0:
 
187
                ipversion = parsedVersion
 
188
            if prefixlen == -1:
 
189
                if ipversion == 4:
 
190
                    prefixlen = 32 - netbits
 
191
                elif ipversion == 6:
 
192
                    prefixlen = 128 - netbits
 
193
                else:
 
194
                    raise ValueError, "only IPv4 and IPv6 supported"
 
195
            self._ipversion = ipversion
 
196
            self._prefixlen = int(prefixlen)
 
197
 
 
198
            if make_net:
 
199
                self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion)
 
200
 
 
201
            if not _checkNetaddrWorksWithPrefixlen(self.ip,
 
202
            self._prefixlen, self._ipversion):
 
203
                raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen)
 
204
 
 
205
    def int(self):
 
206
        """Return the first / base / network addess as an (long) integer.
 
207
 
 
208
        The same as IP[0].
 
209
 
 
210
        >>> "%X" % IP('10.0.0.0/8').int()
 
211
        'A000000'
 
212
        """
 
213
        return self.ip
 
214
 
 
215
    def version(self):
 
216
        """Return the IP version of this Object.
 
217
 
 
218
        >>> IP('10.0.0.0/8').version()
 
219
        4
 
220
        >>> IP('::1').version()
 
221
        6
 
222
        """
 
223
        return self._ipversion
 
224
 
 
225
    def prefixlen(self):
 
226
        """Returns Network Prefixlen.
 
227
 
 
228
        >>> IP('10.0.0.0/8').prefixlen()
 
229
        8
 
230
        """
 
231
        return self._prefixlen
 
232
 
 
233
    def net(self):
 
234
        """
 
235
        Return the base (first) address of a network as an (long) integer.
 
236
        """
 
237
        return self.int()
 
238
 
 
239
    def broadcast(self):
 
240
        """
 
241
        Return the broadcast (last) address of a network as an (long) integer.
 
242
 
 
243
        The same as IP[-1]."""
 
244
        return self.int() + self.len() - 1
 
245
 
 
246
    def _printPrefix(self, want):
 
247
        """Prints Prefixlen/Netmask.
 
248
 
 
249
        Not really. In fact it is our universal Netmask/Prefixlen printer.
 
250
        This is considered an internal function.
 
251
 
 
252
        want == 0 / None        don't return anything    1.2.3.0
 
253
        want == 1               /prefix                  1.2.3.0/24
 
254
        want == 2               /netmask                 1.2.3.0/255.255.255.0
 
255
        want == 3               -lastip                  1.2.3.0-1.2.3.255
 
256
        """
 
257
 
 
258
        if (self._ipversion == 4 and self._prefixlen == 32) or \
 
259
           (self._ipversion == 6 and self._prefixlen == 128):
 
260
            if self.NoPrefixForSingleIp:
 
261
                want = 0
 
262
        if want == None:
 
263
            want = self.WantPrefixLen
 
264
            if want == None:
 
265
                want = 1
 
266
        if want:
 
267
            if want == 2:
 
268
                # this should work with IP and IPint
 
269
                netmask = self.netmask()
 
270
                if type(netmask) != types.IntType \
 
271
                and type(netmask) != types.LongType:
 
272
                    netmask = netmask.int()
 
273
                return "/%s" % (intToIp(netmask, self._ipversion))
 
274
            elif want == 3:
 
275
                return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
 
276
            else:
 
277
                # default
 
278
                return "/%d" % (self._prefixlen)
 
279
        else:
 
280
            return ''
 
281
 
 
282
        # We have different flavours to convert to:
 
283
        # strFullsize   127.0.0.1    2001:0658:022a:cafe:0200:c0ff:fe8d:08fa
 
284
        # strNormal     127.0.0.1    2001:658:22a:cafe:200:c0ff:fe8d:08fa
 
285
        # strCompressed 127.0.0.1    2001:658:22a:cafe::1
 
286
        # strHex        0x7F000001L  0x20010658022ACAFE0200C0FFFE8D08FA
 
287
        # strDec        2130706433   42540616829182469433547974687817795834
 
288
 
 
289
    def strBin(self, wantprefixlen = None):
 
290
        """Return a string representation as a binary value.
 
291
 
 
292
        >>> print IP('127.0.0.1').strBin()
 
293
        01111111000000000000000000000001
 
294
        """
 
295
 
 
296
 
 
297
        if self._ipversion == 4:
 
298
            bits = 32
 
299
        elif self._ipversion == 6:
 
300
            bits = 128
 
301
        else:
 
302
            raise ValueError, "only IPv4 and IPv6 supported"
 
303
 
 
304
        if self.WantPrefixLen == None and wantprefixlen == None:
 
305
            wantprefixlen = 0
 
306
        ret = _intToBin(self.ip)
 
307
        return  '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
 
308
 
 
309
    def strCompressed(self, wantprefixlen = None):
 
310
        """Return a string representation in compressed format using '::' Notation.
 
311
 
 
312
        >>> IP('127.0.0.1').strCompressed()
 
313
        '127.0.0.1'
 
314
        >>> IP('2001:0658:022a:cafe:0200::1').strCompressed()
 
315
        '2001:658:22a:cafe:200::1'
 
316
        >>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed()
 
317
        'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127'
 
318
        """
 
319
 
 
320
        if self.WantPrefixLen == None and wantprefixlen == None:
 
321
            wantprefixlen = 1
 
322
 
 
323
        if self._ipversion == 4:
 
324
            return self.strFullsize(wantprefixlen)
 
325
        else:
 
326
            if self.ip >> 32 == 0xffff:
 
327
                ipv4 = intToIp(self.ip & 0xffffffff, 4)
 
328
                text = "::ffff:" + ipv4 + self._printPrefix(wantprefixlen)
 
329
                return text
 
330
            # find the longest sequence of '0'
 
331
            hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
 
332
            # every element of followingzeros will contain the number of zeros
 
333
            # following the corresponding element of hextets
 
334
            followingzeros = [0] * 8
 
335
            for i in range(len(hextets)):
 
336
                followingzeros[i] = _countFollowingZeros(hextets[i:])
 
337
            # compressionpos is the position where we can start removing zeros
 
338
            compressionpos = followingzeros.index(max(followingzeros))
 
339
            if max(followingzeros) > 1:
 
340
                # genererate string with the longest number of zeros cut out
 
341
                # now we need hextets as strings
 
342
                hextets = [x for x in self.strNormal(0).split(':')]
 
343
                while compressionpos < len(hextets) and hextets[compressionpos] == '0':
 
344
                    del(hextets[compressionpos])
 
345
                hextets.insert(compressionpos, '')
 
346
                if compressionpos + 1 >= len(hextets):
 
347
                    hextets.append('')
 
348
                if compressionpos == 0:
 
349
                    hextets = [''] + hextets
 
350
                return ':'.join(hextets) + self._printPrefix(wantprefixlen)
 
351
            else:
 
352
                return self.strNormal(0) + self._printPrefix(wantprefixlen)
 
353
 
 
354
    def strNormal(self, wantprefixlen = None):
 
355
        """Return a string representation in the usual format.
 
356
 
 
357
        >>> print IP('127.0.0.1').strNormal()
 
358
        127.0.0.1
 
359
        >>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
 
360
        2001:658:22a:cafe:200:0:0:1
 
361
        """
 
362
 
 
363
        if self.WantPrefixLen == None and wantprefixlen == None:
 
364
            wantprefixlen = 1
 
365
 
 
366
        if self._ipversion == 4:
 
367
            ret = self.strFullsize(0)
 
368
        elif self._ipversion == 6:
 
369
            ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
 
370
        else:
 
371
            raise ValueError, "only IPv4 and IPv6 supported"
 
372
 
 
373
 
 
374
 
 
375
        return ret + self._printPrefix(wantprefixlen)
 
376
 
 
377
    def strFullsize(self, wantprefixlen = None):
 
378
        """Return a string representation in the non-mangled format.
 
379
 
 
380
        >>> print IP('127.0.0.1').strFullsize()
 
381
        127.0.0.1
 
382
        >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
 
383
        2001:0658:022a:cafe:0200:0000:0000:0001
 
384
        """
 
385
 
 
386
        if self.WantPrefixLen == None and wantprefixlen == None:
 
387
            wantprefixlen = 1
 
388
 
 
389
        return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
 
390
 
 
391
    def strHex(self, wantprefixlen = None):
 
392
        """Return a string representation in hex format in lower case.
 
393
 
 
394
        >>> IP('127.0.0.1').strHex()
 
395
        '0x7f000001'
 
396
        >>> IP('2001:0658:022a:cafe:0200::1').strHex()
 
397
        '0x20010658022acafe0200000000000001'
 
398
        """
 
399
 
 
400
        if self.WantPrefixLen == None and wantprefixlen == None:
 
401
            wantprefixlen = 0
 
402
 
 
403
        x = hex(self.ip)
 
404
        if x[-1] == 'L':
 
405
            x = x[:-1]
 
406
        return x.lower() + self._printPrefix(wantprefixlen)
 
407
 
 
408
    def strDec(self, wantprefixlen = None):
 
409
        """Return a string representation in decimal format.
 
410
 
 
411
        >>> print IP('127.0.0.1').strDec()
 
412
        2130706433
 
413
        >>> print IP('2001:0658:022a:cafe:0200::1').strDec()
 
414
        42540616829182469433547762482097946625
 
415
        """
 
416
 
 
417
        if self.WantPrefixLen == None and wantprefixlen == None:
 
418
            wantprefixlen = 0
 
419
 
 
420
        x =  str(self.ip)
 
421
        if x[-1] == 'L':
 
422
            x = x[:-1]
 
423
        return x + self._printPrefix(wantprefixlen)
 
424
 
 
425
    def iptype(self):
 
426
        """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
 
427
 
 
428
        >>> print IP('127.0.0.1').iptype()
 
429
        PRIVATE
 
430
        >>> print IP('192.168.1.1').iptype()
 
431
        PRIVATE
 
432
        >>> print IP('195.185.1.2').iptype()
 
433
        PUBLIC
 
434
        >>> print IP('::1').iptype()
 
435
        LOOPBACK
 
436
        >>> print IP('2001:0658:022a:cafe:0200::1').iptype()
 
437
        ASSIGNABLE RIPE
 
438
 
 
439
        The type information for IPv6 is out of sync with reality.
 
440
        """
 
441
 
 
442
        # this could be greatly improved
 
443
 
 
444
        if self._ipversion == 4:
 
445
            iprange = IPv4ranges
 
446
        elif self._ipversion == 6:
 
447
            iprange = IPv6ranges
 
448
        else:
 
449
            raise ValueError, "only IPv4 and IPv6 supported"
 
450
 
 
451
        bits = self.strBin()
 
452
        for i in range(len(bits), 0, -1):
 
453
            if iprange.has_key(bits[:i]):
 
454
                return iprange[bits[:i]]
 
455
        return "unknown"
 
456
 
 
457
 
 
458
    def netmask(self):
 
459
        """Return netmask as an integer.
 
460
 
 
461
        >>> "%X" % IP('195.185.0.0/16').netmask().int()
 
462
        'FFFF0000'
 
463
        """
 
464
 
 
465
        # TODO: unify with prefixlenToNetmask?
 
466
        if self._ipversion == 4:
 
467
            locallen = 32 - self._prefixlen
 
468
        elif self._ipversion == 6:
 
469
            locallen = 128 - self._prefixlen
 
470
        else:
 
471
            raise ValueError, "only IPv4 and IPv6 supported"
 
472
 
 
473
        return ((2L ** self._prefixlen) - 1) << locallen
 
474
 
 
475
 
 
476
    def strNetmask(self):
 
477
        """Return netmask as an string. Mostly useful for IPv6.
 
478
 
 
479
        >>> print IP('195.185.0.0/16').strNetmask()
 
480
        255.255.0.0
 
481
        >>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
 
482
        /64
 
483
        """
 
484
 
 
485
        # TODO: unify with prefixlenToNetmask?
 
486
        if self._ipversion == 4:
 
487
            locallen = 32 - self._prefixlen
 
488
            return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
 
489
        elif self._ipversion == 6:
 
490
            locallen = 128 - self._prefixlen
 
491
            return "/%d" % self._prefixlen
 
492
        else:
 
493
            raise ValueError, "only IPv4 and IPv6 supported"
 
494
 
 
495
    def len(self):
 
496
        """Return the length of a subnet.
 
497
 
 
498
        >>> print IP('195.185.1.0/28').len()
 
499
        16
 
500
        >>> print IP('195.185.1.0/24').len()
 
501
        256
 
502
        """
 
503
 
 
504
        if self._ipversion == 4:
 
505
            locallen = 32 - self._prefixlen
 
506
        elif self._ipversion == 6:
 
507
            locallen = 128 - self._prefixlen
 
508
        else:
 
509
            raise ValueError, "only IPv4 and IPv6 supported"
 
510
 
 
511
        return 2L ** locallen
 
512
 
 
513
 
 
514
    def __nonzero__(self):
 
515
        """All IPy objects should evaluate to true in boolean context.
 
516
        Ordinarily they do, but if handling a default route expressed as
 
517
        0.0.0.0/0, the __len__() of the object becomes 0, which is used
 
518
        as the boolean value of the object.
 
519
        """
 
520
        return 1
 
521
 
 
522
 
 
523
    def __len__(self):
 
524
        """Return the length of a subnet.
 
525
 
 
526
        Called to implement the built-in function len().
 
527
        It breaks with IPv6 Networks. Anybody knows how to fix this."""
 
528
 
 
529
        # Python < 2.2 has this silly restriction which breaks IPv6
 
530
        # how about Python >= 2.2 ... ouch - it persists!
 
531
 
 
532
        return int(self.len())
 
533
 
 
534
 
 
535
    def __getitem__(self, key):
 
536
        """Called to implement evaluation of self[key].
 
537
 
 
538
        >>> ip=IP('127.0.0.0/30')
 
539
        >>> for x in ip:
 
540
        ...  print repr(x)
 
541
        ...
 
542
        IP('127.0.0.0')
 
543
        IP('127.0.0.1')
 
544
        IP('127.0.0.2')
 
545
        IP('127.0.0.3')
 
546
        >>> ip[2]
 
547
        IP('127.0.0.2')
 
548
        >>> ip[-1]
 
549
        IP('127.0.0.3')
 
550
        """
 
551
 
 
552
        if type(key) != types.IntType and type(key) != types.LongType:
 
553
            raise TypeError
 
554
        if key < 0:
 
555
            if abs(key) <= self.len():
 
556
                key = self.len() - abs(key)
 
557
            else:
 
558
                raise IndexError
 
559
        else:
 
560
            if key >= self.len():
 
561
                raise IndexError
 
562
 
 
563
        return self.ip + long(key)
 
564
 
 
565
 
 
566
 
 
567
    def __contains__(self, item):
 
568
        """Called to implement membership test operators.
 
569
 
 
570
        Should return true if item is in self, false otherwise. Item
 
571
        can be other IP-objects, strings or ints.
 
572
 
 
573
        >>> IP('195.185.1.1').strHex()
 
574
        '0xc3b90101'
 
575
        >>> 0xC3B90101L in IP('195.185.1.0/24')
 
576
        1
 
577
        >>> '127.0.0.1' in IP('127.0.0.0/24')
 
578
        1
 
579
        >>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
 
580
        0
 
581
        """
 
582
 
 
583
        item = IP(item)
 
584
        if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
 
585
            return 1
 
586
        else:
 
587
            return 0
 
588
 
 
589
 
 
590
    def overlaps(self, item):
 
591
        """Check if two IP address ranges overlap.
 
592
 
 
593
        Returns 0 if the two ranges don't overlap, 1 if the given
 
594
        range overlaps at the end and -1 if it does at the beginning.
 
595
 
 
596
        >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
 
597
        1
 
598
        >>> IP('192.168.0.0/23').overlaps('192.168.1.255')
 
599
        1
 
600
        >>> IP('192.168.0.0/23').overlaps('192.168.2.0')
 
601
        0
 
602
        >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
 
603
        -1
 
604
        """
 
605
 
 
606
        item = IP(item)
 
607
        if item.ip >= self.ip and item.ip < self.ip + self.len():
 
608
            return 1
 
609
        elif self.ip >= item.ip and self.ip < item.ip + item.len():
 
610
            return -1
 
611
        else:
 
612
            return 0
 
613
 
 
614
 
 
615
    def __str__(self):
 
616
        """Dispatch to the prefered String Representation.
 
617
 
 
618
        Used to implement str(IP)."""
 
619
 
 
620
        return self.strCompressed()
 
621
 
 
622
 
 
623
    def __repr__(self):
 
624
        """Print a representation of the Object.
 
625
 
 
626
        Used to implement repr(IP). Returns a string which evaluates
 
627
        to an identical Object (without the wantprefixlen stuff - see
 
628
        module docstring.
 
629
 
 
630
        >>> print repr(IP('10.0.0.0/24'))
 
631
        IP('10.0.0.0/24')
 
632
        """
 
633
 
 
634
        return("IPint('%s')" % (self.strCompressed(1)))
 
635
 
 
636
 
 
637
    def __cmp__(self, other):
 
638
        """Called by comparison operations.
 
639
 
 
640
        Should return a negative integer if self < other, zero if self
 
641
        == other, a positive integer if self > other.
 
642
 
 
643
        Networks with different prefixlen are considered non-equal.
 
644
        Networks with the same prefixlen and differing addresses are
 
645
        considered non equal but are compared by their base address
 
646
        integer value to aid sorting of IP objects.
 
647
 
 
648
        The version of Objects is not put into consideration.
 
649
 
 
650
        >>> IP('10.0.0.0/24') > IP('10.0.0.0')
 
651
        1
 
652
        >>> IP('10.0.0.0/24') < IP('10.0.0.0')
 
653
        0
 
654
        >>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
 
655
        1
 
656
        >>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
 
657
        0
 
658
 
 
659
        """
 
660
 
 
661
        # Im not really sure if this is "the right thing to do"
 
662
        if self._prefixlen < other.prefixlen():
 
663
            return (other.prefixlen() - self._prefixlen)
 
664
        elif self._prefixlen > other.prefixlen():
 
665
 
 
666
            # Fixed bySamuel Krempp <krempp@crans.ens-cachan.fr>:
 
667
 
 
668
            # The bug is quite obvious really (as 99% bugs are once
 
669
            # spotted, isn't it ? ;-) Because of precedence of
 
670
            # multiplication by -1 over the substraction, prefixlen
 
671
            # differences were causing the __cmp__ function to always
 
672
            # return positive numbers, thus the function was failing
 
673
            # the basic assumptions for a __cmp__ function.
 
674
 
 
675
            # Namely we could have (a > b AND b > a), when the
 
676
            # prefixlen of a and b are different.  (eg let
 
677
            # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything
 
678
            # could happen when launching a sort algorithm..
 
679
            # everything's in order with the trivial, attached patch.
 
680
 
 
681
            return (self._prefixlen - other.prefixlen()) * -1
 
682
        else:
 
683
            if self.ip < other.ip:
 
684
                return -1
 
685
            elif self.ip > other.ip:
 
686
                return 1
 
687
            elif self._ipversion != other._ipversion:
 
688
                # IP('0.0.0.0'), IP('::/0')
 
689
                return cmp(self._ipversion, other._ipversion)
 
690
            else:
 
691
                return 0
 
692
 
 
693
 
 
694
    def __hash__(self):
 
695
        """Called for the key object for dictionary operations, and by
 
696
        the built-in function hash(). Should return a 32-bit integer
 
697
        usable as a hash value for dictionary operations. The only
 
698
        required property is that objects which compare equal have the
 
699
        same hash value
 
700
 
 
701
        >>> IP('10.0.0.0/24').__hash__()
 
702
        -167772185
 
703
        """
 
704
 
 
705
        thehash = int(-1)
 
706
        ip = self.ip
 
707
        while ip > 0:
 
708
            thehash = thehash ^ (ip & 0x7fffffff)
 
709
            ip = ip >> 32
 
710
        thehash = thehash ^ self._prefixlen
 
711
        return int(thehash)
 
712
 
 
713
 
 
714
class IP(IPint):
 
715
    """Class for handling IP addresses and networks."""
 
716
 
 
717
    def net(self):
 
718
        """Return the base (first) address of a network as an IP object.
 
719
 
 
720
        The same as IP[0].
 
721
 
 
722
        >>> IP('10.0.0.0/8').net()
 
723
        IP('10.0.0.0')
 
724
        """
 
725
        return IP(IPint.net(self), ipversion=self._ipversion)
 
726
 
 
727
    def broadcast(self):
 
728
        """Return the broadcast (last) address of a network as an IP object.
 
729
 
 
730
        The same as IP[-1].
 
731
 
 
732
        >>> IP('10.0.0.0/8').broadcast()
 
733
        IP('10.255.255.255')
 
734
        """
 
735
        return IP(IPint.broadcast(self))
 
736
 
 
737
    def netmask(self):
 
738
        """Return netmask as an IP object.
 
739
 
 
740
        >>> IP('10.0.0.0/8').netmask()
 
741
        IP('255.0.0.0')
 
742
         """
 
743
        return IP(IPint.netmask(self))
 
744
 
 
745
 
 
746
    def reverseNames(self):
 
747
        """Return a list with values forming the reverse lookup.
 
748
 
 
749
        >>> IP('213.221.113.87/32').reverseNames()
 
750
        ['87.113.221.213.in-addr.arpa.']
 
751
        >>> IP('213.221.112.224/30').reverseNames()
 
752
        ['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.']
 
753
        >>> IP('127.0.0.0/24').reverseNames()
 
754
        ['0.0.127.in-addr.arpa.']
 
755
        >>> IP('127.0.0.0/23').reverseNames()
 
756
        ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
 
757
        >>> IP('127.0.0.0/16').reverseNames()
 
758
        ['0.127.in-addr.arpa.']
 
759
        >>> IP('127.0.0.0/15').reverseNames()
 
760
        ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
 
761
        >>> IP('128.0.0.0/8').reverseNames()
 
762
        ['128.in-addr.arpa.']
 
763
        >>> IP('128.0.0.0/7').reverseNames()
 
764
        ['128.in-addr.arpa.', '129.in-addr.arpa.']
 
765
        >>> IP('::1:2').reverseNames()
 
766
        ['2.0.0.0.1.ip6.arpa.']
 
767
        """
 
768
 
 
769
        if self._ipversion == 4:
 
770
            ret = []
 
771
            # TODO: Refactor. Add support for IPint objects
 
772
            if self.len() < 2**8:
 
773
                for x in self:
 
774
                    ret.append(x.reverseName())
 
775
            elif self.len() < 2**16L:
 
776
                for i in range(0, self.len(), 2**8):
 
777
                    ret.append(self[i].reverseName()[2:])
 
778
            elif self.len() < 2**24L:
 
779
                for i in range(0, self.len(), 2**16):
 
780
                    ret.append(self[i].reverseName()[4:])
 
781
            else:
 
782
                for i in range(0, self.len(), 2**24):
 
783
                    ret.append(self[i].reverseName()[6:])
 
784
            return ret
 
785
        elif self._ipversion == 6:
 
786
            s = hex(self.ip)[2:].lower()
 
787
            if s[-1] == 'l':
 
788
                s = s[:-1]
 
789
            if self._prefixlen % 4 != 0:
 
790
                raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
 
791
            s = list(s)
 
792
            s.reverse()
 
793
            s = '.'.join(s)
 
794
            first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
 
795
            return ["%s.ip6.arpa." % s[first_nibble_index:]]
 
796
        else:
 
797
            raise ValueError, "only IPv4 and IPv6 supported"
 
798
 
 
799
 
 
800
 
 
801
    def reverseName(self):
 
802
        """Return the value for reverse lookup/PTR records as RFC 2317 look alike.
 
803
 
 
804
        RFC 2317 is an ugly hack which only works for sub-/24 e.g. not
 
805
        for /23. Do not use it. Better set up a zone for every
 
806
        address. See reverseName for a way to achieve that.
 
807
 
 
808
        >>> print IP('195.185.1.1').reverseName()
 
809
        1.1.185.195.in-addr.arpa.
 
810
        >>> print IP('195.185.1.0/28').reverseName()
 
811
        0-15.1.185.195.in-addr.arpa.
 
812
        >>> IP('::1:2').reverseName()
 
813
        '2.0.0.0.1.ip6.arpa.'
 
814
        """
 
815
 
 
816
        if self._ipversion == 4:
 
817
            s = self.strFullsize(0)
 
818
            s = s.split('.')
 
819
            s.reverse()
 
820
            first_byte_index = int(4 - (self._prefixlen / 8))
 
821
            if self._prefixlen % 8 != 0:
 
822
                nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
 
823
                if nibblepart[-1] == 'l':
 
824
                    nibblepart = nibblepart[:-1]
 
825
                nibblepart += '.'
 
826
            else:
 
827
                nibblepart = ""
 
828
 
 
829
            s = '.'.join(s[first_byte_index:])
 
830
            return "%s%s.in-addr.arpa." % (nibblepart, s)
 
831
 
 
832
        elif self._ipversion == 6:
 
833
            s = hex(self.ip)[2:].lower()
 
834
            if s[-1] == 'l':
 
835
                s = s[:-1]
 
836
            if self._prefixlen % 4 != 0:
 
837
                nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
 
838
                if nibblepart[-1] == 'l':
 
839
                    nibblepart = nibblepart[:-1]
 
840
                nibblepart += '.'
 
841
            else:
 
842
                nibblepart = ""
 
843
            s = list(s)
 
844
            s.reverse()
 
845
            s = '.'.join(s)
 
846
            first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
 
847
            return "%s%s.ip6.arpa." % (nibblepart, s[first_nibble_index:])
 
848
        else:
 
849
            raise ValueError, "only IPv4 and IPv6 supported"
 
850
 
 
851
    def make_net(self, netmask):
 
852
        """Transform a single IP address into a network specification by
 
853
        applying the given netmask.
 
854
 
 
855
        Returns a new IP instance.
 
856
 
 
857
        >>> print IP('127.0.0.1').make_net('255.0.0.0')
 
858
        127.0.0.0/8
 
859
        """
 
860
        if '/' in str(netmask):
 
861
            raise ValueError, "invalid netmask (%s)" % netmask
 
862
        return IP('%s/%s' % (self, netmask), make_net=True)
 
863
 
 
864
    def __getitem__(self, key):
 
865
        """Called to implement evaluation of self[key].
 
866
 
 
867
        >>> ip=IP('127.0.0.0/30')
 
868
        >>> for x in ip:
 
869
        ...  print str(x)
 
870
        ...
 
871
        127.0.0.0
 
872
        127.0.0.1
 
873
        127.0.0.2
 
874
        127.0.0.3
 
875
        >>> print str(ip[2])
 
876
        127.0.0.2
 
877
        >>> print str(ip[-1])
 
878
        127.0.0.3
 
879
        """
 
880
        return IP(IPint.__getitem__(self, key))
 
881
 
 
882
    def __repr__(self):
 
883
        """Print a representation of the Object.
 
884
 
 
885
        >>> IP('10.0.0.0/8')
 
886
        IP('10.0.0.0/8')
 
887
        """
 
888
 
 
889
        return("IP('%s')" % (self.strCompressed(1)))
 
890
 
 
891
    def __add__(self, other):
 
892
        """Emulate numeric objects through network aggregation"""
 
893
        if self.prefixlen() != other.prefixlen():
 
894
            raise ValueError, "Only networks with the same prefixlen can be added."
 
895
        if self.prefixlen < 1:
 
896
            raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
 
897
        if self.version() != other.version():
 
898
            raise ValueError, "Only networks with the same IP version can be added."
 
899
        if self > other:
 
900
            # fixed by Skinny Puppy <skin_pup-IPy@happypoo.com>
 
901
            return other.__add__(self)
 
902
        else:
 
903
            ret = IP(self.int())
 
904
            ret._prefixlen = self.prefixlen() - 1
 
905
            return ret
 
906
 
 
907
 
 
908
def _parseAddressIPv6(ipstr):
 
909
    """
 
910
    Internal function used by parseAddress() to parse IPv6 address with ':'.
 
911
 
 
912
    >>> _parseAddressIPv6('::')
 
913
    0L
 
914
    >>> _parseAddressIPv6('::1')
 
915
    1L
 
916
    >>> _parseAddressIPv6('0:0:0:0:0:0:0:1')
 
917
    1L
 
918
    >>> _parseAddressIPv6('0:0:0::0:0:1')
 
919
    1L
 
920
    >>> _parseAddressIPv6('0:0:0:0:0:0:0:0')
 
921
    0L
 
922
    >>> _parseAddressIPv6('0:0:0::0:0:0')
 
923
    0L
 
924
 
 
925
    >>> _parseAddressIPv6('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210')
 
926
    338770000845734292534325025077361652240L
 
927
    >>> _parseAddressIPv6('1080:0000:0000:0000:0008:0800:200C:417A')
 
928
    21932261930451111902915077091070067066L
 
929
    >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A')
 
930
    21932261930451111902915077091070067066L
 
931
    >>> _parseAddressIPv6('1080:0::8:800:200C:417A')
 
932
    21932261930451111902915077091070067066L
 
933
    >>> _parseAddressIPv6('1080::8:800:200C:417A')
 
934
    21932261930451111902915077091070067066L
 
935
    >>> _parseAddressIPv6('FF01:0:0:0:0:0:0:43')
 
936
    338958331222012082418099330867817087043L
 
937
    >>> _parseAddressIPv6('FF01:0:0::0:0:43')
 
938
    338958331222012082418099330867817087043L
 
939
    >>> _parseAddressIPv6('FF01::43')
 
940
    338958331222012082418099330867817087043L
 
941
    >>> _parseAddressIPv6('0:0:0:0:0:0:13.1.68.3')
 
942
    218186755L
 
943
    >>> _parseAddressIPv6('::13.1.68.3')
 
944
    218186755L
 
945
    >>> _parseAddressIPv6('0:0:0:0:0:FFFF:129.144.52.38')
 
946
    281472855454758L
 
947
    >>> _parseAddressIPv6('::FFFF:129.144.52.38')
 
948
    281472855454758L
 
949
    >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A')
 
950
    21932261930451111902915077091070067066L
 
951
    >>> _parseAddressIPv6('1080::8:800:200C:417A')
 
952
    21932261930451111902915077091070067066L
 
953
    >>> _parseAddressIPv6('::1:2:3:4:5:6')
 
954
    1208962713947218704138246L
 
955
    >>> _parseAddressIPv6('1:2:3:4:5:6::')
 
956
    5192455318486707404433266432802816L
 
957
    """
 
958
 
 
959
    # Split string into a list, example:
 
960
    #   '1080:200C::417A' => ['1080', '200C', '417A'] and fill_pos=2
 
961
    # and fill_pos is the position of '::' in the list
 
962
    items = []
 
963
    index = 0
 
964
    fill_pos = None
 
965
    while index < len(ipstr):
 
966
        text = ipstr[index:]
 
967
        if text.startswith("::"):
 
968
            if fill_pos is not None:
 
969
                # Invalid IPv6, eg. '1::2::'
 
970
                raise ValueError("%r: Invalid IPv6 address: more than one '::'" % ipstr)
 
971
            fill_pos = len(items)
 
972
            index += 2
 
973
            continue
 
974
        pos = text.find(':')
 
975
        if pos == 0:
 
976
            # Invalid IPv6, eg. '1::2:'
 
977
            raise ValueError("%r: Invalid IPv6 address" % ipstr)
 
978
        if pos != -1:
 
979
            items.append(text[:pos])
 
980
            if text[pos:pos+2] == "::":
 
981
                index += pos
 
982
            else:
 
983
                index += pos+1
 
984
 
 
985
            if index == len(ipstr):
 
986
                # Invalid IPv6, eg. '1::2:'
 
987
                raise ValueError("%r: Invalid IPv6 address" % ipstr)
 
988
        else:
 
989
            items.append(text)
 
990
            break
 
991
 
 
992
    if items and '.' in items[-1]:
 
993
        # IPv6 ending with IPv4 like '::ffff:192.168.0.1'
 
994
        if not (fill_pos <= len(items)-1):
 
995
            # Invalid IPv6: 'ffff:192.168.0.1::'
 
996
            raise ValueError("%r: Invalid IPv6 address: '::' after IPv4" % ipstr)
 
997
        value = parseAddress(items[-1])[0]
 
998
        items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xffff)]
 
999
 
 
1000
    # Expand fill_pos to fill with '0'
 
1001
    # ['1','2'] with fill_pos=1 => ['1', '0', '0', '0', '0', '0', '0', '2']
 
1002
    if fill_pos is not None:
 
1003
        diff = 8 - len(items)
 
1004
        if diff <= 0:
 
1005
            raise ValueError("%r: Invalid IPv6 address: '::' is not needed" % ipstr)
 
1006
        items = items[:fill_pos] + ['0']*diff + items[fill_pos:]
 
1007
 
 
1008
    # Here we have a list of 8 strings
 
1009
    if len(items) != 8:
 
1010
        # Invalid IPv6, eg. '1:2:3'
 
1011
        raise ValueError("%r: Invalid IPv6 address: should have 8 hextets" % ipstr)
 
1012
 
 
1013
    # Convert strings to long integer
 
1014
    value = 0L
 
1015
    index = 0
 
1016
    for item in items:
 
1017
        try:
 
1018
            item = int(item, 16)
 
1019
            error = not(0 <= item <= 0xFFFF)
 
1020
        except ValueError:
 
1021
            error = True
 
1022
        if error:
 
1023
            raise ValueError("%r: Invalid IPv6 address: invalid hexlet %r" % (ipstr, item))
 
1024
        value = (value << 16) + item
 
1025
        index += 1
 
1026
    return value
 
1027
 
 
1028
def parseAddress(ipstr):
 
1029
    """
 
1030
    Parse a string and return the corresponding IP address (as integer)
 
1031
    and a guess of the IP version.
 
1032
 
 
1033
    Following address formats are recognized:
 
1034
 
 
1035
    >>> parseAddress('0x0123456789abcdef')           # IPv4 if <= 0xffffffff else IPv6
 
1036
    (81985529216486895L, 6)
 
1037
    >>> parseAddress('123.123.123.123')              # IPv4
 
1038
    (2071690107L, 4)
 
1039
    >>> parseAddress('123.123')                      # 0-padded IPv4
 
1040
    (2071658496L, 4)
 
1041
    >>> parseAddress('1080:0000:0000:0000:0008:0800:200C:417A')
 
1042
    (21932261930451111902915077091070067066L, 6)
 
1043
    >>> parseAddress('1080:0:0:0:8:800:200C:417A')
 
1044
    (21932261930451111902915077091070067066L, 6)
 
1045
    >>> parseAddress('1080:0::8:800:200C:417A')
 
1046
    (21932261930451111902915077091070067066L, 6)
 
1047
    >>> parseAddress('::1')
 
1048
    (1L, 6)
 
1049
    >>> parseAddress('::')
 
1050
    (0L, 6)
 
1051
    >>> parseAddress('0:0:0:0:0:FFFF:129.144.52.38')
 
1052
    (281472855454758L, 6)
 
1053
    >>> parseAddress('::13.1.68.3')
 
1054
    (218186755L, 6)
 
1055
    >>> parseAddress('::FFFF:129.144.52.38')
 
1056
    (281472855454758L, 6)
 
1057
    """
 
1058
 
 
1059
    if ipstr.startswith('0x'):
 
1060
        ret = long(ipstr[2:], 16)
 
1061
        if ret > 0xffffffffffffffffffffffffffffffffL:
 
1062
            raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
 
1063
        if ret < 0x100000000L:
 
1064
            return (ret, 4)
 
1065
        else:
 
1066
            return (ret, 6)
 
1067
 
 
1068
    if ipstr.find(':') != -1:
 
1069
        return (_parseAddressIPv6(ipstr), 6)
 
1070
 
 
1071
    elif len(ipstr) == 32:
 
1072
        # assume IPv6 in pure hexadecimal notation
 
1073
        return (long(ipstr, 16), 6)
 
1074
 
 
1075
    elif  ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
 
1076
        # assume IPv4  ('127' gets interpreted as '127.0.0.0')
 
1077
        bytes = ipstr.split('.')
 
1078
        if len(bytes) > 4:
 
1079
            raise ValueError, "IPv4 Address with more than 4 bytes"
 
1080
        bytes += ['0'] * (4 - len(bytes))
 
1081
        bytes = [long(x) for x in bytes]
 
1082
        for x in bytes:
 
1083
            if x > 255 or x < 0:
 
1084
                raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
 
1085
        return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
 
1086
 
 
1087
    else:
 
1088
        # we try to interprete it as a decimal digit -
 
1089
        # this ony works for numbers > 255 ... others
 
1090
        # will be interpreted as IPv4 first byte
 
1091
        ret = long(ipstr, 10)
 
1092
        if ret > 0xffffffffffffffffffffffffffffffffL:
 
1093
            raise ValueError, "IP Address can't be bigger than 2^128"
 
1094
        if ret <= 0xffffffffL:
 
1095
            return (ret, 4)
 
1096
        else:
 
1097
            return (ret, 6)
 
1098
 
 
1099
 
 
1100
def intToIp(ip, version):
 
1101
    """Transform an integer string into an IP address."""
 
1102
 
 
1103
    # just to be sure and hoping for Python 2.22
 
1104
    ip = long(ip)
 
1105
 
 
1106
    if ip < 0:
 
1107
        raise ValueError, "IPs can't be negative: %d" % (ip)
 
1108
 
 
1109
    ret = ''
 
1110
    if version == 4:
 
1111
        if ip > 0xffffffffL:
 
1112
            raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
 
1113
        for l in range(4):
 
1114
            ret = str(ip & 0xffL) + '.' + ret
 
1115
            ip = ip >> 8
 
1116
        ret = ret[:-1]
 
1117
    elif version == 6:
 
1118
        if ip > 0xffffffffffffffffffffffffffffffffL:
 
1119
            raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
 
1120
        l = '0' * 32 + hex(ip)[2:-1]
 
1121
        for x in range(1, 33):
 
1122
            ret = l[-x] + ret
 
1123
            if x % 4 == 0:
 
1124
                ret = ':' + ret
 
1125
        ret = ret[1:]
 
1126
    else:
 
1127
        raise ValueError, "only IPv4 and IPv6 supported"
 
1128
 
 
1129
    return ret
 
1130
 
 
1131
def _ipVersionToLen(version):
 
1132
    """Return number of bits in address for a certain IP version.
 
1133
 
 
1134
    >>> _ipVersionToLen(4)
 
1135
    32
 
1136
    >>> _ipVersionToLen(6)
 
1137
    128
 
1138
    >>> _ipVersionToLen(5)
 
1139
    Traceback (most recent call last):
 
1140
      File "<stdin>", line 1, in ?
 
1141
      File "IPy.py", line 1076, in _ipVersionToLen
 
1142
        raise ValueError, "only IPv4 and IPv6 supported"
 
1143
    ValueError: only IPv4 and IPv6 supported
 
1144
    """
 
1145
 
 
1146
    if version == 4:
 
1147
        return 32
 
1148
    elif version == 6:
 
1149
        return 128
 
1150
    else:
 
1151
        raise ValueError, "only IPv4 and IPv6 supported"
 
1152
 
 
1153
 
 
1154
def _countFollowingZeros(l):
 
1155
    """Return number of elements containing 0 at the beginning of the list."""
 
1156
    if len(l) == 0:
 
1157
        return 0
 
1158
    elif l[0] != 0:
 
1159
        return 0
 
1160
    else:
 
1161
        return 1 + _countFollowingZeros(l[1:])
 
1162
 
 
1163
 
 
1164
_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
 
1165
            '4': '0100', '5': '0101', '6': '0110', '7': '0111',
 
1166
            '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
 
1167
            'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
 
1168
 
 
1169
def _intToBin(val):
 
1170
    """Return the binary representation of an integer as string."""
 
1171
 
 
1172
    if val < 0:
 
1173
        raise ValueError, "Only positive values allowed"
 
1174
    s = hex(val).lower()
 
1175
    ret = ''
 
1176
    if s[-1] == 'l':
 
1177
        s = s[:-1]
 
1178
    for x in s[2:]:
 
1179
        if __debug__:
 
1180
            if not _BitTable.has_key(x):
 
1181
                raise AssertionError, "hex() returned strange result"
 
1182
        ret += _BitTable[x]
 
1183
    # remove leading zeros
 
1184
    while ret[0] == '0' and len(ret) > 1:
 
1185
        ret = ret[1:]
 
1186
    return ret
 
1187
 
 
1188
def _count1Bits(num):
 
1189
    """Find the highest bit set to 1 in an integer."""
 
1190
    ret = 0
 
1191
    while num > 0:
 
1192
        num = num >> 1
 
1193
        ret += 1
 
1194
    return ret
 
1195
 
 
1196
def _count0Bits(num):
 
1197
    """Find the highest bit set to 0 in an integer."""
 
1198
 
 
1199
    # this could be so easy if _count1Bits(~long(num)) would work as excepted
 
1200
    num = long(num)
 
1201
    if num < 0:
 
1202
        raise ValueError, "Only positive Numbers please: %s" % (num)
 
1203
    ret = 0
 
1204
    while num > 0:
 
1205
        if num & 1 == 1:
 
1206
            break
 
1207
        num = num >> 1
 
1208
        ret += 1
 
1209
    return ret
 
1210
 
 
1211
 
 
1212
def _checkPrefix(ip, prefixlen, version):
 
1213
    """Check the validity of a prefix
 
1214
 
 
1215
    Checks if the variant part of a prefix only has 0s, and the length is
 
1216
    correct.
 
1217
 
 
1218
    >>> _checkPrefix(0x7f000000L, 24, 4)
 
1219
    1
 
1220
    >>> _checkPrefix(0x7f000001L, 24, 4)
 
1221
    0
 
1222
    >>> repr(_checkPrefix(0x7f000001L, -1, 4))
 
1223
    'None'
 
1224
    >>> repr(_checkPrefix(0x7f000001L, 33, 4))
 
1225
    'None'
 
1226
    """
 
1227
 
 
1228
    # TODO: unify this v4/v6/invalid code in a function
 
1229
    bits = _ipVersionToLen(version)
 
1230
 
 
1231
    if prefixlen < 0 or prefixlen > bits:
 
1232
        return None
 
1233
 
 
1234
    if ip == 0:
 
1235
        zbits = bits + 1
 
1236
    else:
 
1237
        zbits = _count0Bits(ip)
 
1238
    if zbits <  bits - prefixlen:
 
1239
        return 0
 
1240
    else:
 
1241
        return 1
 
1242
 
 
1243
 
 
1244
def _checkNetmask(netmask, masklen):
 
1245
    """Checks if a netmask is expressable as a prefixlen."""
 
1246
 
 
1247
    num = long(netmask)
 
1248
    bits = masklen
 
1249
 
 
1250
    # remove zero bits at the end
 
1251
    while (num & 1) == 0 and bits != 0:
 
1252
        num = num >> 1
 
1253
        bits -= 1
 
1254
        if bits == 0:
 
1255
            break
 
1256
    # now check if the rest consists only of ones
 
1257
    while bits > 0:
 
1258
        if (num & 1) == 0:
 
1259
            raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
 
1260
        num = num >> 1
 
1261
        bits -= 1
 
1262
 
 
1263
 
 
1264
def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
 
1265
    """Check if a base addess of a network is compatible with a prefixlen"""
 
1266
    if net & _prefixlenToNetmask(prefixlen, version) == net:
 
1267
        return 1
 
1268
    else:
 
1269
        return 0
 
1270
 
 
1271
 
 
1272
def _netmaskToPrefixlen(netmask):
 
1273
    """Convert an Integer representing a netmask to a prefixlen.
 
1274
 
 
1275
    E.g. 0xffffff00 (255.255.255.0) returns 24
 
1276
    """
 
1277
 
 
1278
    netlen = _count0Bits(netmask)
 
1279
    masklen = _count1Bits(netmask)
 
1280
    _checkNetmask(netmask, masklen)
 
1281
    return masklen - netlen
 
1282
 
 
1283
 
 
1284
def _prefixlenToNetmask(prefixlen, version):
 
1285
    """Return a mask of n bits as a long integer.
 
1286
 
 
1287
    From 'IP address conversion functions with the builtin socket module'
 
1288
    by Alex Martelli
 
1289
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
 
1290
    """
 
1291
    if prefixlen == 0:
 
1292
        return 0
 
1293
    elif prefixlen < 0:
 
1294
        raise ValueError, "Prefixlen must be > 0"
 
1295
    return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
 
1296
 
 
1297
 
 
1298
if __name__ == "__main__":
 
1299
    import doctest
 
1300
    failure, nbtest = doctest.testmod()
 
1301
    if failure:
 
1302
        import sys
 
1303
        sys.exit(1)
 
1304