package com.subgraph.orchid.circuits.hs;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
import com.subgraph.orchid.Circuit;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.HiddenServiceCircuit;
import com.subgraph.orchid.InternalCircuit;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.TorException;
import com.subgraph.orchid.circuits.CircuitManagerImpl;
import com.subgraph.orchid.crypto.TorTapKeyAgreement;
public class RendezvousCircuitBuilder implements Callable<HiddenServiceCircuit>{
private final Logger logger = Logger.getLogger(RendezvousCircuitBuilder.class.getName());
private final Directory directory;
private final CircuitManagerImpl circuitManager;
private final HiddenService hiddenService;
private final HSDescriptor serviceDescriptor;
public RendezvousCircuitBuilder(Directory directory, CircuitManagerImpl circuitManager, HiddenService hiddenService, HSDescriptor descriptor) {
this.directory = directory;
this.circuitManager = circuitManager;
this.hiddenService = hiddenService;
this.serviceDescriptor = descriptor;
}
public HiddenServiceCircuit call() throws Exception {
logger.fine("Opening rendezvous circuit for "+ logServiceName());
final InternalCircuit rendezvous = circuitManager.getCleanInternalCircuit();
logger.fine("Establishing rendezvous for "+ logServiceName());
RendezvousProcessor rp = new RendezvousProcessor(rendezvous);
if(!rp.establishRendezvous()) {
rendezvous.markForClose();
return null;
}
logger.fine("Opening introduction circuit for "+ logServiceName());
final IntroductionProcessor introductionProcessor = openIntroduction();
if(introductionProcessor == null) {
logger.info("Failed to open connection to any introduction point");
rendezvous.markForClose();
return null;
}
logger.fine("Sending introduce cell for "+ logServiceName());
final TorTapKeyAgreement kex = new TorTapKeyAgreement();
final boolean icResult = introductionProcessor.sendIntroduce(introductionProcessor.getServiceKey(), kex.getPublicKeyBytes(), rp.getCookie(), rp.getRendezvousRouter());
introductionProcessor.markCircuitForClose();
if(!icResult) {
rendezvous.markForClose();
return null;
}
logger.fine("Processing RV2 for "+ logServiceName());
HiddenServiceCircuit hsc = rp.processRendezvous2(kex);
if(hsc == null) {
rendezvous.markForClose();
}
logger.fine("Rendezvous circuit opened for "+ logServiceName());
return hsc;
}
private String logServiceName() {
return hiddenService.getOnionAddressForLogging();
}
private IntroductionProcessor openIntroduction() {
for(IntroductionPoint ip: serviceDescriptor.getShuffledIntroductionPoints()) {
final Circuit circuit = attemptOpenIntroductionCircuit(ip);
if(circuit != null) {
return new IntroductionProcessor(hiddenService, circuit, ip);
}
}
return null;
}
private Circuit attemptOpenIntroductionCircuit(IntroductionPoint ip) {
final Router r = directory.getRouterByIdentity(ip.getIdentity());
if(r == null) {
return null;
}
try {
final InternalCircuit circuit = circuitManager.getCleanInternalCircuit();
return circuit.cannibalizeToIntroductionPoint(r);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} catch (TorException e) {
logger.fine("cannibalizeTo() failed : "+ e.getMessage());
return null;
}
}
}