IP_addr.py - IP Address manipulation

'''
version 1.00
IP address object class
October 2014

author: Guillermo Gomez (a.k.a Momo)

'''

import socket
import struct

class IP_addr(object):
    
   '''
   Defines an object containing an IP address, mask, prefix length, network
   and a description of problems found in object manipulation.
      address              'x.x.x.x'
      prefix               prefix length (integer from 0 to 32)
                           In case, prefix info is preferred to mask info
      mask                 'm.m.m.m'
      network              'n.n.n.n'
      error_description    error description string
   '''
   def __init__(self, proposal=''):
      '''
      Tries to fill the object from an string in usual dot notation
      'x.x.x.x/nn' or 'x.x.x.x/m.m.m.m'
      Prefix or mask is optional
      '''
      if proposal <> '': # if there is one proposal analyze it
         (self.address, morp, self.mask) = proposal.partition('/')
         self.error_description = ''
         if self.mask == '': # no mask means it's a host
            self.prefix = 32
            self.mask = '255.255.255.255'
         if self.validate_address(): # IP must be valid
            if self.validate_mask(): 
               self.calculate_prefix()
            else:
               self.error_description = ''
               try: # take care, prefix lenght is an integer
                  self.prefix = int (self.mask)
                  if self.validate_prefix():
                     self.calculate_mask()
                  else:
                     self.mask = '255.255.255.255'
                     self.error_description = 'Invalid Prefix Length'
               except:             
                  self.prefix = 32
                  self.mask = '255.255.255.255'
                  self.error_description = 'Invalid Mask/Prefix'
            self.calculate_network()
         else:
            self.set_default()
            self.error_description = 'Invalid Address'
      else:
         self.set_default()

   def set_default(self):
      '''
      Set object to default values:
       address = '127.0.0.1'
       prefix  = 32
       mask    = '255.255.255.255'
       network = ''
       error_descrition = ''
      '''
      self.address = '127.0.0.1'
      self.prefix = 32
      self.mask = '255.255.255.255'
      self.network = ''
      self.error_description = ''
  
   def validate_address(self):
      '''
      Checks if the objects contains a valid IP Address
      returns a bool
      possible error 'Invalid Address'
      '''
      add_list = self.address.split ('.',3)
      if len (add_list) == 4:
         for i in range (0,4):
            if add_list[i].isdigit():
               if int(add_list[i]) < 0 or int(add_list[i]) > 255:
                  self.error_description = 'Invalid Address'
                  return False
            else:
               self.error_description = 'Invalid Address'
               return False
      else:
         self.error_description = 'Invalid Address'
         return False
      self.error = '' 
      return True

   def validate_mask(self):
      '''
      Checks if the objects contains a valid IP Mask
      returns bool
      possible error 'Invalid Mask'
      '''
      msk_list = self.mask.split ('.',3)
      valid = ('255', '254', '252', '248', '240', '224', '192', '128', '0')
      if len (msk_list) == 4:
         for i in range (0,4):
            if msk_list[i].isdigit():
               if msk_list[i] not in valid:
                  self.error_description = 'Invalid Mask'
                  return False
               elif msk_list[i] <> '255':
                  valid = ('0')
            else:
               self.error_description = 'Invalid Mask'
               return False
      else:
         self.error_description = 'Invalid Mask'
         return False
      self.error = ''
      return True

   def validate_prefix(self):
      '''
      Checks if the objects contains a valid IP Prefix
      returns bool
      possible error 'Invalid Prefix Length'
      '''
      if self.prefix >= 0 and self.prefix <=32:
         self.error = ''
         return True
      else:
         self.prefix = 32
         self.error_description = 'Invalid Prefix Length'
         return False

   def calculate_prefix(self):
      '''
      Calculates Prefix Length from Mask information
      On error resets mask and prefix
      '''
      if self.validate_mask():
         prfx_dict = {255:8, 254:7, 252:6, 248:5, 240:4, 224:3, 192:2, 128:1, 0:0}
         self.prefix = 0 
         msk_list = self.mask.split ('.',3)       
         for i in range (0,4): 
            self.prefix += prfx_dict[int(msk_list[i])]
      else:
         self.prefix = 32
         self.mask = '255.255.255.255'

   def calculate_mask(self):
      '''
      Calculates Mask from Prefix Length information
      On error resets mask and prefix
      '''
      valid_dict = {8:'255', 7:'254', 6:'252', 5:'248', 4:'240', 3:'224', 2:'192', 1:'128', 0:'0'}
      if self.validate_prefix():
         index = self.prefix
         if self.prefix < 9:
            index = self.prefix
            self.mask = '{}.0.0.0'.format(valid_dict[index])
         elif self.prefix < 17:
            index = self.prefix - 8
            self.mask = '255.{}.0.0'.format(valid_dict[index])
         elif self.prefix < 25:
            index = self.prefix - 16
            self.mask = '255.255.{}.0'.format(valid_dict[index])
         else:
            index = self.prefix - 24
            self.mask = '255.255.255.{}'.format(valid_dict[index])
      else:
         self.mask = '255.255.255.255'
         self.prefix = 32
         
   def calculate_network(self):
      '''
      Calculates Network address from IP Address and Prefix information
      If there is no prefix info it tries from Mask info.
      On error resets mask and prefix or even the whole object
      '''
      if self.validate_address(): # need a valid address
         if not self.validate_prefix(): # there is no prefix info
            self.calculate_prefix()
         address = socket.inet_aton(self.address)
         network = struct.unpack ('BBBB', address)
         net0, net1, net2, net3 = network
         if self.prefix < 9:
            net0 = net0 >> (8 - self.prefix) << (8 - self.prefix)
            net1 = net2 = net3 = 0
         elif self.prefix < 17:
            net1 = net1 >> (16 - self.prefix) << (16 - self.prefix)
            net2 = net3 = 0
         elif self.prefix < 25:
            net2 = net2 >> (24 - self.prefix) << (24 - self.prefix)
            net3 = 0
         else: # there is a valid prefix
            net3 = net3 >> (32 - self.prefix) << (32 - self.prefix)
         address = struct.pack ('BBBB', net0, net1, net2, net3)
         self.network = socket.inet_ntoa(address)
      else:
         self.set_default()
         self.error_description = 'Invalid Address'

No comments:

Post a Comment