package com.theorem.radserver3.examples.server; import com.theorem.radserver3.*; import com.theorem.radserver3.radutil.*; import com.theorem.misc.*; import java.util.*; /** * Sends packets to a series of servers in an attempt to load balance * server usage or handle dead or busy servers. * This version of ProxyFailover attempts to reconnect to the same * server over Access-Challenge requests once one is found that * is accepting connections. */ public class ProxyFailover2 extends ProxyImpl { LogImpl dbg, svr; Configure conf; // Timer for creating a list of servers to try. Timer timer; // Circular list of servers to try. CircularList serverList = new CircularList(); // List of timer object. They will remove themselves as they expire. // This is required so a Timer won't disappear because of a lack of a reference. ArrayList timerList = new ArrayList(); /** * Constructor. * @param conf Configuration object. */ public ProxyFailover2(Configure conf) { super(); this.conf = conf; timer = new Timer(); // For the sake of testing add a fake server followed by a real one. // the fake name doesn't have a server - essentially it's down. // Note that the randomizer may scramble this order. addServer("andromedaNONE"); addServer("andromeda"); } /** * Intialize the logs for this class. * * @param svrLog Server log object. * @param dbgLog Debug log object. */ public void logs(LogImpl svrLog, LogImpl dbgLog) { dbg = dbgLog; svr = svrLog; } /** * Method to add a server to the circular list. * @param server Server realm. * The realm must be in our list of proxy target servers. */ public void addServer(String server) { serverList.add(server); } /** * Send a proxy request to a number of servers in the hopes one will respond. * * @param prx ProxyInfo object from server. */ public void changeRequest(ProxyInfo prx) throws AccessDropException, AccessRejectException { // If this is an Access-Request and has a State attribute that we can read // recover the old state information and direct the packet to the answering server. if (prx.getRequestType() == PacketType.Access_Request) { Object stateObject = prx.getStateObject(ProxyInfo.AUTH_STATE_OBJECT); if (stateObject != null) { // The state object is expected to be a local data structure. if (stateObject instanceof StateRealm) { AttributeList inList = prx.getRequestAttributeList(); // Get the realm. StateRealm sr = (StateRealm) stateObject; // Remove the present State attribute. inList.delete(Attribute.State); // Restore the original State information. inList.mergeAttributes(sr.capturedState); // Send the packet immediately to it's target realm. ProxyClient pc = null; try { pc = prx.getProxyClient(false, RADIUSClient.Access_Request, inList, null); pc.send(sr.realm); } catch (RADIUSServerException rse) { String msg = "Unable to create ProxyClient object - " + rse.getMessage(); RADIUSServer server = prx.getRADIUSServer(); server.log(msg, server.getServerLogLevel()); } throw new AccessDropException("Packet Redirected to " + sr.realm); } } // Fall through. } timer = new Timer(); timerList.add(timer); try { // Try sending a packet from within this method. // Set up the proxyClient with no particular associated object. ProxyClient pc = prx.getProxyClient(false, RADIUSClient.Access_Request, prx.getRequestAttributeList(), null); // Create the packet timeout object that will handle all the attempts // to send the packet. ProxyPacketTimeout t = new ProxyPacketTimeout(timer, pc, timerList); // Add realms from the circular list - start with a random entry. Random r = new Random(); int count = serverList.size(); int skip = r.nextInt() % count; for (int i = 0; i < count; i++) serverList.next(); for (int i = 0; i < count; i++) { String s = (String) serverList.next(); t.addRealm(s); } // Set the time between attempts to some number of seconds. t.setInterval(2); // Set the timer. t.go(); } catch (RADIUSServerException rse) { String msg = "Unable to create ProxyClient object - " + rse.getMessage(); RADIUSServer server = prx.getRADIUSServer(); server.log(msg, server.getServerLogLevel()); } // Unless we make this more sophisticated drop the original packet so we don't // send it to one of the proxy servers twice. throw new AccessDropException("Dropping original packet"); } /** * Change proxy response routing. * Don't actually change a proxies response attributes in this case. * * @param prx ProxyInfo object from server. */ public void changeResponse(ProxyInfo prx) throws AccessDropException, AccessRejectException { // Once a packet is successfully recovered cancel the timer. ProxyPacketTimeout ppt = (ProxyPacketTimeout) prx.getProxyStateObject(); if (ppt != null) { ppt.cancel(); // If this is an Access-Challenge packet find it's realm. if (prx.getRequestType() == PacketType.Access_Challenge) { int i; NAS realmNAS = prx.getNAS(); // Get the list of ProxyTargets. RADIUSServer server = prx.getRADIUSServer(); ProxyTarget ptList[] = server.getProxyTargetList(); StateRealm sr = null; for (i = 0; i < ptList.length; i++) { if (ptList[i].targetAddr.equals(realmNAS.NASAddress)) { // Capture all State information and save it. AttributeList outList = prx.getRequestAttributeList(); AttributeList stateList = outList.getAttributeList(Attribute.State); // Remove the original State attributes. outList.delete(Attribute.State); // Create our return information. sr = new StateRealm(ptList[i].targetName, stateList); // Add our own State information. prx.setStateObject(ProxyInfo.AUTH_STATE_OBJECT, sr); prx.setResponseAttributes(outList); break; } } if (i == ptList.length) throw new AccessDropException("ProxyFailover2: Can't find nas for proxy target " + realmNAS); } } } /** * Inner class to hold the data structure of the old state and the name of the * answering proxy target. */ class StateRealm { String realm; AttributeList capturedState; StateRealm(String realm, AttributeList stateList) { this.realm = realm; this.capturedState = stateList; } public String toString() { return "StateRealm: realm: " + realm + " State:\n" + capturedState; } } }