package com.subgraph.orchid.socks; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; import com.subgraph.orchid.CircuitManager; import com.subgraph.orchid.SocksPortListener; import com.subgraph.orchid.TorConfig; import com.subgraph.orchid.TorException; public class SocksPortListenerImpl implements SocksPortListener { private final static Logger logger = Logger.getLogger(SocksPortListenerImpl.class.getName()); private final Set<Integer> listeningPorts = new HashSet<Integer>(); private final Map<Integer, AcceptTask> acceptThreads = new HashMap<Integer, AcceptTask>(); private final TorConfig config; private final CircuitManager circuitManager; private final ExecutorService executor; private boolean isStopped; public SocksPortListenerImpl(TorConfig config, CircuitManager circuitManager) { this.config = config; this.circuitManager = circuitManager; executor = Executors.newCachedThreadPool(); } public void addListeningPort(int port) { if(port <= 0 || port > 65535) { throw new TorException("Illegal listening port: "+ port); } synchronized(listeningPorts) { if(isStopped) { throw new IllegalStateException("Cannot add listening port because Socks proxy has been stopped"); } if(listeningPorts.contains(port)) return; listeningPorts.add(port); try { startListening(port); logger.fine("Listening for SOCKS connections on port "+ port); } catch (IOException e) { listeningPorts.remove(port); throw new TorException("Failed to listen on port "+ port +" : "+ e.getMessage()); } } } public void stop() { synchronized (listeningPorts) { for(AcceptTask t: acceptThreads.values()) { t.stop(); } executor.shutdownNow(); isStopped = true; } } private void startListening(int port) throws IOException { final AcceptTask task = new AcceptTask(port); acceptThreads.put(port, task); executor.execute(task); } private Runnable newClientSocket(final Socket s) { return new SocksClientTask(config, s, circuitManager); } private class AcceptTask implements Runnable { private final ServerSocket socket; private final int port; private volatile boolean stopped; AcceptTask(int port) throws IOException { this.socket = new ServerSocket(port); this.port = port; } void stop() { stopped = true; try { socket.close(); } catch (IOException e) { } } public void run() { try { runAcceptLoop(); } catch (IOException e) { if(!stopped) { logger.warning("System error accepting SOCKS socket connections: "+ e.getMessage()); } } finally { synchronized (listeningPorts) { listeningPorts.remove(port); acceptThreads.remove(port); } } } private void runAcceptLoop() throws IOException { while(!Thread.interrupted() && !stopped) { final Socket s = socket.accept(); executor.execute(newClientSocket(s)); } } } }