Package Products :: Package ZenUtils :: Module IpUtil |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.IpUtil

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, 2009 Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     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
     25 -class IpAddressError(ZentinelException): pass
     26
     27 -class InvalidIPRangeError(Exception):
     28      """
     29      Attempted to parse an invalid IP range.
     30      """
     31
     32
     33  # Match if this is an IPv4 address
     34  isip = re.compile("^\d+\.\d+\.\d+\.\d+$").search
     35 -def checkip(ip):
     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
     80 -def numbip(ip):
     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
     89 -def ipToDecimal(ip):
     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
    109 -def ipFromIpMask(ipmask):
    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
    121 -def strip(ip):
    122      """
    123      Convert a numeric IP address to a string
    124
    125      Deprecated in favour of decimalIpToStr()
    126      """
    127      return decimalIpToStr(ip)
    128
    129 -def decimalIpToStr(ip):
    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
    152 -def hexToBits(hex):
    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
    164 -def hexToMask(hex):
    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
    193 -def maskToBits(netmask):
    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
    216 -def bitsToMaskNumb(netbits):
    217      """
    218      Convert integer number of netbits to a decimal number
    219
    220      Deprecated in favour of bitsToDecimalMask()
    221      """
    222      return bitsToDecimalMask(netbits)
    223
    224 -def bitsToDecimalMask(netbits):
    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
    242 -def bitsToMask(netbits):
    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
    256 -def getnet(ip, netmask):
    257      """
    258      Deprecated in favour of decimalNetFromIpAndNet()
    259      """
    260      return decimalNetFromIpAndNet(ip, netmask)
    261
    262 -def decimalNetFromIpAndNet(ip, netmask):
    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
    285 -def getnetstr(ip, netmask):
    286      """
    287      Deprecated in favour of netFromIpAndNet()
    288      """
    289      return netFromIpAndNet(ip, netmask)
    290
    291 -def netFromIpAndNet(ip, netmask):
    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
    306 -def asyncNameLookup(address, uselibcresolver = True):
    307      """
    308      Turn IP addreses into names using deferreds
    309      """
    310      if uselibcresolver:
    311          # This is the most reliable way to do a lookup use it
    312          from twisted.internet import threads
    313          import socket
    314          return threads.deferToThread(lambda : socket.gethostbyaddr(address)[0])
    315      else:
    316          # There is a problem with this method because it will ignore /etc/hosts
    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
    324 -def asyncIpLookup(name):
    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
    336 -def parse_iprange(iprange):
    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      # Get the relevant octet
    353      net, octet = iprange.rsplit('.', 1)
    354      split = octet.split('-')
    355      if len(split) > 2: # Nothing we can do about this
    356          raise InvalidIPRangeError('%s is an invalid IP range.')
    357      elif len(split)==1: # A single IP was passed
    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
    364 -def getSubnetBounds(ip):
    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
    401 -def ensureIp(ip):
    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      # filter out the alpha characters
    421      stripped = ''.join([c for c in ip if c in '1234567890.'])
    422      octets = stripped.split('.')
    423
    424      # make sure we always have 4
    425      while (len(octets) < 4):
    426          octets.append('0')
    427
    428      # validate each octet
    429      for (idx, octet) in enumerate(octets):
    430          # cast it to an integer
    431          try:
    432              octet = int(octet)
    433          except ValueError:
    434              octet = 0
    435
    436          # make it 0 if not in the valid ip range
    437          if not (0 < octet < 255):
    438              octets[idx] = '0'
    439
    440      return '.'.join(octets)
    441