/* this belongs to the WebRaveler package */ package webraveler.proxy; import java.io.*; import java.net.*; import java.util.*; import pygmy.core.SocketConnection; import pygmy.core.ThreadPool; import webraveler.http.HTTPRequest; import webraveler.http.HTTPResponse; import webraveler.ravel.Raveler; import webraveler.ravel.RavelerUser; import webraveler.util.MD5Helper; /** * Provides the proxy server architecture for Web Raveler * * @author The Web Raveler Development Squad * @version 1.0 */ public class ProxyServer implements Runnable { /* variables */ private boolean DEBUG = false; private HTTPResponse response; private Socket client; private int count; private boolean isAuthenticated; private Raveler ravel; private InputStream in; private OutputStream out; private String username; private int port; /* constructors */ public ProxyServer(int proxyPort,Raveler ravel ) { this.port = proxyPort; this.ravel = ravel; } /* methods */ /** * Process the incoming connection on the given socket. * * @param sock */ private void processRequest(Socket sock) { SocketConnection con; InputStream in = null; OutputStream out = null; HTTPRequest request; try { in = sock.getInputStream(); out = sock.getOutputStream(); request = new HTTPRequest(in); RavelerUser user = verifyLogin(request); response = ravel.handleRequest(user,request); response.write(out); } catch( Exception e ) { sendAuthenticationChallenge(out,0); }//catch try { sock.close(); } catch ( Exception e ) { } }//void processRequest(Socket) /** * Checks the login information, throwing an * exception when something goes wrong, and a * LoginDataException when there's a problem * specifically with the login information. * * @param request * @throws Exception */ private RavelerUser verifyLogin(HTTPRequest request) throws Exception { String hashedPass; RavelerUser ravelUser; String credentials = request.getHeader("proxy-authorization"); if ( credentials == null ) throw new Exception("ProxyServer: request had no login info"); credentials = credentials.trim().substring(6).trim(); String decodedString = webraveler.rabbit.Coder.uudecode(credentials); int colonIndex = decodedString.indexOf(":"); String userName = decodedString.substring(0,colonIndex); // compute the MD5 digest of the decoded password int colon = decodedString.indexOf(":"); String proposedUsername = decodedString.substring(0,colon); String proposedPassword = decodedString.substring(colon+1); hashedPass = MD5Helper.md5(proposedPassword); if (!ravel.checkCredentials(proposedUsername,hashedPass)) throw new Exception("ProxyServer: Bad credentials."); ravelUser = ravel.getUser(proposedUsername); username = ravelUser.username; if ( ravelUser.isLoggedIn ) { return ravelUser; } else { ravelUser.realmCount++; ravelUser.realmCount = ravelUser.realmCount % 4096; ravelUser.isLoggedIn = true; sendAuthenticationChallenge(out,ravelUser.realmCount); throw new Exception("ProxyServer: Couldn't verify authentication"); }//else }//void verifyAuthentication(HTTPRequest) /** * Send a proxy-authenticate challenge to the client waiting on the given output stream. * * @param out an outputstream for the client connection to the proxy server * @throws an IOException when we can't do the i/o with the output stream */ private void sendAuthenticationChallenge(OutputStream out, int realmCount) { Hashtable authResponseHash; HTTPResponse authResponse; authResponseHash = new Hashtable(); authResponseHash.put("Connection","close"); if ( DEBUG ) System.out.println("Proxy Server: realm = " + realmCount); authResponseHash.put("proxy-authenticate","Basic realm=\"Web Raveler"+realmCount+"\""); authResponse = new HTTPResponse("HTTP/1.0", 407, "Proxy Authorization Required", new byte[0], authResponseHash); authResponse.write(out); }//sendAuthChallenge /** * Wait for incoming client connections on the specified port, and spawn * a ProxyServer thread for each connection. */ public void run() { ServerSocket proxyServerSocket; Socket proxySocket; try{ proxyServerSocket = new ServerSocket(port); System.out.println("ProxyServer: Proxy server bound at port " + port); int counter = 1; ThreadPool pool = new ThreadPool(25); try{ while(true) { proxySocket = proxyServerSocket.accept(); proxySocket.setSoTimeout(60*1000); /* timeout in 60 seconds */ if ( DEBUG ) System.out.println("ProxyServer: Servicing connection #"+(counter++)+" from " + proxySocket.getInetAddress()); final Socket socket = proxySocket; pool.execute(new Runnable() { public void run() { processRequest(socket); }//void run() }); } } catch(IOException ioe) { ioe.printStackTrace(); } }catch(IOException ioe) { System.err.println("ProxyServer: Couldn't bind proxy server to port "+port); ioe.printStackTrace(); System.exit(1); } }// run() }//class ProxyServer