package com.subgraph.orchid.circuits.hs;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import com.subgraph.orchid.DirectoryCircuit;
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.TorException;
import com.subgraph.orchid.circuits.CircuitManagerImpl;
import com.subgraph.orchid.directory.DocumentFieldParserImpl;
import com.subgraph.orchid.directory.downloader.DirectoryRequestFailedException;
import com.subgraph.orchid.directory.downloader.HttpConnection;
import com.subgraph.orchid.directory.parsing.DocumentParsingResultHandler;
public class HSDescriptorDownloader {
private final static Logger logger = Logger.getLogger(HSDescriptorDirectory.class.getName());
private final HiddenService hiddenService;
private final CircuitManagerImpl circuitManager;
private final List<HSDescriptorDirectory> directories;
public HSDescriptorDownloader(HiddenService hiddenService, CircuitManagerImpl circuitManager, List<HSDescriptorDirectory> directories) {
this.hiddenService = hiddenService;
this.circuitManager = circuitManager;
this.directories = directories;
}
public HSDescriptor downloadDescriptor() {
for(HSDescriptorDirectory d: directories) {
HSDescriptor descriptor = downloadDescriptorFrom(d);
if(descriptor != null) {
return descriptor;
}
}
// All directories failed
return null;
}
private HSDescriptor downloadDescriptorFrom(HSDescriptorDirectory dd) {
logger.fine("Downloading descriptor from "+ dd.getDirectory());
Stream stream = null;
try {
stream = openHSDirectoryStream(dd.getDirectory());
HttpConnection http = new HttpConnection(stream);
http.sendGetRequest("/tor/rendezvous2/"+ dd.getDescriptorId().toBase32());
http.readResponse();
if(http.getStatusCode() == 200) {
return readDocument(dd, http.getMessageBody());
} else {
logger.fine("HS descriptor download for "+ hiddenService.getOnionAddressForLogging() + " failed with status "+ http.getStatusCode());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} catch (TimeoutException e) {
logger.fine("Timeout downloading HS descriptor from "+ dd.getDirectory());
e.printStackTrace();
return null;
} catch (IOException e) {
logger.info("IOException downloading HS descriptor from "+ dd.getDirectory() +" : "+ e);
return null;
} catch (OpenFailedException e) {
logger.info("Failed to open stream to HS directory "+ dd.getDirectory() +" : "+ e.getMessage());
return null;
} catch (DirectoryRequestFailedException e) {
logger.info("Directory request to HS directory "+ dd.getDirectory() + " failed "+ e.getMessage());
return null;
} finally {
if(stream != null) {
stream.close();
stream.getCircuit().markForClose();
}
}
return null;
}
private Stream openHSDirectoryStream(Router directory) throws TimeoutException, InterruptedException, OpenFailedException {
final InternalCircuit circuit = circuitManager.getCleanInternalCircuit();
try {
final DirectoryCircuit dc = circuit.cannibalizeToDirectory(directory);
return dc.openDirectoryStream(10000, true);
} catch (StreamConnectFailedException e) {
circuit.markForClose();
throw new OpenFailedException("Failed to open directory stream");
} catch (TorException e) {
circuit.markForClose();
throw new OpenFailedException("Failed to extend circuit to HS directory: "+ e.getMessage());
}
}
private HSDescriptor readDocument(HSDescriptorDirectory dd, ByteBuffer body) {
DocumentFieldParserImpl fieldParser = new DocumentFieldParserImpl(body);
HSDescriptorParser parser = new HSDescriptorParser(hiddenService, fieldParser, hiddenService.getAuthenticationCookie());
DescriptorParseResult result = new DescriptorParseResult(dd);
parser.parse(result);
return result.getDescriptor();
}
private static class DescriptorParseResult implements DocumentParsingResultHandler<HSDescriptor> {
HSDescriptorDirectory dd;
HSDescriptor descriptor;
public DescriptorParseResult(HSDescriptorDirectory dd) {
this.dd = dd;
}
HSDescriptor getDescriptor() {
return descriptor;
}
public void documentParsed(HSDescriptor document) {
this.descriptor = document;
}
public void documentInvalid(HSDescriptor document, String message) {
logger.info("Invalid HS descriptor document received from "+ dd.getDirectory() + " for descriptor "+ dd.getDescriptorId());
}
public void parsingError(String message) {
logger.info("Failed to parse HS descriptor document received from "+ dd.getDirectory() + " for descriptor "+ dd.getDescriptorId() + " : " + message);
}
}
}