// Java FTP Proxy Server // by Christian Schmidt, 1998 // http://www.student.dtu.dk/~c960657/ import java.net.*; import java.lang.*; import java.io.*; public class FtpProxy extends Thread { private Socket skControlClient, skControlServer; private BufferedReader brClient, brServer; private PrintWriter pwClient, pwServer; private ServerSocket ssDataClient, ssDataServer; private Socket skDataClient; private ControlConnect ccControl; private DataConnect dcData; private static int iSOTIMEOUT = 2000; private static int iDATASOTIMEOUT = 1000; private static int iDATABUFFERSIZE = 256; private String sLocalIP; private static String sServer = "Server> "; private static String sClient = "Client> "; private static String sProxy = "Proxy > "; private static String sSpace = " "; public FtpProxy (Socket skControlClient) { this.skControlClient = skControlClient; } public void run() { String sFromClient, sFromServer; ccControl = new ControlConnect(); try { brClient = new BufferedReader(new InputStreamReader(skControlClient.getInputStream())); pwClient = new PrintWriter(skControlClient.getOutputStream(), true); byte b[] = skControlClient.getLocalAddress().getAddress(); sLocalIP = (0xff & b[3]) + "," + (0xff & b[2]) + ',' + (0xff & b[1]) + ',' + (0xff & b[0]); sFromServer = "220 Java FTP Proxy Server (usage: USERID=user@site) ready."; pwClient.println(sFromServer); System.out.println(sProxy + sFromServer); sFromClient = brClient.readLine(); //should be "user@site" System.out.println(sClient + sFromClient); int i = sFromClient.indexOf('@'); if (i <= 5 || i + 4 >= sFromClient.length() || sFromClient.indexOf('.', i) == -1) { //simple validation af input sFromServer = "531 Incorrect usage - closing connection."; System.out.println(sProxy + sFromServer); pwClient.println(sFromServer); skControlClient.close(); return; } String sUser = sFromClient.substring(0,i); String sServerHost = sFromClient.substring(i+1); System.out.println("Connecting to " + sServerHost + " on port 21"); Socket skControlServer = new Socket(sServerHost, 21); brServer = new BufferedReader(new InputStreamReader(skControlServer.getInputStream())); pwServer = new PrintWriter(skControlServer.getOutputStream(), true); pwServer.println(sUser); //USER user System.out.println(sProxy + sUser); skControlClient.setSoTimeout(iSOTIMEOUT); skControlServer.setSoTimeout(iSOTIMEOUT); ccControl.start(); } catch (Exception e) { try { sFromServer = "421 Internal error, closing connection."; System.out.println(sProxy + sFromServer + e.toString()); pwClient.println(sFromServer); } catch (Exception ioe) {} ccControl.close(); } } public static void main (String args[]) { int port = 8089; try { if (args.length == 1) port = Integer.parseInt(args[0]); else if (args.length > 1) System.out.println("Usage: java FtpProxy [port]"); } catch (NumberFormatException e) {} try { ServerSocket ssControlClient = new ServerSocket(port); System.out.println("Listening on port " + port); while (true) { Socket skControlClient = ssControlClient.accept(); System.out.println("New connection"); new FtpProxy(skControlClient).start(); } } catch (IOException ioe) { System.out.println(ioe.toString()); } } public class ControlConnect extends Thread { private boolean bClosed = false; private boolean bIgnoreServer = true; private String readLine(BufferedReader br) throws IOException { while (!bClosed) try { return br.readLine(); } catch (InterruptedIOException iioe) {} throw new IOException(); } private synchronized void fromServer(String sFS) throws IOException { String sFromClient, sFromServer = sFS; if (!bIgnoreServer) pwClient.println(sFromServer); System.out.println(sServer + sFromServer); if (sFromServer.charAt(3) != '-') bIgnoreServer = false; } private synchronized void fromClient(String sFC) throws IOException { String sFromServer, sFromClient = sFC; if (sFromClient == null) { close(); return; } else if (sFromClient.toUpperCase().startsWith("PASS")) { pwServer.println(sFromClient); System.out.println(sClient + "PASS *****"); } else if (sFromClient.toUpperCase().startsWith("PASV")) { System.out.println(sClient + sFromClient); try {if (ssDataClient != null) ssDataClient.close();} catch (IOException ioe) {} try {if (skDataClient != null) skDataClient.close();} catch (IOException ioe) {} try {if (ssDataServer != null) ssDataServer.close();} catch (IOException ioe) {} if (dcData != null) dcData.close(); skDataClient = null; ssDataClient = new ServerSocket(0); int iPort = ssDataClient.getLocalPort(); sFromServer = "227 Entering Passive Mode (" + sLocalIP + "," + (int)(iPort/256) + "," + (iPort % 256) + ")"; pwClient.println(sFromServer); System.out.println(sProxy + sFromServer); ssDataServer = new ServerSocket(0); iPort = ssDataServer.getLocalPort(); sFromClient = "PORT " + sLocalIP + ',' + (int)(iPort/256) + ',' + (iPort % 256); pwServer.println(sFromClient); System.out.println(sProxy + sFromClient); bIgnoreServer = true; (dcData = new DataConnect(ssDataClient, ssDataServer)).start(); } else if (sFromClient.toUpperCase().startsWith("PORT")) { int iClientPort, i; try { i = sFromClient.lastIndexOf(','); iClientPort = Integer.parseInt(sFromClient.substring(i+1)); iClientPort += 256 * Integer.parseInt(sFromClient.substring(sFromClient.lastIndexOf(',', i-1)+1, i)); } catch (Exception e) {throw new IOException();} if (ssDataClient != null) try {ssDataClient.close();} catch (IOException ioe) {} if (skDataClient != null) try {skDataClient.close();} catch (IOException ioe) {} if (ssDataServer != null) try {ssDataServer.close();} catch (IOException ioe) {} if (dcData != null) dcData.close(); ssDataClient = null; try { skDataClient = new Socket(skControlClient.getInetAddress(), iClientPort); } catch (IOException ioe) { sFromServer = "500 PORT command failed - try using PASV instead."; pwClient.println(sFromServer); System.out.println(sProxy + sFromServer); System.out.println(ioe); return; } ssDataServer = new ServerSocket(0); int iPort = ssDataServer.getLocalPort(); sFromClient = "PORT " + sLocalIP + ',' + (int)(iPort/256) + ',' + (iPort % 256); pwServer.println(sFromClient); System.out.println(sProxy + sFromClient); (dcData = new DataConnect(skDataClient, ssDataServer)).start(); } else { pwServer.println(sFromClient); System.out.println(sClient + sFromClient); } } public void start() { super.start(); String sFromServer; while (!bClosed) try { if ((sFromServer = readLine(brServer)) == null) { break; } fromServer(sFromServer); } catch (IOException ioe) { break; } close(); } public void run() { String sFromClient; while (!bClosed) try { if ((sFromClient = readLine(brClient)) == null) { break; } fromClient(sFromClient); } catch (IOException ioe) { break; } close(); } public void close() { if (!bClosed) { bClosed = true; if (ssDataClient != null) try {ssDataClient.close();} catch (IOException ioe) {} if (ssDataServer != null) try {ssDataServer.close();} catch (IOException ioe) {} if (pwClient != null) pwClient.close(); if (pwServer != null) pwServer.close(); if (dcData != null) dcData.close(); } } } public class DataConnect extends Thread { private byte bRead[] = new byte[iDATABUFFERSIZE]; private ServerSocket ss1, ss2; private Socket sk1, sk2; boolean bClosed, bUsed, bFirst = true; public DataConnect (ServerSocket ss1, ServerSocket ss2) throws SocketException { this.ss1 = ss1; this.ss2 = ss2; } public DataConnect (Socket sk1, ServerSocket ss2) throws SocketException { this.sk1 = sk1; this.ss2 = ss2; } public void run() { BufferedInputStream bis=null; BufferedOutputStream bos=null; try { if (bFirst) { bFirst = false; if (ss1 != null) { sk1 = ss1.accept(); ss1.close(); } sk2 = ss2.accept(); ss2.close(); sk1.setSoTimeout(iDATASOTIMEOUT); sk2.setSoTimeout(iDATASOTIMEOUT); bis = new BufferedInputStream(sk1.getInputStream()); bos = new BufferedOutputStream(sk2.getOutputStream()); new Thread(this).start(); } else { bis = new BufferedInputStream(sk2.getInputStream()); bos = new BufferedOutputStream(sk1.getOutputStream()); } int i; boolean bUsedLocal = false ; while (!bClosed) try { while ((i = bis.read(bRead, 0, iDATABUFFERSIZE)) != -1) { bos.write(bRead, 0, i); bUsedLocal = true; bUsed = true; } break; } catch (InterruptedIOException iioe) { if (bUsed && !bUsedLocal) return; //use data connection for EITHER send or receive } bos.flush(); } catch (IOException ioe) { System.out.println(ioe); } try {bis.close();} catch (Exception e) {} try {bos.close();} catch (Exception e) {} close(); } public void close() { if (!bClosed) { bClosed = true; try {sk1.close();} catch (Exception e) {} try {sk2.close();} catch (Exception e) {} } } } }