############################################################################
##
## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary
## All rights reserved.
##
## $Id: Conntrack.py,v 1.11.2.3 2001/09/19 19:22:17 bazsi Exp $
##
## Author  : SaSa
## Auditor : 
## Last audited version:
## Notes:
##
############################################################################

from Zorp import *
#from Zorp import ZorpCTrack, log
from Stream import Stream
from Session import StackedSession, MasterSession
#from Zorp import ServiceException, LimitException
from traceback import print_exc

CORE_DEBUG = "core.debug"

class AbstractTracker(ZorpCTrack):
	"""Encapsulates an abstract connection tracker.

	This class encapsulates a general connection tracker for datagram
	based protocols. A connection tracker creates sessions out of
	unconnected datagrams by associating them with the most possible
	means. This default CTracker class uses timeout, but derived ones
	can also use protocol specific values.

	For performance reasons the sessions are classified to two groups:
	real and silent sessions. When a real session is to be started a
	process similar to starting a stream oriented session is performed
	using access control checks, thread creation etc. Since this poses a
	substantial overhead, the term silent session is defined, which
	skips these checks and forwards the new session to one of the
	proxies started earlier.

	Silent sessions are assigned to existing sessions based on the
	"similarity" between the new and the old session. The basis of this
	similarity is controlled by the administrator with the boolean
	attributes 'source_ip', 'source_port', 'destination_ip' and
	'destination_port' and 'all_session'. The first four attributes
	control which properties of a new session is significant. For
	example if 'source_ip' and 'destination_ip' are TRUE, and
	'source_port' and 'destination_port' are FALSE, all sessions from
	the same IP destined to the same destination IP will be routed to
	the same real session regardless of the ports used.

	This behaviour is overridden by the 'all_session' attribute, which
	when set to TRUE makes the tracker create real sessions for each
	datagram session.

	Attributes
	
	  name		-- The name of the service.
	  
	  service	-- Service to be started for a session.

	  session	-- Virtual session. Use it because of compatibility with other parts of Zorp.

	  source_ip		-- Boolean attribute which controls whether 
				   the source IP of a datagram is
				   significant when a new session is started.

	  source_port 		-- Boolean attribute which controls whether 
				   the source port of a datagram is
				   significant when a new session is started.

	  destination_ip	-- Boolean attribute which controls whether 
				   the destination IP of a datagram is
				   significant when a new session is started.

	  destination_port 	-- Boolean attribute which controls whether 
				   the destination port of a datagram is
				   significant when a new session is started.

	  all_session		-- Boolean attribute which makes the tracker
				   create distinct sessions for each datagram
				   session.
	"""
	
	def __init__(self, name, service):
		"""Constructor to initialize an CTracker instance or a derivative.

		This constructor sets attributes based on arguments.
		
		Arguments

		  self  -- this instance
		  
		  name	-- name of the service

		"""
		self.service = service
		self.name = name
		try:
			ZorpCTrack.__init__(self)
		except:
			print_exc()

	def config(self):
		"""Function to initialize the default configuration for connection trackers.

		This function is called by the low level connection tracker
		to initialize configuration. It sets the default time-based
		timeout to 30 seconds. It can be overridden by descendant
		classes if needed.

		Arguments

		  self -- this instance
		"""
		self.timeout = 30

	def addClient(self, fd):
		"""Add a new socket to listen to.

		Adds a new socket to the pool of watched sockets.
		
		Arguments

		  self  -- this instance
		  
		  fd	-- socket listening to

		"""
		ZorpCTrack.addClient(self, fd)

	def startInstance(self):
		"""Function starting a new connection tracking instance.

		This function starts a new connection tracking instance.
		
		Arguments

		  self  -- this instance
		  
		"""
		ZorpCTrack.startInstance(self, self.name)

	def proxyAuth(self, session, saddr):
		"""Authenticate the session

		Authenticate the new session. It's only done if packet
		headers and the setup recommends this.
		
		Arguments

		  self  -- this instance

		  session	-- session to authenticate

		  saddr	-- Address of client trying to connect
		  
		"""
		try:

#			if self.transparent and session.client_local.port == self.local.port:
#				log(CORE_ERROR, 1, "%s: Transparent listener connected directly, local=%s, client_local=%s" % (session.session_id, self.local, session.client_local))
			if session.isClientPermitted() == Z_ACCEPT:
				log(CORE_DEBUG, 8, "%s: Connection accepted; client address='%s'" % (session.session_id, saddr))
				return Z_ACCEPT
			raise DACException
		except ZoneException, s:
			log(CORE_POLICY, 1, "%s: Zone not found; zone='%s'" % (session.session_id, s))
		except DACException:
			log(CORE_POLICY, 1, "%s: DAC policy violation;" % (session.session_id))
		except MACException:
			log(CORE_POLICY, 1, "%s: MAC policy violation;" % (session.session_id))
		except AuthException:
			log(CORE_POLICY, 1, "%s: Authentication failure;" % (session.session_id))
		except LimitException:
			log(CORE_POLICY, 1, "%s: Connection over permitted limits;" % (session.session_id))
		except:
			print_exc()

		if session != None:
			session.destroy()

		return Z_REJECT

	def startProxyInstance(self, fd, saddr, daddr):
		"""Start a new proxy Instance.

		Start a new, full weight proxy instance.
		
		Arguments

		  self  -- this instance

		  fd	-- socket with incoming packet

		  saddr	-- Address of client trying to connect

		  daddr	-- Address of server trying to connect
		  
		"""
		self.session = MasterSession()
		self.session.setService(self.service)
		self.session.setClient(fd, saddr)
		self.session.client_local = getSockName(fd);

		if self.proxyAuth(self.session, saddr) != Z_ACCEPT:
			return None
		return self.session.service.startInstance(self.session, 3)

	def connectServer(self, daddr):
		"""Connect server event.

		It's calculate the source and destination adress, where
		the Zorp will connect, if it's accepted.

		Arguments

		  daddr	-- Address of server trying to connect

		"""
		self.session.client_dest = daddr
		self.session.service.router.routeConnection(self.session)

		local = None
		remote = self.session.server_address

		if self.session.service.snat:
			local = self.session.service.snat.performTranslation(session, local)

		if self.session.service.dnat:
			remote = self.session.service.dnat.performTranslation(session, local)

		return (local, remote)
