############################################################################
##
## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary
## All rights reserved.
##
## $Id: Domain.py,v 1.7.2.3.2.1 2001/05/11 14:19:10 bazsi Exp $
##
############################################################################

"""Module implementing address domains.

This module implements the class AbstractDomain and some derived classes,
which encapsulate a set of physical addresses.

"""

from Zorp import *
from SockAddr import SockAddrInet, inet_ntoa, inet_aton, htonl, ntohl
from string import split, atoi

class AbstractDomain:
	"""Abstract base class for address domains.
	
	An address domain encapsulates an address type (AF_INET, AF_INET6
	etc) and provides functions to parse and compare these addresses. 
	This functionality is primarily used by Zone classes (see the module
	'Zone'), and the PacketFilter classes (see the module 'IPChains')
	
	Attributes
	
	  family  -- address family this domain uses
	"""
	family = AF_UNSPEC
	
	def __init__(self):
		"""Constructor initializing an AbstractDomain instance.
		
		This constructor is basically empty and does nothing.
		
		Arguments
		
		  self -- this instance
		"""
		pass

	def __cmp__(self, other):
		"""Function to compare two domains.
		
		This function is a placeholder and is to be overridden by
		derived classes. It should return -1, 0 and 1 for
		relations less-than, equal-to and greater-than.
		
		Arguments
		
		  self -- this instance
		  
		  other -- instance to compare to
		"""
		raise NotImplementedError

class InetDomain(AbstractDomain):
	"""Class derived from Domain representing IP address ranges.

	A class representing internet (IPv4) addresses, and IP segments
	represented in the form A.B.C.D/M, where A.B.C.D is the network
	address, and M specifies the number of ones in the netmask.
	
	The InetDomain objects are comparable, comparison means "contains",
	"equal to" and "contained by". It's used to organize Zones
	hierarchically.  The comparison can raise ValueError for
	incomparable ip addresses.
		
	Attributes
	
	  mask_bits -- number of bits in the netmask
	  
	  mask	    -- netmask in network byte order
	  
	  ip        -- network addresss in network byte order
		       
       	"""
	def __init__(self, addr):
		"""Constructor to initializes an InetDomain instance
		
		This constructor parses the argument 'addr' and fills
		instance attributes accordingly.
		
		Arguments
		
		  self -- this instance
		  
		  addr -- the string representation of an address, or 
		          address range
	       	"""
	        #FIXME: input validation!
	        self.family = AF_INET
		parts = split(addr,'/')
		try:
			self.mask_bits = atoi(parts[1])
			self.mask = htonl(((1 << self.mask_bits) - 1) << (32 - self.mask_bits))
		except IndexError:
			self.mask_bits = 32
			self.mask = 0xffffffff
		self.ip = inet_aton(parts[0]) & self.mask

	def __str__(self):
		"""Function returning the string representation of this instance.
		
                This function returns the string representation of this
                instance in the form address/mask.
                
                Arguments
                
                  self -- this instance
                        
		Returns
		
                  a string representing this object
                """
                return "%s/%u" % (inet_ntoa(self.ip), self.mask_bits)

	def __cmp__(self, other):
		"""Function to compare this instance to another.
		
		This function compares this instance to another InetDomain
		instance or to a SockAddrInet instance using set inclusion
		on addresses.  An address is less than another, if it's
		fully contained by other.
		   
		Arguments
		
		  self  -- this instance
		  
		  other -- the other InetDomain object to compare to
		  
		Returns
		
		  an integer representing the relation between self and other (-1, 0, or 1)
		"""
		if isinstance(other, InetDomain):
			if ((self.ip == other.ip) & (self.netmask() == other.netmask())):
				return 0
			if ((self.mask_bits >= other.mask_bits) & ((self.ip & other.netmask()) == other.ip)):
				return -1
			if ((other.mask_bits >= self.mask_bits) & ((other.ip & self.netmask()) == self.ip)):
				return 1
		else:
			try:
				if ((other.ip & self.netmask()) == self.ip):
					return 1
			except AttributeError:
			       	pass
		raise ValueError, '%s and %s are incomparable' % (self, other)
	
	def netaddr(self):
		"""Function calculating the network address of this address range.
		
		This function returns the network address of the address
		range represented by this instance.
		   
		Arguments
		
		  self -- this instance
		
		Returns
		
		  ip address in network byte order

		"""
		return self.ip
	
	def broadcast(self):
		"""Function calculating the broadcast address of this address range
		   
		This function returns the broadcast address of this domain
		calculated based on attributes.
		   
		Arguments
		
		  self -- this instance

		Returns
		
		  the broadcast address in network byte order
			
		"""
		if self.mask_bits == 0:
			return self.ip
		
		return htonl(ntohl(self.ip) | (0x7fffffff >> (self.mask_bits - 1)))
		
	def netmask(self):
		"""Function to calculate the netmask of this address range.
		
		This function calculates and returns the netmask of this
		address range as an integer in network byte order.
		   
		Arguments
		
		  self -- this instance
		
		Returns
		
		  the network mask as ip in network byte order
		"""
		return self.mask


#class Inet6Domain(AbstractDomain):
#	pass
