package com.subgraph.orchid.directory; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.subgraph.orchid.DirectoryServer; import com.subgraph.orchid.KeyCertificate; import com.subgraph.orchid.RouterStatus; import com.subgraph.orchid.data.HexDigest; public class DirectoryServerImpl extends RouterImpl implements DirectoryServer { private List<KeyCertificate> certificates = new ArrayList<KeyCertificate>(); private boolean isHiddenServiceAuthority = false; private boolean isBridgeAuthority = false; private boolean isExtraInfoCache = false; private int port; private HexDigest v3Ident; DirectoryServerImpl(RouterStatus status) { super(null, status); } void setHiddenServiceAuthority() { isHiddenServiceAuthority = true; } void unsetHiddenServiceAuthority() { isHiddenServiceAuthority = false; } void setBridgeAuthority() { isBridgeAuthority = true; } void setExtraInfoCache() { isExtraInfoCache = true; } void setPort(int port) { this.port = port; } void setV3Ident(HexDigest fingerprint) { this.v3Ident = fingerprint; } public boolean isTrustedAuthority() { return true; } /** * Return true if this DirectoryServer entry has * complete and valid information. * @return */ public boolean isValid() { return true; } public boolean isV2Authority() { return hasFlag("Authority") && hasFlag("V2Dir"); } public boolean isV3Authority() { return hasFlag("Authority") && v3Ident != null; } public boolean isHiddenServiceAuthority() { return isHiddenServiceAuthority; } public boolean isBridgeAuthority() { return isBridgeAuthority; } public boolean isExtraInfoCache() { return isExtraInfoCache; } public HexDigest getV3Identity() { return v3Ident; } public KeyCertificate getCertificateByFingerprint(HexDigest fingerprint) { for(KeyCertificate kc: getCertificates()) { if(kc.getAuthoritySigningKey().getFingerprint().equals(fingerprint)) { return kc; } } return null; } public List<KeyCertificate> getCertificates() { synchronized(certificates) { purgeExpiredCertificates(); purgeOldCertificates(); return new ArrayList<KeyCertificate>(certificates); } } private void purgeExpiredCertificates() { Iterator<KeyCertificate> it = certificates.iterator(); while(it.hasNext()) { KeyCertificate elem = it.next(); if(elem.isExpired()) { it.remove(); } } } private void purgeOldCertificates() { if(certificates.size() < 2) { return; } final KeyCertificate newest = getNewestCertificate(); final Iterator<KeyCertificate> it = certificates.iterator(); while(it.hasNext()) { KeyCertificate elem = it.next(); if(elem != newest && isMoreThan48HoursOlder(newest, elem)) { it.remove(); } } } private KeyCertificate getNewestCertificate() { KeyCertificate newest = null; for(KeyCertificate kc : certificates) { if(newest == null || getPublishedMilliseconds(newest) > getPublishedMilliseconds(kc)) { newest = kc; } } return newest; } private boolean isMoreThan48HoursOlder(KeyCertificate newer, KeyCertificate older) { final long milliseconds = 48 * 60 * 60 * 1000; return (getPublishedMilliseconds(newer) - getPublishedMilliseconds(older)) > milliseconds; } private long getPublishedMilliseconds(KeyCertificate certificate) { return certificate.getKeyPublishedTime().getDate().getTime(); } public void addCertificate(KeyCertificate certificate) { if(!certificate.getAuthorityFingerprint().equals(v3Ident)) { throw new IllegalArgumentException("This certificate does not appear to belong to this directory authority"); } synchronized(certificates) { certificates.add(certificate); } } public String toString() { if(v3Ident != null) return "(Directory: "+ getNickname() +" "+ getAddress() +":"+ port +" fingerprint="+ getIdentityHash() +" v3ident="+ v3Ident +")"; else return "(Directory: "+ getNickname() +" "+ getAddress() +":"+ port +" fingerprint="+ getIdentityHash() +")"; } }