1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__ = """IpUtil
15
16 IPv4 utility functions
17
18 """
19
20 import types
21 import re
22 from Products.ZenUtils.Exceptions import ZentinelException
23 from twisted.names.client import lookupPointer
24
26
28 """
29 Attempted to parse an invalid IP range.
30 """
31
32
33
34 isip = re.compile("^\d+\.\d+\.\d+\.\d+$").search
36 """
37 Check that an IPv4 address is valid. Return true
38 or raise an exception(!)
39
40 >>> checkip('10.10.20.5')
41 True
42 >>> try: checkip(10)
43 ... except IpAddressError, ex: print ex
44 10 is not a dot delimited address
45 >>> try: checkip('10')
46 ... except IpAddressError, ex: print ex
47 10 is an invalid address
48 >>> try: checkip('10.10.20.500')
49 ... except IpAddressError, ex: print ex
50 10.10.20.500 is an invalid address
51 >>> checkip('10.10.20.00')
52 True
53 >>> checkip('10.10.20.0')
54 True
55 >>> checkip('10.10.20.255')
56 True
57 """
58 success = True
59 if ip == '':
60 success = False
61 else:
62 try:
63 octs = ip.split('.')
64 except:
65 raise IpAddressError( '%s is not a dot delimited address' % ip )
66 if len(octs) != 4:
67 success = False
68 else:
69 for o in octs:
70 try:
71 if not (0 <= int(o) <= 255):
72 success = False
73 except:
74 success = False
75 if not success:
76 raise IpAddressError( "%s is an invalid address" % ip )
77 return True
78
79
81 """
82 Convert a string IP to a decimal representation easier for
83 calculating netmasks etc.
84
85 Deprecated in favour of ipToDecimal()
86 """
87 return ipToDecimal(ip)
88
90 """
91 Convert a string IP to a decimal representation easier for
92 calculating netmasks etc.
93
94 >>> ipToDecimal('10.10.20.5')
95 168432645L
96 >>> try: ipToDecimal('10.10.20.500')
97 ... except IpAddressError, ex: print ex
98 10.10.20.500 is an invalid address
99 """
100 checkip(ip)
101 octs = ip.split('.')
102 octs.reverse()
103 i = 0L
104 for j in range(len(octs)):
105 i += (256l ** j) * int(octs[j])
106 return i
107
108
110 """
111 Get just the IP address from an CIDR string like 1.1.1.1/24
112
113 >>> ipFromIpMask('1.1.1.1')
114 '1.1.1.1'
115 >>> ipFromIpMask('1.1.1.1/24')
116 '1.1.1.1'
117 """
118 return ipmask.split("/")[0]
119
120
122 """
123 Convert a numeric IP address to a string
124
125 Deprecated in favour of decimalIpToStr()
126 """
127 return decimalIpToStr(ip)
128
130 """
131 Convert a decimal IP address (as returned by ipToDecimal)
132 to a regular IPv4 dotted quad address.
133
134 >>> decimalIpToStr(ipToDecimal('10.23.44.57'))
135 '10.23.44.57'
136 """
137 _masks = (
138 0x000000ffL,
139 0x0000ff00L,
140 0x00ff0000L,
141 0xff000000L,
142 )
143 o = []
144 for i in range(len(_masks)):
145 t = ip & _masks[i]
146 s = str(t >> (i*8))
147 o.append(s)
148 o.reverse()
149 return '.'.join(o)
150
151
153 """
154 Convert hex netbits (0xff000000) to decimal netmask (8)
155
156 >>> hexToBits("0xff000000")
157 8
158 >>> hexToBits("0xffffff00")
159 24
160 """
161 return maskToBits(hexToMask(hex))
162
163
165 """
166 Converts a netmask represented in hex to octets represented in
167 decimal.
168
169 >>> hexToMask("0xffffff00")
170 '255.255.255.0'
171 >>> hexToMask("0xffffffff")
172 '255.255.255.255'
173 >>> hexToMask("0x00000000")
174 '0.0.0.0'
175 >>> hexToMask("trash")
176 '255.255.255.255'
177 """
178 if hex.find('x') < 0:
179 return "255.255.255.255"
180
181 hex = list(hex.lower().split('x')[1])
182 octets = []
183 while len(hex) > 0:
184 snippit = list(hex.pop() + hex.pop())
185 snippit.reverse()
186 decimal = int(''.join(snippit), 16)
187 octets.append(str(decimal))
188
189 octets.reverse()
190 return '.'.join(octets)
191
192
194 """
195 Convert string rep of netmask to number of bits
196
197 >>> maskToBits('255.255.255.255')
198 32
199 >>> maskToBits('255.255.224.0')
200 19
201 >>> maskToBits('0.0.0.0')
202 0
203 """
204 if type(netmask) == types.StringType and netmask.find('.') > -1:
205 test = 0xffffffffL
206 if netmask[0]=='0': return 0
207 masknumb = ipToDecimal(netmask)
208 for i in range(32):
209 if test == masknumb: return 32-i
210 test = test - 2 ** i
211 return None
212 else:
213 return int(netmask)
214
215
217 """
218 Convert integer number of netbits to a decimal number
219
220 Deprecated in favour of bitsToDecimalMask()
221 """
222 return bitsToDecimalMask(netbits)
223
225 """
226 Convert integer number of netbits to a decimal number
227
228 >>> bitsToDecimalMask(32)
229 4294967295L
230 >>> bitsToDecimalMask(19)
231 4294959104L
232 >>> bitsToDecimalMask(0)
233 0L
234 """
235 masknumb = 0L
236 netbits=int(netbits)
237 for i in range(32-netbits, 32):
238 masknumb += 2L ** i
239 return masknumb
240
241
243 """
244 Convert netbits into a dotted-quad subnetmask
245
246 >>> bitsToMask(12)
247 '255.240.0.0'
248 >>> bitsToMask(0)
249 '0.0.0.0'
250 >>> bitsToMask(32)
251 '255.255.255.255'
252 """
253 return decimalIpToStr(bitsToDecimalMask(netbits))
254
255
261
263 """
264 Get network address of IP as string netmask as in the form 255.255.255.0
265
266 >>> getnet('10.12.25.33', 24)
267 168564992L
268 >>> getnet('10.12.25.33', '255.255.255.0')
269 168564992L
270 """
271 checkip(ip)
272 ip = ipToDecimal(ip)
273
274 try: netbits = int(netmask)
275 except ValueError: netbits = -1
276
277 if 0 < netbits <= 32:
278 netmask = bitsToDecimalMask(netbits)
279 else:
280 checkip(netmask)
281 netmask = ipToDecimal(netmask)
282 return ip & netmask
283
284
290
292 """
293 Return network number as string
294
295 >>> netFromIpAndNet('10.12.25.33', 24)
296 '10.12.25.0'
297 >>> netFromIpAndNet('250.12.25.33', 1)
298 '128.0.0.0'
299 >>> netFromIpAndNet('10.12.25.33', 16)
300 '10.12.0.0'
301 >>> netFromIpAndNet('10.12.25.33', 32)
302 '10.12.25.33'
303 """
304 return decimalIpToStr(getnet(ip, netmask))
305
307 """
308 Turn IP addreses into names using deferreds
309 """
310 if uselibcresolver:
311
312 from twisted.internet import threads
313 import socket
314 return threads.deferToThread(lambda : socket.gethostbyaddr(address)[0])
315 else:
316
317 address = '.'.join(address.split('.')[::-1]) + '.in-addr.arpa'
318 d = lookupPointer(address, [1,2,4])
319 def ip(result):
320 return str(result[0][0].payload.name)
321 d.addCallback(ip)
322 return d
323
325 """
326 Look up an IP based on the name passed in. We use gethostbyname to make
327 sure that we use /etc/hosts as mentioned above.
328
329 This hasn't been tested.
330 """
331 from twisted.internet import threads
332 import socket
333 return threads.deferToThread(lambda : socket.gethostbyname(name))
334
335
337 """
338 Turn a string specifying an IP range into a list of IPs.
339
340 @param iprange: The range string, in the format '10.0.0.a-b'
341 @type iprange: str
342
343 >>> parse_iprange('10.0.0.1-5')
344 ['10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5']
345 >>> parse_iprange('10.0.0.1')
346 ['10.0.0.1']
347 >>> try: parse_iprange('10.0.0.1-2-3')
348 ... except InvalidIPRangeError: print "Invalid"
349 Invalid
350
351 """
352
353 net, octet = iprange.rsplit('.', 1)
354 split = octet.split('-')
355 if len(split) > 2:
356 raise InvalidIPRangeError('%s is an invalid IP range.')
357 elif len(split)==1:
358 return [iprange]
359 else:
360 start, end = map(int, split)
361 return ['%s.%s' % (net, x) for x in xrange(start, end+1)]
362
363
365 """
366 Given a string representing the lower limit of a subnet, return decimal
367 representations of the first and last IP of that subnet.
368
369 0 is considered to define the beginning of a subnet, so x.x.x.0 represents
370 a /24, x.x.0.0 represents a /16, etc. An octet of 0 followed by a non-zero
371 octet, of course, is not considered to define a lower limit.
372
373 >>> map(decimalIpToStr, getSubnetBounds('10.1.1.0'))
374 ['10.1.1.0', '10.1.1.255']
375 >>> map(decimalIpToStr, getSubnetBounds('10.1.1.1'))
376 ['10.1.1.1', '10.1.1.1']
377 >>> map(decimalIpToStr, getSubnetBounds('10.0.1.0'))
378 ['10.0.1.0', '10.0.1.255']
379 >>> map(decimalIpToStr, getSubnetBounds('0.0.0.0'))
380 ['0.0.0.0', '255.255.255.255']
381 >>> map(decimalIpToStr, getSubnetBounds('10.0.0.0'))
382 ['10.0.0.0', '10.255.255.255']
383 >>> map(decimalIpToStr, getSubnetBounds('100.0.0.0'))
384 ['100.0.0.0', '100.255.255.255']
385
386 """
387 octets = ip.split('.')
388 otherend = []
389 while octets:
390 o = octets.pop()
391 if o=='0':
392 otherend.append('255')
393 else:
394 otherend.append(o)
395 break
396 otherend.reverse()
397 octets.extend(otherend)
398 upper = '.'.join(octets)
399 return numbip(ip), numbip(upper)
400
402 """
403 Given a partially formed IP address this will return a complete Ip address
404 with four octets with the invalid or missing entries replaced by 0
405
406 @param ip partially formed ip (will strip out alpha characters)
407 @return valid IP address field
408
409 >>> from Products.ZenUtils.IpUtil import ensureIp
410 >>> ensureIp('20')
411 '20.0.0.0'
412 >>> ensureIp('2000')
413 '0.0.0.0'
414 >>> ensureIp('10.175.X')
415 '10.175.0.0'
416 >>> ensureIp('10.0.1')
417 '10.0.1.0'
418 >>>
419 """
420
421 stripped = ''.join([c for c in ip if c in '1234567890.'])
422 octets = stripped.split('.')
423
424
425 while (len(octets) < 4):
426 octets.append('0')
427
428
429 for (idx, octet) in enumerate(octets):
430
431 try:
432 octet = int(octet)
433 except ValueError:
434 octet = 0
435
436
437 if not (0 < octet < 255):
438 octets[idx] = '0'
439
440 return '.'.join(octets)
441