package org.torproject.jtor.circuits.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.torproject.jtor.circuits.Circuit;
import org.torproject.jtor.circuits.CircuitManager;
import org.torproject.jtor.circuits.OpenStreamResponse;
import org.torproject.jtor.crypto.TorRandom;
import org.torproject.jtor.data.IPv4Address;
import org.torproject.jtor.directory.Directory;
import org.torproject.jtor.logging.LogManager;
import org.torproject.jtor.logging.Logger;
public class CircuitManagerImpl implements CircuitManager {
private final static boolean DEBUG_CIRCUIT_CREATION = true;
private final ConnectionManagerImpl connectionManager;
private final Logger logger;
private final Set<Circuit> pendingCircuits;
private final Set<Circuit> activeCircuits;
private final Set<Circuit> cleanCircuits;
private final TorRandom random;
private final List<StreamExitRequest> pendingExitStreams = new LinkedList<StreamExitRequest>();
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
private final Runnable circuitCreationTask;
public CircuitManagerImpl(Directory directory, ConnectionManagerImpl connectionManager, LogManager logManager) {
this.connectionManager = connectionManager;
this.logger = logManager.getLogger("circuits");
this.logger.enableDebug();
this.circuitCreationTask = new CircuitCreationTask(directory, this, logger);
this.activeCircuits = new HashSet<Circuit>();
this.pendingCircuits = new HashSet<Circuit>();
this.cleanCircuits = new HashSet<Circuit>();
this.random = new TorRandom();
}
public void startBuildingCircuits() {
scheduledExecutor.scheduleAtFixedRate(circuitCreationTask, 0, 1000, TimeUnit.MILLISECONDS);
if(DEBUG_CIRCUIT_CREATION) {
Runnable debugTask = createCircuitCreationDebugTask();
scheduledExecutor.scheduleAtFixedRate(debugTask, 0, 30000, TimeUnit.MILLISECONDS);
}
}
private Runnable createCircuitCreationDebugTask() {
return new Runnable() { public void run() {
logger.debug("CLEAN: "+ getCleanCircuitCount()
+ " PENDING: "+ getPendingCircuitCount()
+ " ACTIVE: "+ getActiveCircuitCount());
}};
}
public Circuit createNewCircuit() {
return CircuitImpl.create(this, connectionManager, logger);
}
synchronized void circuitStartConnect(Circuit circuit) {
pendingCircuits.add(circuit);
}
synchronized void circuitConnected(Circuit circuit) {
pendingCircuits.remove(circuit);
activeCircuits.add(circuit);
cleanCircuits.add(circuit);
}
synchronized void circuitDirty(Circuit circuit) {
cleanCircuits.remove(circuit);
}
synchronized void circuitInactive(Circuit circuit) {
pendingCircuits.remove(circuit);
activeCircuits.remove(circuit);
cleanCircuits.remove(circuit);
}
synchronized int getCleanCircuitCount() {
return cleanCircuits.size();
}
synchronized int getActiveCircuitCount() {
return activeCircuits.size();
}
synchronized int getPendingCircuitCount() {
return pendingCircuits.size();
}
List<Circuit> getRandomlyOrderedListOfActiveCircuits() {
final ArrayList<Circuit> ac = new ArrayList<Circuit>(activeCircuits);
final int sz = ac.size();
for(int i = 0; i < sz; i++) {
final Circuit tmp = ac.get(i);
final int swapIdx = random.nextInt(sz);
ac.set(i, ac.get(swapIdx));
ac.set(swapIdx, tmp);
}
return ac;
}
public OpenStreamResponse openExitStreamTo(String hostname, int port)
throws InterruptedException {
return openExitStreamByRequest(new StreamExitRequest(this, hostname, port));
}
public OpenStreamResponse openExitStreamTo(IPv4Address address, int port)
throws InterruptedException {
return openExitStreamByRequest(new StreamExitRequest(this, address, port));
}
private OpenStreamResponse openExitStreamByRequest(StreamExitRequest request) throws InterruptedException {
synchronized(pendingExitStreams) {
pendingExitStreams.add(request);
while(!request.isCompleted())
pendingExitStreams.wait();
}
return request.getResponse();
}
List<StreamExitRequest> getPendingExitStreams() {
synchronized(pendingExitStreams) {
return new ArrayList<StreamExitRequest>(pendingExitStreams);
}
}
List<Circuit> getPendingCircuits() {
synchronized(pendingCircuits) {
return new ArrayList<Circuit>(pendingCircuits);
}
}
void streamRequestIsCompleted(StreamExitRequest request) {
synchronized(pendingExitStreams) {
pendingExitStreams.remove(request);
pendingExitStreams.notifyAll();
}
}
}