Package com.subgraph.orchid.circuits

Source Code of com.subgraph.orchid.circuits.CircuitManagerImpl$CircuitFilter

package com.subgraph.orchid.circuits;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.subgraph.orchid.Circuit;
import com.subgraph.orchid.CircuitBuildHandler;
import com.subgraph.orchid.CircuitManager;
import com.subgraph.orchid.CircuitNode;
import com.subgraph.orchid.Connection;
import com.subgraph.orchid.ConnectionCache;
import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.DirectoryCircuit;
import com.subgraph.orchid.ExitCircuit;
import com.subgraph.orchid.InternalCircuit;
import com.subgraph.orchid.OpenFailedException;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.Stream;
import com.subgraph.orchid.StreamConnectFailedException;
import com.subgraph.orchid.Tor;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.circuits.guards.EntryGuards;
import com.subgraph.orchid.circuits.hs.HiddenServiceManager;
import com.subgraph.orchid.circuits.path.CircuitPathChooser;
import com.subgraph.orchid.crypto.TorRandom;
import com.subgraph.orchid.dashboard.DashboardRenderable;
import com.subgraph.orchid.dashboard.DashboardRenderer;
import com.subgraph.orchid.data.IPv4Address;
import com.subgraph.orchid.directory.downloader.DirectoryDownloaderImpl;

public class CircuitManagerImpl implements CircuitManager, DashboardRenderable {
  private final static int OPEN_DIRECTORY_STREAM_RETRY_COUNT = 5;
  private final static int OPEN_DIRECTORY_STREAM_TIMEOUT = 10 * 1000;
 
  interface CircuitFilter {
    boolean filter(Circuit circuit);
  }

  private final TorConfig config;
  private final Directory directory;
  private final ConnectionCache connectionCache;
  private final Set<CircuitImpl> activeCircuits;
  private final Queue<InternalCircuit> cleanInternalCircuits;
  private int requestedInternalCircuitCount = 0;
  private int pendingInternalCircuitCount = 0;
  private final TorRandom random;
  private final PendingExitStreams pendingExitStreams;
  private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
  private final CircuitCreationTask circuitCreationTask;
  private final TorInitializationTracker initializationTracker;
  private final CircuitPathChooser pathChooser;
  private final HiddenServiceManager hiddenServiceManager;

  public CircuitManagerImpl(TorConfig config, DirectoryDownloaderImpl directoryDownloader, Directory directory, ConnectionCache connectionCache, TorInitializationTracker initializationTracker) {
    this.config = config;
    this.directory = directory;
    this.connectionCache = connectionCache;
    this.pathChooser = CircuitPathChooser.create(config, directory);
    if(config.getUseEntryGuards() || config.getUseBridges()) {
      this.pathChooser.enableEntryGuards(new EntryGuards(config, connectionCache, directoryDownloader, directory));
    }
    this.pendingExitStreams = new PendingExitStreams(config);
    this.circuitCreationTask = new CircuitCreationTask(config, directory, connectionCache, pathChooser, this, initializationTracker);
    this.activeCircuits = new HashSet<CircuitImpl>();
    this.cleanInternalCircuits = new LinkedList<InternalCircuit>();
    this.random = new TorRandom();
   
    this.initializationTracker = initializationTracker;
    this.hiddenServiceManager = new HiddenServiceManager(config, directory, this);
   
    directoryDownloader.setCircuitManager(this);
  }

  public void startBuildingCircuits() {
    scheduledExecutor.scheduleAtFixedRate(circuitCreationTask, 0, 1000, TimeUnit.MILLISECONDS);
  }

  public synchronized void stopBuildingCircuits(boolean killCircuits) {
    scheduledExecutor.shutdownNow();
    if(killCircuits) {
      List<CircuitImpl> circuits = new ArrayList<CircuitImpl>(activeCircuits);
      for(CircuitImpl c: circuits) {
        c.destroyCircuit();
      }
    }
  }

  public ExitCircuit createNewExitCircuit(Router exitRouter) {
    return CircuitImpl.createExitCircuit(this, exitRouter);
  }

  void addActiveCircuit(CircuitImpl circuit) {
    synchronized (activeCircuits) {
      activeCircuits.add(circuit);
      activeCircuits.notifyAll();
    }
  }
 
  void removeActiveCircuit(CircuitImpl circuit) {
    synchronized (activeCircuits) {
      activeCircuits.remove(circuit);
    }
  }

  synchronized int getActiveCircuitCount() {
    return activeCircuits.size();
  }

  Set<Circuit> getPendingCircuits() {
    return getCircuitsByFilter(new CircuitFilter() {
      public boolean filter(Circuit circuit) {
        return circuit.isPending();
      }
    });
  }

  synchronized int getPendingCircuitCount() {
    return getPendingCircuits().size();
  }
 
  Set<Circuit> getCircuitsByFilter(CircuitFilter filter) {
    final Set<Circuit> result = new HashSet<Circuit>();
    synchronized (activeCircuits) {
      for(CircuitImpl c: activeCircuits) {
        if(filter == null || filter.filter(c)) {
          result.add(c);
        }
      }
    }
    return result;
  }

  List<ExitCircuit> getRandomlyOrderedListOfExitCircuits() {
    final Set<Circuit> notDirectory = getCircuitsByFilter(new CircuitFilter() {
     
      public boolean filter(Circuit circuit) {
        final boolean exitType = circuit instanceof ExitCircuit;
        return exitType && !circuit.isMarkedForClose() && circuit.isConnected();
      }
    });
    final ArrayList<ExitCircuit> ac = new ArrayList<ExitCircuit>();
    for(Circuit c: notDirectory) {
      if(c instanceof ExitCircuit) {
        ac.add((ExitCircuit) c);
      }
    }
    final int sz = ac.size();
    for(int i = 0; i < sz; i++) {
      final ExitCircuit tmp = ac.get(i);
      final int swapIdx = random.nextInt(sz);
      ac.set(i, ac.get(swapIdx));
      ac.set(swapIdx, tmp);
    }
    return ac;
  }

  public Stream openExitStreamTo(String hostname, int port)
      throws InterruptedException, TimeoutException, OpenFailedException {
    if(hostname.endsWith(".onion")) {
      return hiddenServiceManager.getStreamTo(hostname, port);
    }
    validateHostname(hostname);
    circuitCreationTask.predictPort(port);
    return pendingExitStreams.openExitStream(hostname, port);
  }

  private void validateHostname(String hostname) throws OpenFailedException {
    maybeRejectInternalAddress(hostname);
    if(hostname.toLowerCase().endsWith(".onion")) {
      throw new OpenFailedException("Hidden services not supported");
    } else if(hostname.toLowerCase().endsWith(".exit")) {
      throw new OpenFailedException(".exit addresses are not supported");
    }
  }
 
  private void maybeRejectInternalAddress(String hostname) throws OpenFailedException {
    if(IPv4Address.isValidIPv4AddressString(hostname)) {
      maybeRejectInternalAddress(IPv4Address.createFromString(hostname));
    }
  }
 
  private void maybeRejectInternalAddress(IPv4Address address) throws OpenFailedException {
    final InetAddress inetAddress = address.toInetAddress();
    if(inetAddress.isSiteLocalAddress() && config.getClientRejectInternalAddress()) {
      throw new OpenFailedException("Rejecting stream target with internal address: "+ address);
    }
  }
  public Stream openExitStreamTo(IPv4Address address, int port)
      throws InterruptedException, TimeoutException, OpenFailedException {
    maybeRejectInternalAddress(address);
    circuitCreationTask.predictPort(port);
    return pendingExitStreams.openExitStream(address, port);
  }

  public List<StreamExitRequest> getPendingExitStreams() {
    return pendingExitStreams.getUnreservedPendingRequests();
  }

  public Stream openDirectoryStream() throws OpenFailedException, InterruptedException, TimeoutException {
    return openDirectoryStream(0);
  }

  public Stream openDirectoryStream(int purpose) throws OpenFailedException, InterruptedException {
    final int requestEventCode = purposeToEventCode(purpose, false);
    final int loadingEventCode = purposeToEventCode(purpose, true);
   
    int failCount = 0;
    while(failCount < OPEN_DIRECTORY_STREAM_RETRY_COUNT) {
      final DirectoryCircuit circuit = openDirectoryCircuit();
      if(requestEventCode > 0) {
        initializationTracker.notifyEvent(requestEventCode);
      }
      try {
        final Stream stream = circuit.openDirectoryStream(OPEN_DIRECTORY_STREAM_TIMEOUT, true);
        if(loadingEventCode > 0) {
          initializationTracker.notifyEvent(loadingEventCode);
        }
        return stream;
      } catch (StreamConnectFailedException e) {
        circuit.markForClose();
        failCount += 1;
      } catch (TimeoutException e) {
        circuit.markForClose();
      }
    }
    throw new OpenFailedException("Retry count exceeded opening directory stream");
  }

  public DirectoryCircuit openDirectoryCircuit() throws OpenFailedException {
    int failCount = 0;
    while(failCount < OPEN_DIRECTORY_STREAM_RETRY_COUNT) {
      final DirectoryCircuit circuit = CircuitImpl.createDirectoryCircuit(this);
      if(tryOpenCircuit(circuit, true, true)) {
        return circuit;
      }
      failCount += 1;
    }
    throw new OpenFailedException("Could not create circuit for directory stream");
  }
 
  private int purposeToEventCode(int purpose, boolean getLoadingEvent) {
    switch(purpose) {
    case DIRECTORY_PURPOSE_CONSENSUS:
      return getLoadingEvent ? Tor.BOOTSTRAP_STATUS_LOADING_STATUS : Tor.BOOTSTRAP_STATUS_REQUESTING_STATUS;
    case DIRECTORY_PURPOSE_CERTIFICATES:
       return getLoadingEvent ? Tor.BOOTSTRAP_STATUS_LOADING_KEYS : Tor.BOOTSTRAP_STATUS_REQUESTING_KEYS;
    case DIRECTORY_PURPOSE_DESCRIPTORS:
      return getLoadingEvent ? Tor.BOOTSTRAP_STATUS_LOADING_DESCRIPTORS : Tor.BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS;
    default:
      return 0;
    }
  }

  private static class DirectoryCircuitResult implements CircuitBuildHandler {

    private boolean isFailed;
   
    public void connectionCompleted(Connection connection) {}
    public void nodeAdded(CircuitNode node) {}
    public void circuitBuildCompleted(Circuit circuit) {}
   
    public void connectionFailed(String reason) {
      isFailed = true;
    }

    public void circuitBuildFailed(String reason) {
      isFailed = true;
    }
   
    boolean isSuccessful() {
      return !isFailed;
    }
  }

  public void dashboardRender(DashboardRenderer renderer, PrintWriter writer, int flags) throws IOException {
    if((flags & DASHBOARD_CIRCUITS) == 0) {
      return;
    }
    renderer.renderComponent(writer, flags, connectionCache);
    renderer.renderComponent(writer, flags, circuitCreationTask.getCircuitPredictor());
    writer.println("[Circuit Manager]");
    writer.println();
    for(Circuit c: getCircuitsByFilter(null)) {
      renderer.renderComponent(writer, flags, c);
    }
  }

  public InternalCircuit getCleanInternalCircuit() throws InterruptedException {
    synchronized(cleanInternalCircuits) {
      try {
        requestedInternalCircuitCount += 1;
        while(cleanInternalCircuits.isEmpty()) {
          cleanInternalCircuits.wait();
        }
        return cleanInternalCircuits.remove();
      } finally {
        requestedInternalCircuitCount -= 1;
      }
    }
  }

  int getNeededCleanCircuitCount(boolean isPredicted) {
    synchronized (cleanInternalCircuits) {
      final int predictedCount = (isPredicted) ? 2 : 0;
      final int needed = Math.max(requestedInternalCircuitCount, predictedCount) - (pendingInternalCircuitCount + cleanInternalCircuits.size());
      if(needed < 0) {
        return 0;
      } else {
        return needed;
      }
    }
  }
 
  void incrementPendingInternalCircuitCount() {
    synchronized (cleanInternalCircuits) {
      pendingInternalCircuitCount += 1;
    }
  }
 
  void decrementPendingInternalCircuitCount() {
    synchronized (cleanInternalCircuits) {
      pendingInternalCircuitCount -= 1;
    }
  }

  void addCleanInternalCircuit(InternalCircuit circuit) {
    synchronized(cleanInternalCircuits) {
      pendingInternalCircuitCount -= 1;
      cleanInternalCircuits.add(circuit);
      cleanInternalCircuits.notifyAll();
    }
  }

  boolean isNtorEnabled() {
    switch(config.getUseNTorHandshake()) {
    case AUTO:
      return isNtorEnabledInConsensus();
    case FALSE:
      return false;
    case TRUE:
      return true;
    default:
      throw new IllegalArgumentException("getUseNTorHandshake() returned "+ config.getUseNTorHandshake());
    }
  }
 
  boolean isNtorEnabledInConsensus() {
    ConsensusDocument consensus = directory.getCurrentConsensusDocument();
    return (consensus != null) && (consensus.getUseNTorHandshake());
  }

  public DirectoryCircuit openDirectoryCircuitTo(List<Router> path) throws OpenFailedException {
    final DirectoryCircuit circuit = CircuitImpl.createDirectoryCircuitTo(this, path);
    if(!tryOpenCircuit(circuit, true, false)) {
      throw new OpenFailedException("Could not create directory circuit for path");
    }
    return circuit;
  }

  public ExitCircuit openExitCircuitTo(List<Router> paththrows OpenFailedException {
    final ExitCircuit circuit = CircuitImpl.createExitCircuitTo(this, path);
    if(!tryOpenCircuit(circuit, false, false)) {
      throw new OpenFailedException("Could not create exit circuit for path");
    }
    return circuit;
  }

  public InternalCircuit openInternalCircuitTo(List<Router> path) throws OpenFailedException {
    final InternalCircuit circuit = CircuitImpl.createInternalCircuitTo(this, path);
    if(!tryOpenCircuit(circuit, false, false)) {
      throw new OpenFailedException("Could not create internal circuit for path");
    }
    return circuit;
  }
 
  private boolean tryOpenCircuit(Circuit circuit, boolean isDirectory, boolean trackInitialization) {
    final DirectoryCircuitResult result = new DirectoryCircuitResult();
    final CircuitCreationRequest req = new CircuitCreationRequest(pathChooser, circuit, result, isDirectory);
    final CircuitBuildTask task = new CircuitBuildTask(req, connectionCache, isNtorEnabled(), (trackInitialization) ? (initializationTracker) : (null));
    task.run();
    return result.isSuccessful();
  }
}
TOP

Related Classes of com.subgraph.orchid.circuits.CircuitManagerImpl$CircuitFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.