package org.torproject.jtor.socks.impl; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import org.torproject.jtor.circuits.Stream; import org.torproject.jtor.logging.Logger; public class SocksStreamConnection { public static void runConnection(Socket socket, Stream stream, Logger logger) { SocksStreamConnection ssc = new SocksStreamConnection(socket, stream, logger); ssc.run(); } private final static int TRANSFER_BUFFER_SIZE = 4096; private final Stream stream; private final InputStream torInputStream; private final OutputStream torOutputStream; private final Socket socket; private final Logger logger; private final Thread incomingThread; private final Thread outgoingThread; private final Object lock = new Object(); private volatile boolean outgoingClosed; private volatile boolean incomingClosed; private SocksStreamConnection(Socket socket, Stream stream, Logger logger) { this.socket = socket; this.stream = stream; torInputStream = stream.getInputStream(); torOutputStream = stream.getOutputStream(); this.logger = logger; incomingThread = createIncomingThread(); outgoingThread = createOutgoingThread(); } private void run() { incomingThread.start(); outgoingThread.start(); synchronized(lock) { while(!(outgoingClosed && incomingClosed)) { try { lock.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } closeStream(torInputStream); closeStream(torOutputStream); } } private Thread createIncomingThread() { return new Thread(new Runnable() { public void run() { try { incomingTransferLoop(); } catch (IOException e) { logger.debug("System error on incoming stream IO "+ stream +" : "+ e.getMessage()); } finally { synchronized(lock) { incomingClosed = true; lock.notifyAll(); } } }}); } private Thread createOutgoingThread() { return new Thread(new Runnable() { public void run() { try { outgoingTransferLoop(); } catch (IOException e) { logger.debug("System error on outgoing stream IO "+ stream +" : "+ e.getMessage()); } finally { synchronized(lock) { outgoingClosed = true; lock.notifyAll(); } } }}); } private void incomingTransferLoop() throws IOException { final byte[] incomingBuffer = new byte[TRANSFER_BUFFER_SIZE]; while(true) { final int n = torInputStream.read(incomingBuffer); if(n == -1) { logger.debug("EOF on TOR input stream "+ stream); socket.shutdownOutput(); return; } else if(n > 0) { logger.debug("Transferring "+ n +" bytes from "+ stream +" to SOCKS socket"); if(!socket.isOutputShutdown()) { socket.getOutputStream().write(incomingBuffer, 0, n); socket.getOutputStream().flush(); } else { closeStream(torInputStream); return; } } } } private void outgoingTransferLoop() throws IOException { final byte[] outgoingBuffer = new byte[TRANSFER_BUFFER_SIZE]; while(true) { stream.waitForSendWindow(); final int n = socket.getInputStream().read(outgoingBuffer); if(n == -1) { logger.debug("EOF on SOCKS socket connected to "+ stream); return; } else if(n > 0) { logger.debug("Transferring "+ n +" bytes from SOCKS socket to "+ stream); torOutputStream.write(outgoingBuffer, 0, n); torOutputStream.flush(); } } } private void closeStream(Closeable c) { try { c.close(); } catch (IOException e) { logger.warning("Close failed on "+ c + " : "+ e.getMessage()); } } }