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

"""Module defining the chainer interface for chaining up to parent proxies.

This module defines the 'AbstractChainer' interface, whose implementations
are responsible to connect a child proxy to its parent.
"""

from Zorp import *
from Connector import Connect
from Stream import Stream

class AbstractChainer:
	"""Abstract class encapsulating the chaining process.
	
	This abstract class encapsulates the process of chaining to
	the parent proxy.
	"""
	def chainParent(self, session):
		"""Function to be called when a proxy wants to connect to its parent.
		
		This function is called to actually perform chaining to the parent.
		
		Arguments
		
		  self -- this instance
		  
		  session -- session we belong to
		"""
		raise NotImplementedError

class ConnectChainer:
	"""Class encapsulating a connection establishment.
	
	This class encapsulates a real TCP/IP connection establishment, and
	is used when a top-level proxy wants to perform chaining.
	
	When no explicit top-level chainer is specified for services, this
	one is used by default.	
	"""
	def establishConnection(self, session, local, remote):
		"""Function to actually establish a connection.
		
		Internal function to establish a connection with the given
		local and remote addresses. It is used by derived chainer
		classes after finding out the destination address to connect
		to. This function performs access control checks.
		   
		Arguments

		  self         -- this instance
		  
		  session      -- Session object
		  
		  local	       -- bind address
		  
		  remote       -- host to connect to
		  
		Returns
		
		  The stream of the connection to the server

		"""
		try:
			session.setServer(remote)
			if session.isServerPermitted() == Z_ACCEPT:
				#remote.options = session.client_address.options
				conn = Connect(local, remote)
				session.server_fd = conn.blockingConnect()
				session.server_local = conn.local
				if session.server_fd == -1:
					session.server_stream = None
				else:
					session.server_stream = Stream(session.server_fd, session.owner.session_id + ": server")
					
				log(CORE_SESSION, 3, "%s: Server connection established; server_fd='%d', server_addr='%s', server_zone='%s', server_local='%s'" % (session.session_id, session.server_fd, session.server_address, session.server_zone, session.server_local))
				return session.server_stream
			raise DACException
		except DACException:
			pass
		except MACException:
			pass
		return None

	def chainParent(self, session):
		"""Overridden function to perform connection establishment.
		
		This function is called by the actual Proxy implementation
		to actually connect to the server-endpoint of the session. 
		The destination address is 'session.server_address' (which
		is previously set by the Router used for the service, and
		optionally overridden by the Proxy). The local address to
		bind to is determined with the help of a NAT object if one
		is provided, or allocated dynamically by the kernel.
		
		Arguments
		
		  self -- this instance
		  
		  session -- session we belong to
		
		"""
		local = None
		remote = session.server_address
		if session.service.snat:
			local = session.service.snat.performTranslation(session, local)
		if session.service.dnat:
			remote = session.service.dnat.performTranslation(session, remote)
		return self.establishConnection(session, local, remote)
