package com.subgraph.orchid.directory.downloader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import com.subgraph.orchid.CircuitManager;
import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.ConsensusDocument.RequiredCertificate;
import com.subgraph.orchid.Descriptor;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.DirectoryCircuit;
import com.subgraph.orchid.DirectoryDownloader;
import com.subgraph.orchid.KeyCertificate;
import com.subgraph.orchid.OpenFailedException;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.RouterDescriptor;
import com.subgraph.orchid.RouterMicrodescriptor;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.circuits.TorInitializationTracker;
import com.subgraph.orchid.data.HexDigest;
public class DirectoryDownloaderImpl implements DirectoryDownloader {
private final static Logger logger = Logger.getLogger(DirectoryDownloaderImpl.class.getName());
private final TorConfig config;
private final TorInitializationTracker initializationTracker;
private CircuitManager circuitManager;
private boolean isStarted;
private boolean isStopped;
private DirectoryDownloadTask downloadTask;
private Thread downloadTaskThread;
public DirectoryDownloaderImpl(TorConfig config, TorInitializationTracker initializationTracker) {
this.config = config;
this.initializationTracker = initializationTracker;
}
public void setCircuitManager(CircuitManager circuitManager) {
this.circuitManager = circuitManager;
}
public synchronized void start(Directory directory) {
if(isStarted) {
logger.warning("Directory downloader already running");
return;
}
if(circuitManager == null) {
throw new IllegalStateException("Must set CircuitManager instance with setCircuitManager() before starting.");
}
downloadTask = new DirectoryDownloadTask(config, directory, this);
downloadTaskThread = new Thread(downloadTask);
downloadTaskThread.start();
isStarted = true;
}
public synchronized void stop() {
if(!isStarted || isStopped) {
return;
}
downloadTask.stop();
downloadTaskThread.interrupt();
}
public RouterDescriptor downloadBridgeDescriptor(Router bridge) throws DirectoryRequestFailedException {
final DirectoryDocumentRequestor requestor = new DirectoryDocumentRequestor(openBridgeCircuit(bridge));
return requestor.downloadBridgeDescriptor(bridge);
}
public ConsensusDocument downloadCurrentConsensus(boolean useMicrodescriptors) throws DirectoryRequestFailedException {
return downloadCurrentConsensus(useMicrodescriptors, openCircuit());
}
public ConsensusDocument downloadCurrentConsensus(boolean useMicrodescriptors, DirectoryCircuit circuit) throws DirectoryRequestFailedException {
final DirectoryDocumentRequestor requestor = new DirectoryDocumentRequestor(circuit, initializationTracker);
return requestor.downloadCurrentConsensus(useMicrodescriptors);
}
public List<KeyCertificate> downloadKeyCertificates(Set<RequiredCertificate> required) throws DirectoryRequestFailedException {
return downloadKeyCertificates(required, openCircuit());
}
public List<KeyCertificate> downloadKeyCertificates(Set<RequiredCertificate> required, DirectoryCircuit circuit) throws DirectoryRequestFailedException {
final DirectoryDocumentRequestor requestor = new DirectoryDocumentRequestor(circuit, initializationTracker);
return requestor.downloadKeyCertificates(required);
}
public List<RouterDescriptor> downloadRouterDescriptors(Set<HexDigest> fingerprints) throws DirectoryRequestFailedException {
return downloadRouterDescriptors(fingerprints, openCircuit());
}
public List<RouterDescriptor> downloadRouterDescriptors(Set<HexDigest> fingerprints, DirectoryCircuit circuit) throws DirectoryRequestFailedException {
final DirectoryDocumentRequestor requestor = new DirectoryDocumentRequestor(circuit, initializationTracker);
final List<RouterDescriptor> ds = requestor.downloadRouterDescriptors(fingerprints);
return removeUnrequestedDescriptors(fingerprints, ds);
}
public List<RouterMicrodescriptor> downloadRouterMicrodescriptors(Set<HexDigest> fingerprints) throws DirectoryRequestFailedException {
return downloadRouterMicrodescriptors(fingerprints, openCircuit());
}
public List<RouterMicrodescriptor> downloadRouterMicrodescriptors(Set<HexDigest> fingerprints, DirectoryCircuit circuit) throws DirectoryRequestFailedException {
final DirectoryDocumentRequestor requestor = new DirectoryDocumentRequestor(circuit, initializationTracker);
final List<RouterMicrodescriptor> ds = requestor.downloadRouterMicrodescriptors(fingerprints);
return removeUnrequestedDescriptors(fingerprints, ds);
}
private <T extends Descriptor> List<T> removeUnrequestedDescriptors(Set<HexDigest> requested, List<T> received) {
final List<T> result = new ArrayList<T>();
int unrequestedCount = 0;
for(T d: received) {
if(requested.contains(d.getDescriptorDigest())) {
result.add(d);
} else {
unrequestedCount += 1;
}
}
if(unrequestedCount > 0) {
logger.warning("Discarding "+ unrequestedCount + " received descriptor(s) with fingerprints that did not match requested descriptors");
}
return result;
}
private DirectoryCircuit openCircuit() throws DirectoryRequestFailedException {
try {
return circuitManager.openDirectoryCircuit();
} catch (OpenFailedException e) {
throw new DirectoryRequestFailedException("Failed to open directory circuit", e);
}
}
private DirectoryCircuit openBridgeCircuit(Router bridge) throws DirectoryRequestFailedException {
try {
return circuitManager.openDirectoryCircuitTo(Arrays.asList(bridge));
} catch (OpenFailedException e) {
throw new DirectoryRequestFailedException("Failed to open directory circuit to bridge "+ bridge, e);
}
}
}