# proxy.py
# 
# Copyright 2005,2007 Wichert Akkerman <wichert@wiggy.net>
#
# A RADIUS proxy as defined in RFC 2138

from pyrad.server import ServerPacketError
from pyrad.server import Server
from pyrad import packet
import select
import socket


class Proxy(Server):
	"""Base class for RADIUS proxies.

	This class extends tha RADIUS server class with the capability to
	handle communication with other RADIUS servers as well.

	@ivar _proxyfd: network socket used to communicate with other servers
	@type _proxyfd: socket class instance

	"""

	def _PrepareSockets(self):
		Server._PrepareSockets(self)

		self._proxyfd=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		self._fdmap[self._proxyfd.fileno()]=self._proxyfd
		self._poll.register(self._proxyfd.fileno(), (select.POLLIN|select.POLLPRI|select.POLLERR))


	def _HandleProxyPacket(self, pkt):
		"""Process a packet received on the reply socket.

		If this packet should be dropped instead of processed a
                ServerPacketError exception should be raised. The main loop
                will drop the packet and log the reason.

		@param pkt: packet to process
		@type  pkt: Packet class instance
		"""
		if not self.hosts.has_key(pkt.source[0]):
			raise ServerPacketError, "Received packet from unknown host"

		pkt.secret=self.hosts[pkt.source[0]].secret

		if not pkt.code in [ packet.AccessAccept, packet.AccessReject, packet.AccountingResponse ]:
			raise ServerPacketError, "Received non-response on proxy socket"



	def _ProcessInput(self, fd):
		"""Process available data.

		If this packet should be dropped instead of processed a
                ServerPacketError exception should be raised. The main loop
                will drop the packet and log the reason.

		This function calls either HandleAuthPacket(),
		HandleAcctPacket() or _HandleProxyPacket() depending on which
		socket is being processed.

		@param  fd: socket to read packet from
		@type   fd: socket class instance
		@param pkt: packet to process
		@type  pkt: Packet class instance
		"""
		if fd.fileno()==self._proxyfd.fileno():
			pkt=self._GrabPacket(lambda data, s=self: s.CreatePacket(packet=data), fd)
			self._HandleProxyPacket(pkt)
		else:
			Server._ProcessInput(self, fd)


