package com.subgraph.orchid.directory;
import java.util.Collections;
import java.util.Set;
import com.subgraph.orchid.Descriptor;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.RouterDescriptor;
import com.subgraph.orchid.RouterStatus;
import com.subgraph.orchid.TorException;
import com.subgraph.orchid.crypto.TorPublicKey;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.IPv4Address;
import com.subgraph.orchid.geoip.CountryCodeService;
public class RouterImpl implements Router {
static RouterImpl createFromRouterStatus(Directory directory, RouterStatus status) {
return new RouterImpl(directory, status);
}
private final Directory directory;
private final HexDigest identityHash;
protected RouterStatus status;
private Descriptor descriptor;
private volatile String cachedCountryCode;
protected RouterImpl(Directory directory, RouterStatus status) {
this.directory = directory;
this.identityHash = status.getIdentity();
this.status = status;
refreshDescriptor();
}
void updateStatus(RouterStatus status) {
if(!identityHash.equals(status.getIdentity()))
throw new TorException("Identity hash does not match status update");
this.status = status;
this.cachedCountryCode = null;
this.descriptor = null;
refreshDescriptor();
}
public boolean isDescriptorDownloadable() {
refreshDescriptor();
if(descriptor != null) {
return false;
}
final long now = System.currentTimeMillis();
final long diff = now - status.getPublicationTime().getDate().getTime();
return diff > (1000 * 60 * 10);
}
public String getVersion() {
return status.getVersion();
}
public HexDigest getDescriptorDigest() {
return status.getDescriptorDigest();
}
public IPv4Address getAddress() {
return status.getAddress();
}
public Descriptor getCurrentDescriptor() {
refreshDescriptor();
return descriptor;
}
private synchronized void refreshDescriptor() {
if(descriptor != null || directory == null) {
return;
}
if(status.getMicrodescriptorDigest() != null) {
descriptor = directory.getMicrodescriptorFromCache(status.getMicrodescriptorDigest());
} else if(status.getDescriptorDigest() != null){
descriptor = directory.getBasicDescriptorFromCache(status.getDescriptorDigest());
}
}
public HexDigest getMicrodescriptorDigest() {
return status.getMicrodescriptorDigest();
}
public boolean hasFlag(String flag) {
return status.hasFlag(flag);
}
public boolean isHibernating() {
final RouterDescriptor rd = downcastDescriptor();
if(rd == null) {
return false;
} else {
return rd.isHibernating();
}
}
public boolean isRunning() {
return hasFlag("Running");
}
public boolean isValid() {
return hasFlag("Valid");
}
public boolean isBadExit() {
return hasFlag("BadExit");
}
public boolean isPossibleGuard() {
return hasFlag("Guard");
}
public boolean isExit() {
return hasFlag("Exit");
}
public boolean isFast() {
return hasFlag("Fast");
}
public boolean isStable() {
return hasFlag("Stable");
}
public boolean isHSDirectory() {
return hasFlag("HSDir");
}
public int getDirectoryPort() {
return status.getDirectoryPort();
}
public HexDigest getIdentityHash() {
return identityHash;
}
public TorPublicKey getIdentityKey() {
final RouterDescriptor rd = downcastDescriptor();
if(rd != null) {
return rd.getIdentityKey();
} else {
return null;
}
}
public String getNickname() {
return status.getNickname();
}
public int getOnionPort() {
return status.getRouterPort();
}
public TorPublicKey getOnionKey() {
refreshDescriptor();
if(descriptor != null) {
return descriptor.getOnionKey();
} else {
return null;
}
}
public byte[] getNTorOnionKey() {
refreshDescriptor();
if(descriptor != null) {
return descriptor.getNTorOnionKey();
} else {
return null;
}
}
public boolean hasBandwidth() {
return status.hasBandwidth();
}
public int getEstimatedBandwidth() {
return status.getEstimatedBandwidth();
}
public int getMeasuredBandwidth() {
return status.getMeasuredBandwidth();
}
public Set<String> getFamilyMembers() {
refreshDescriptor();
if(descriptor != null) {
return descriptor.getFamilyMembers();
} else {
return Collections.emptySet();
}
}
public int getAverageBandwidth() {
final RouterDescriptor rd = downcastDescriptor();
if(rd == null) {
return 0;
} else {
return rd.getAverageBandwidth();
}
}
public int getBurstBandwidth() {
final RouterDescriptor rd = downcastDescriptor();
if(rd == null) {
return 0;
} else {
return rd.getBurstBandwidth();
}
}
public int getObservedBandwidth() {
final RouterDescriptor rd = downcastDescriptor();
if(rd == null) {
return 0;
} else {
return rd.getObservedBandwidth();
}
}
public boolean exitPolicyAccepts(IPv4Address address, int port) {
refreshDescriptor();
if(descriptor == null) {
return false;
} else if(address == null) {
return descriptor.exitPolicyAccepts(port);
} else {
return descriptor.exitPolicyAccepts(address, port);
}
}
public boolean exitPolicyAccepts(int port) {
return exitPolicyAccepts(null, port);
}
public String toString() {
return "Router["+ getNickname() +" ("+getAddress() +":"+ getOnionPort() +")]";
}
public String getCountryCode() {
String cc = cachedCountryCode;
if(cc == null) {
cc = CountryCodeService.getInstance().getCountryCodeForAddress(getAddress());
cachedCountryCode = cc;
}
return cc;
}
private RouterDescriptor downcastDescriptor() {
refreshDescriptor();
if(descriptor instanceof RouterDescriptor) {
return (RouterDescriptor) descriptor;
} else {
return null;
}
}
}