[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-lib/master] Move all Java sources to src/main/java/.
commit 600a020877bb82ee1fd5852a5694d268411e2ed4
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Tue Jul 5 10:28:06 2016 +0200
Move all Java sources to src/main/java/.
---
build.xml | 10 +-
.../torproject/descriptor/BandwidthHistory.java | 52 +
.../descriptor/BridgeExtraInfoDescriptor.java | 25 +
.../torproject/descriptor/BridgeNetworkStatus.java | 128 ++
.../descriptor/BridgePoolAssignment.java | 47 +
.../descriptor/BridgeServerDescriptor.java | 24 +
.../java/org/torproject/descriptor/Descriptor.java | 39 +
.../torproject/descriptor/DescriptorCollector.java | 62 +
.../descriptor/DescriptorDownloader.java | 198 +++
.../org/torproject/descriptor/DescriptorFile.java | 77 +
.../descriptor/DescriptorParseException.java | 20 +
.../torproject/descriptor/DescriptorParser.java | 47 +
.../torproject/descriptor/DescriptorReader.java | 143 ++
.../torproject/descriptor/DescriptorRequest.java | 100 ++
.../descriptor/DescriptorSourceFactory.java | 187 +++
.../org/torproject/descriptor/DirSourceEntry.java | 96 ++
.../descriptor/DirectoryKeyCertificate.java | 109 ++
.../torproject/descriptor/DirectorySignature.java | 52 +
.../java/org/torproject/descriptor/ExitList.java | 92 ++
.../org/torproject/descriptor/ExitListEntry.java | 55 +
.../torproject/descriptor/ExtraInfoDescriptor.java | 646 ++++++++
.../ImplementationNotAccessibleException.java | 22 +
.../org/torproject/descriptor/Microdescriptor.java | 135 ++
.../torproject/descriptor/NetworkStatusEntry.java | 177 ++
.../org/torproject/descriptor/RelayDirectory.java | 104 ++
.../descriptor/RelayExtraInfoDescriptor.java | 21 +
.../torproject/descriptor/RelayNetworkStatus.java | 176 ++
.../descriptor/RelayNetworkStatusConsensus.java | 223 +++
.../descriptor/RelayNetworkStatusVote.java | 408 +++++
.../descriptor/RelayServerDescriptor.java | 20 +
.../torproject/descriptor/RouterStatusEntry.java | 51 +
.../torproject/descriptor/ServerDescriptor.java | 435 +++++
.../org/torproject/descriptor/TorperfResult.java | 215 +++
.../descriptor/impl/BandwidthHistoryImpl.java | 100 ++
.../descriptor/impl/BlockingIteratorImpl.java | 98 ++
.../impl/BridgeExtraInfoDescriptorImpl.java | 37 +
.../descriptor/impl/BridgeNetworkStatusImpl.java | 230 +++
.../descriptor/impl/BridgePoolAssignmentImpl.java | 99 ++
.../impl/BridgeServerDescriptorImpl.java | 37 +
.../descriptor/impl/DescriptorCollectorImpl.java | 249 +++
.../descriptor/impl/DescriptorDownloaderImpl.java | 283 ++++
.../descriptor/impl/DescriptorFileImpl.java | 78 +
.../torproject/descriptor/impl/DescriptorImpl.java | 337 ++++
.../descriptor/impl/DescriptorParseException.java | 15 +
.../descriptor/impl/DescriptorParserImpl.java | 28 +
.../descriptor/impl/DescriptorReaderImpl.java | 364 ++++
.../descriptor/impl/DescriptorRequestImpl.java | 114 ++
.../descriptor/impl/DirSourceEntryImpl.java | 218 +++
.../descriptor/impl/DirectoryDownloader.java | 104 ++
.../impl/DirectoryKeyCertificateImpl.java | 308 ++++
.../descriptor/impl/DirectorySignatureImpl.java | 115 ++
.../descriptor/impl/DownloadCoordinator.java | 10 +
.../descriptor/impl/DownloadCoordinatorImpl.java | 298 ++++
.../descriptor/impl/ExitListEntryImpl.java | 216 +++
.../torproject/descriptor/impl/ExitListImpl.java | 142 ++
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 1284 +++++++++++++++
.../descriptor/impl/MicrodescriptorImpl.java | 328 ++++
.../descriptor/impl/NetworkStatusEntryImpl.java | 382 +++++
.../descriptor/impl/NetworkStatusImpl.java | 270 +++
.../torproject/descriptor/impl/ParseHelper.java | 567 +++++++
.../descriptor/impl/RelayDirectoryImpl.java | 547 ++++++
.../impl/RelayExtraInfoDescriptorImpl.java | 37 +
.../impl/RelayNetworkStatusConsensusImpl.java | 414 +++++
.../descriptor/impl/RelayNetworkStatusImpl.java | 384 +++++
.../impl/RelayNetworkStatusVoteImpl.java | 761 +++++++++
.../descriptor/impl/RelayServerDescriptorImpl.java | 37 +
.../descriptor/impl/RouterStatusEntryImpl.java | 41 +
.../descriptor/impl/ServerDescriptorImpl.java | 985 +++++++++++
.../descriptor/impl/TorperfResultImpl.java | 546 ++++++
.../org/torproject/descriptor/package-info.java | 80 +
.../torproject/descriptor/BandwidthHistory.java | 52 -
.../descriptor/BridgeExtraInfoDescriptor.java | 25 -
.../torproject/descriptor/BridgeNetworkStatus.java | 128 --
.../descriptor/BridgePoolAssignment.java | 47 -
.../descriptor/BridgeServerDescriptor.java | 24 -
src/org/torproject/descriptor/Descriptor.java | 39 -
.../torproject/descriptor/DescriptorCollector.java | 62 -
.../descriptor/DescriptorDownloader.java | 198 ---
src/org/torproject/descriptor/DescriptorFile.java | 77 -
.../descriptor/DescriptorParseException.java | 20 -
.../torproject/descriptor/DescriptorParser.java | 47 -
.../torproject/descriptor/DescriptorReader.java | 143 --
.../torproject/descriptor/DescriptorRequest.java | 100 --
.../descriptor/DescriptorSourceFactory.java | 187 ---
src/org/torproject/descriptor/DirSourceEntry.java | 96 --
.../descriptor/DirectoryKeyCertificate.java | 109 --
.../torproject/descriptor/DirectorySignature.java | 52 -
src/org/torproject/descriptor/ExitList.java | 92 --
src/org/torproject/descriptor/ExitListEntry.java | 55 -
.../torproject/descriptor/ExtraInfoDescriptor.java | 646 --------
.../ImplementationNotAccessibleException.java | 22 -
src/org/torproject/descriptor/Microdescriptor.java | 135 --
.../torproject/descriptor/NetworkStatusEntry.java | 177 --
src/org/torproject/descriptor/RelayDirectory.java | 104 --
.../descriptor/RelayExtraInfoDescriptor.java | 21 -
.../torproject/descriptor/RelayNetworkStatus.java | 176 --
.../descriptor/RelayNetworkStatusConsensus.java | 223 ---
.../descriptor/RelayNetworkStatusVote.java | 408 -----
.../descriptor/RelayServerDescriptor.java | 20 -
.../torproject/descriptor/RouterStatusEntry.java | 51 -
.../torproject/descriptor/ServerDescriptor.java | 435 -----
src/org/torproject/descriptor/TorperfResult.java | 215 ---
.../descriptor/impl/BandwidthHistoryImpl.java | 100 --
.../descriptor/impl/BlockingIteratorImpl.java | 98 --
.../impl/BridgeExtraInfoDescriptorImpl.java | 37 -
.../descriptor/impl/BridgeNetworkStatusImpl.java | 230 ---
.../descriptor/impl/BridgePoolAssignmentImpl.java | 99 --
.../impl/BridgeServerDescriptorImpl.java | 37 -
.../descriptor/impl/DescriptorCollectorImpl.java | 249 ---
.../descriptor/impl/DescriptorDownloaderImpl.java | 283 ----
.../descriptor/impl/DescriptorFileImpl.java | 78 -
.../torproject/descriptor/impl/DescriptorImpl.java | 337 ----
.../descriptor/impl/DescriptorParseException.java | 15 -
.../descriptor/impl/DescriptorParserImpl.java | 28 -
.../descriptor/impl/DescriptorReaderImpl.java | 364 ----
.../descriptor/impl/DescriptorRequestImpl.java | 114 --
.../descriptor/impl/DirSourceEntryImpl.java | 218 ---
.../descriptor/impl/DirectoryDownloader.java | 104 --
.../impl/DirectoryKeyCertificateImpl.java | 308 ----
.../descriptor/impl/DirectorySignatureImpl.java | 115 --
.../descriptor/impl/DownloadCoordinator.java | 10 -
.../descriptor/impl/DownloadCoordinatorImpl.java | 298 ----
.../descriptor/impl/ExitListEntryImpl.java | 216 ---
.../torproject/descriptor/impl/ExitListImpl.java | 142 --
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 1284 ---------------
.../descriptor/impl/MicrodescriptorImpl.java | 328 ----
.../descriptor/impl/NetworkStatusEntryImpl.java | 382 -----
.../descriptor/impl/NetworkStatusImpl.java | 270 ---
.../torproject/descriptor/impl/ParseHelper.java | 567 -------
.../descriptor/impl/RelayDirectoryImpl.java | 547 ------
.../impl/RelayExtraInfoDescriptorImpl.java | 37 -
.../impl/RelayNetworkStatusConsensusImpl.java | 414 -----
.../descriptor/impl/RelayNetworkStatusImpl.java | 384 -----
.../impl/RelayNetworkStatusVoteImpl.java | 761 ---------
.../descriptor/impl/RelayServerDescriptorImpl.java | 37 -
.../descriptor/impl/RouterStatusEntryImpl.java | 41 -
.../descriptor/impl/ServerDescriptorImpl.java | 985 -----------
.../descriptor/impl/TorperfResultImpl.java | 546 ------
src/org/torproject/descriptor/package-info.java | 80 -
.../descriptor/benchmark/MeasurePerformance.java | 278 ++++
.../descriptor/impl/BridgeNetworkStatusTest.java | 151 ++
.../descriptor/impl/ConsensusBuilder.java | 321 ++++
.../impl/DescriptorCollectorImplTest.java | 134 ++
.../descriptor/impl/ExitListImplTest.java | 131 ++
.../impl/ExtraInfoDescriptorImplTest.java | 1737 ++++++++++++++++++++
.../descriptor/impl/MicrodescriptorImplTest.java | 82 +
.../impl/RelayNetworkStatusConsensusImplTest.java | 1272 ++++++++++++++
.../impl/RelayNetworkStatusVoteImplTest.java | 1373 ++++++++++++++++
.../descriptor/impl/ServerDescriptorImplTest.java | 1605 ++++++++++++++++++
.../descriptor/impl/TorperfResultImplTest.java | 97 ++
.../descriptor/benchmark/MeasurePerformance.java | 278 ----
.../descriptor/impl/BridgeNetworkStatusTest.java | 151 --
.../descriptor/impl/ConsensusBuilder.java | 321 ----
.../impl/DescriptorCollectorImplTest.java | 134 --
.../descriptor/impl/ExitListImplTest.java | 131 --
.../impl/ExtraInfoDescriptorImplTest.java | 1737 --------------------
.../descriptor/impl/MicrodescriptorImplTest.java | 82 -
.../impl/RelayNetworkStatusConsensusImplTest.java | 1272 --------------
.../impl/RelayNetworkStatusVoteImplTest.java | 1373 ----------------
.../descriptor/impl/ServerDescriptorImplTest.java | 1605 ------------------
.../descriptor/impl/TorperfResultImplTest.java | 97 --
161 files changed, 21515 insertions(+), 21515 deletions(-)
diff --git a/build.xml b/build.xml
index 6bb773b..0d6cf7d 100644
--- a/build.xml
+++ b/build.xml
@@ -1,10 +1,10 @@
<project default="jar" name="descriptor" basedir=".">
<property name="release.version" value="1.2.0-dev" />
- <property name="sources" value="src"/>
+ <property name="sources" value="src/main/java"/>
<property name="resources" value="resources"/>
<property name="classes" value="classes"/>
<property name="docs" value="javadoc"/>
- <property name="tests" value="test"/>
+ <property name="tests" value="src/test/java"/>
<property name="libs" value="lib"/>
<property name="jarfile" value="descriptor-${release.version}.jar" />
<property name="jarsourcesfile"
@@ -152,11 +152,11 @@
<include name="*.md" />
</tarfileset>
<tarfileset dir="${sources}"
- prefix="descriptor-${release.version}/src" />
+ prefix="descriptor-${release.version}/${sources}" />
<tarfileset dir="${tests}"
- prefix="descriptor-${release.version}/test" />
+ prefix="descriptor-${release.version}/${tests}" />
<tarfileset dir="${libs}"
- prefix="descriptor-${release.version}/lib" />
+ prefix="descriptor-${release.version}/${libs}" />
</tar>
</target>
</project>
diff --git a/src/main/java/org/torproject/descriptor/BandwidthHistory.java b/src/main/java/org/torproject/descriptor/BandwidthHistory.java
new file mode 100644
index 0000000..0be1a53
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BandwidthHistory.java
@@ -0,0 +1,52 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains the bandwidth history of a relay or bridge.
+ *
+ * <p>A bandwidth history is not a descriptor type of its own but usually
+ * part of extra-info descriptors ({@link ExtraInfoDescriptor}) or server
+ * descriptors ({@link ServerDescriptor}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface BandwidthHistory {
+
+ /**
+ * Return the original bandwidth history line as contained in the
+ * descriptor, possibly prefixed with {@code "opt "}.
+ *
+ * @since 1.0.0
+ */
+ public String getLine();
+
+ /**
+ * Return the time in milliseconds since the epoch when the most recent
+ * interval ends.
+ *
+ * @since 1.0.0
+ */
+ public long getHistoryEndMillis();
+
+ /**
+ * Return the interval length in seconds.
+ *
+ * @since 1.0.0
+ */
+ public long getIntervalLength();
+
+ /**
+ * Return the (possibly empty) bandwidth history with map keys being
+ * times in milliseconds since the epoch when intervals end and map
+ * values being number of bytes used in the interval, ordered from
+ * oldest to newest interval.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<Long, Long> getBandwidthValues();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
new file mode 100644
index 0000000..a3c168d
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
@@ -0,0 +1,25 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a sanitized bridge extra-info descriptor.
+ *
+ * <p>Sanitized bridge extra-info descriptors share many contents with
+ * relay extra-info descriptors ({@link RelayExtraInfoDescriptor}), which
+ * is why they share a common
+ * superinterface ({@link ExtraInfoDescriptor}). The main purpose of
+ * having two subinterfaces is being able to distinguish descriptor types
+ * more easily.</p>
+ *
+ * <p>Details about sanitizing bridge extra-info descriptors can be found
+ * <a href="https://collector.torproject.org/#type-bridge-extra-info">here</a>.
+ * </p>
+ *
+ * @since 1.1.0
+ */
+public interface BridgeExtraInfoDescriptor extends ExtraInfoDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
new file mode 100644
index 0000000..c7458fd
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
@@ -0,0 +1,128 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains a sanitized bridge network status document.
+ *
+ * <p>The bridge directory authority periodically publishes a network
+ * status document with one entry per known bridge in the network
+ * ({@link NetworkStatusEntry}) containing: a hash of its identity key, a
+ * hash of its most recent server descriptor, and a summary of what the
+ * bridge authority believed about its status.</p>
+ *
+ * <p>The main purpose of this document is to get an authoritative list of
+ * running bridges to the bridge distribution service BridgeDB.</p>
+ *
+ * <p>Details about sanitizing bridge network statuses can be found
+ * <a href="https://collector.torproject.org/#type-bridge-network-status">here</a>.
+ * </p>
+ *
+ * @since 1.0.0
+ */
+public interface BridgeNetworkStatus extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the minimum uptime in seconds that this authority requires
+ * for assigning the Stable flag, or -1 if the authority doesn't report
+ * this value.
+ *
+ * @since 1.1.0
+ */
+ public long getStableUptime();
+
+ /**
+ * Return the minimum MTBF (mean time between failure) that this
+ * authority requires for assigning the Stable flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getStableMtbf();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Fast flag, or -1 if the authority doesn't report this
+ * value.
+ *
+ * @since 1.1.0
+ */
+ public long getFastBandwidth();
+
+ /**
+ * Return the minimum WFU (weighted fractional uptime) in percent that
+ * this authority requires for assigning the Guard flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public double getGuardWfu();
+
+ /**
+ * Return the minimum weighted time in seconds that this authority
+ * needs to know about a relay before assigning the Guard flag, or -1 if
+ * the authority doesn't report this information.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardTk();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardBandwidthIncludingExits();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can not be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardBandwidthExcludingExits();
+
+ /**
+ * Return 1 if the authority has measured enough MTBF info to use the
+ * MTBF requirement instead of the uptime requirement for assigning the
+ * Stable flag, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getEnoughMtbfInfo();
+
+ /**
+ * Return 1 if the authority has enough measured bandwidths that it'll
+ * ignore the advertised bandwidth claims of routers without measured
+ * bandwidth, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getIgnoringAdvertisedBws();
+
+ /**
+ * Return status entries for each contained bridge, with map keys being
+ * SHA-1 digests of SHA-1 digest of the bridges' public identity keys,
+ * encoded as 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java b/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java
new file mode 100644
index 0000000..2de4ee9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java
@@ -0,0 +1,47 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains a sanitized list of bridges together with the distribution
+ * pools they have been assigned to by the bridge distribution service
+ * BridgeDB.
+ *
+ * <p>BridgeDB receives bridge network statuses
+ * ({@link BridgeNetworkStatus}) from the bridge authority, assigns these
+ * bridges to persistent distribution rings, and hands them out to bridge
+ * users. BridgeDB periodically dumps the list of running bridges with
+ * information about the rings, subrings, and file buckets to which they
+ * are assigned to a local file.</p>
+ *
+ * <p>Details about sanitizing bridge pool assignments can be found
+ * <a href="https://collector.torproject.org/#type-bridge-pool-assignment">here</a>.
+ * </p>
+ *
+ * @since 1.0.0
+ */
+public interface BridgePoolAssignment extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the entries contained in this bridge pool assignment list
+ * with map keys being SHA-1 digests of SHA-1 digest of the bridges'
+ * public identity keys, encoded as 40 upper-case hexadecimal
+ * characters, and map values being assignment strings, e.g.
+ * {@code "https ring=3 flag=stable"}.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, String> getEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java b/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java
new file mode 100644
index 0000000..7d4503f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java
@@ -0,0 +1,24 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a sanitized bridge server descriptor.
+ *
+ * <p>Sanitized bridge server descriptors share many contents with relay
+ * server descriptors ({@link RelayServerDescriptor}), which is why they
+ * share a common superinterface ({@link ServerDescriptor}). The main
+ * purpose of having two subinterfaces is being able to distinguish
+ * descriptor types more easily.</p>
+ *
+ * <p>Details about sanitizing bridge server descriptors can be found
+ * <a href="https://collector.torproject.org/#type-bridge-server-descriptor">here</a>.
+ * </p>
+ *
+ * @since 1.1.0
+ */
+public interface BridgeServerDescriptor extends ServerDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/Descriptor.java b/src/main/java/org/torproject/descriptor/Descriptor.java
new file mode 100644
index 0000000..7cad109
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/Descriptor.java
@@ -0,0 +1,39 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Superinterface for any descriptor with access to generic information
+ * about the descriptor.
+ *
+ * @since 1.0.0
+ */
+public interface Descriptor {
+
+ /**
+ * Return the raw descriptor bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getRawDescriptorBytes();
+
+ /**
+ * Return the (possibly empty) list of annotations in the format
+ * {@code "@key( value)*"}.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getAnnotations();
+
+ /**
+ * Return any unrecognized lines when parsing this descriptor, or an
+ * empty list if there were no unrecognized lines.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getUnrecognizedLines();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorCollector.java b/src/main/java/org/torproject/descriptor/DescriptorCollector.java
new file mode 100644
index 0000000..b1027dc
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorCollector.java
@@ -0,0 +1,62 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+
+/**
+ * Descriptor source that synchronizes descriptors from the CollecTor
+ * service to a given local directory.
+ *
+ * <p>This type is not a descriptor source in the proper sense, because it
+ * does not produce descriptors by itself. But it often creates the
+ * prerequisites for reading descriptors from disk using
+ * {@link DescriptorReader}.</p>
+ *
+ * <p>Code sample:</p>
+ * <pre>{@code
+ * DescriptorCollector descriptorCollector =
+ * DescriptorSourceFactory.createDescriptorCollector();
+ * descriptorCollector.collectDescriptors(
+ * // Download from Tor's main CollecTor instance,
+ * "https://collector.torproject.org",
+ * // include network status consensuses and relay server descriptors
+ * new String[] { "/recent/relay-descriptors/consensuses/",
+ * "/recent/relay-descriptors/server-descriptors/" },
+ * // regardless of last-modified time,
+ * 0L,
+ * // write to the local directory called in/,
+ * new File("in"),
+ * // and delete extraneous files that do not exist remotely anymore.
+ * true);
+ * }</pre>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorCollector {
+
+ /**
+ * Fetch remote files from a CollecTor instance that do not yet exist
+ * locally and possibly delete local files that do not exist remotely
+ * anymore.
+ *
+ * @param collecTorBaseUrl CollecTor base URL without trailing slash,
+ * e.g., {@code "https://collector.torproject.org"}
+ * @param remoteDirectories Remote directories to collect descriptors
+ * from, e.g.,
+ * {@code "/recent/relay-descriptors/server-descriptors/"}, without
+ * processing subdirectories unless they are explicitly listed
+ * @param minLastModified Minimum last-modified time in milliseconds of
+ * files to be collected, or 0 for collecting all files
+ * @param localDirectory Directory where collected files will be written
+ * @param deleteExtraneousLocalFiles Whether to delete all local files
+ * that do not exist remotely anymore
+ *
+ * @since 1.0.0
+ */
+ public void collectDescriptors(String collecTorBaseUrl,
+ String[] remoteDirectories, long minLastModified,
+ File localDirectory, boolean deleteExtraneousLocalFiles);
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorDownloader.java b/src/main/java/org/torproject/descriptor/DescriptorDownloader.java
new file mode 100644
index 0000000..f0b1101
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorDownloader.java
@@ -0,0 +1,198 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Descriptor source that downloads relay descriptors from directory
+ * authorities or mirrors.
+ *
+ * <p>Downloading descriptors is done in a batch which starts after
+ * setting any configuration options and initiating the download
+ * process.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorDownloader {
+
+ /**
+ * Add a directory authority to download descriptors from, which is
+ * only required for downloading network status votes and will be used
+ * when no directory mirrors are available.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectoryAuthority(String nickname, String ip,
+ int dirPort);
+
+ /**
+ * Add a directory mirror to download descriptors from, which is
+ * preferred for downloading descriptors, except for network status
+ * votes which are only available on directory authorities.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectoryMirror(String nickname, String ip, int dirPort);
+
+ /**
+ * Include the current network status consensus in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentConsensus();
+
+ /**
+ * Include the current network status consensus in the downloads, and
+ * attempt to download it from all directory authorities.
+ *
+ * <p>The primary purpose of doing this is to compare different
+ * consensuses and download characteristics to each other. Typically,
+ * downloading from a single directory mirror or authority is
+ * sufficient.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentConsensusFromAllDirectoryAuthorities();
+
+ /**
+ * Include the current network status votes referenced from a
+ * previously downloaded consensus in the downloads, which requires
+ * downloading the current consensus from at least one directory mirror
+ * or authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentReferencedVotes();
+
+ /**
+ * Include the current network status vote published by the given
+ * directory authority in the downloads, which requires downloading from
+ * at least one directory authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentVote(String fingerprint);
+
+ /**
+ * Include the current network status votes published by the given
+ * directory authorities in the downloads, which requires downloading
+ * from at least one directory authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentVotes(Set<String> fingerprints);
+
+ /**
+ * Include all server descriptors referenced from a previously
+ * downloaded network status consensus in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeReferencedServerDescriptors();
+
+ /**
+ * Exclude the server descriptor with the given identifier from the
+ * downloads even if it's referenced from a consensus and we're supposed
+ * to download all referenced server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeServerDescriptor(String identifier);
+
+ /**
+ * Exclude the server descriptors with the given identifiers from the
+ * downloads even if they are referenced from a consensus and we're
+ * supposed to download all referenced server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeServerDescriptors(Set<String> identifier);
+
+ /**
+ * Include all extra-info descriptors referenced from previously
+ * downloaded server descriptors in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeReferencedExtraInfoDescriptors();
+
+ /**
+ * Exclude the extra-info descriptor with the given identifier from the
+ * downloads even if it's referenced from a server descriptor and we're
+ * supposed to download all referenced extra-info descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeExtraInfoDescriptor(String identifier);
+
+ /**
+ * Exclude the extra-info descriptors with the given identifiers from
+ * the downloads even if they are referenced from server descriptors
+ * and we're supposed to download all referenced extra-info
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeExtraInfoDescriptors(Set<String> identifiers);
+
+ /**
+ * Define a connect timeout for a single request.
+ *
+ * <p>If a timeout expires, no further requests will be sent to the
+ * directory authority or mirror. Setting this value to 0 disables the
+ * connect timeout. Default value is 1 minute (60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setConnectTimeout(long connectTimeoutMillis);
+
+ /**
+ * Define a read timeout for a single request.
+ *
+ * <p>If a timeout expires, no further requests will be sent to the
+ * directory authority or mirror. Setting this value to 0 disables the
+ * read timeout. Default value is 1 minute (60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setReadTimeout(long readTimeoutMillis);
+
+ /**
+ * Define a global timeout for all requests.
+ *
+ * <p>Once this timeout expires, all running requests are aborted and no
+ * further requests are made. Setting this value to 0 disables the
+ * global timeout. Default is 1 hour (60 * 60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setGlobalTimeout(long globalTimeoutMillis);
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines();
+
+ /**
+ * Download the previously configured relay descriptors and make them
+ * available via the returned blocking iterator.
+ *
+ * <p>Whenever the downloader runs out of descriptors and expects to
+ * provide more shortly after, it blocks the caller. This method can
+ * only be run once.</p>
+ *
+ * @since 1.0.0
+ */
+ public Iterator<DescriptorRequest> downloadDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorFile.java b/src/main/java/org/torproject/descriptor/DescriptorFile.java
new file mode 100644
index 0000000..417d7f9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorFile.java
@@ -0,0 +1,77 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Container for descriptors read from a file.
+ *
+ * <p>When the {@link DescriptorReader} reads descriptors from local files
+ * it provides an iterator over these containers which in turn contain
+ * references to classes implementing the {@link Descriptor} interface.
+ * This container also stores potentially useful meta-data about the
+ * descriptor file.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorFile {
+
+ /**
+ * Return the directory where this descriptor file was contained, or
+ * null if the file was contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getDirectory();
+
+ /**
+ * Return the tarball where this descriptor file was contained, or null
+ * if the file was not contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getTarball();
+
+ /**
+ * Return the descriptor file itself, or null if the descriptor file
+ * was contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getFile();
+
+ /**
+ * Return the descriptor file name, which is either the absolute path
+ * of the file on disk, or the tar file entry name.
+ *
+ * @since 1.0.0
+ */
+ public String getFileName();
+
+ /**
+ * Return the time in milliseconds since the epoch when the descriptor
+ * file on disk was last modified.
+ *
+ * @since 1.0.0
+ */
+ public long getLastModified();
+
+ /**
+ * Return the descriptors contained in the descriptor file.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> getDescriptors();
+
+ /**
+ * Return the first exception that was thrown when reading this file or
+ * parsing its content, or null if no exception was thrown.
+ *
+ * @since 1.0.0
+ */
+ public Exception getException();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
new file mode 100644
index 0000000..309d3f7
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
@@ -0,0 +1,20 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Thrown if raw descriptor contents cannot be parsed to one or more
+ * {@link Descriptor} instances, according to descriptor specifications.
+ *
+ * @since 1.0.0
+ */
+@SuppressWarnings("deprecation")
+public class DescriptorParseException
+ extends org.torproject.descriptor.impl.DescriptorParseException {
+ private static final long serialVersionUID = 100L;
+ public DescriptorParseException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorParser.java b/src/main/java/org/torproject/descriptor/DescriptorParser.java
new file mode 100644
index 0000000..680b8b2
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorParser.java
@@ -0,0 +1,47 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Descriptor source that parses descriptors from raw descriptor contents.
+ *
+ * <p>Unlike most of the other descriptor sources this descriptor source
+ * does not operate in a batch-processing mode. It takes the raw
+ * descriptor contents of one or more descriptors, parses them, and
+ * returns a list of descriptors.</p>
+ *
+ * <p>This descriptor source is internally used by other descriptor
+ * sources but can also be used directly by applications that obtain
+ * raw descriptor contents via other means than one of the existing
+ * descriptor sources.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorParser {
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines);
+
+ /**
+ * Parse descriptors in the given byte array, possibly parsing the
+ * publication time from the file name, depending on the descriptor
+ * type.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
+ String fileName) throws DescriptorParseException;
+}
diff --git a/src/main/java/org/torproject/descriptor/DescriptorReader.java b/src/main/java/org/torproject/descriptor/DescriptorReader.java
new file mode 100644
index 0000000..771755e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorReader.java
@@ -0,0 +1,143 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.SortedMap;
+
+/**
+ * Descriptor source that reads descriptors from local files and provides
+ * an iterator over parsed descriptors.
+ *
+ * <p>This descriptor source is likely the most widely used one, possibly
+ * in combination with {@link DescriptorCollector} to synchronize
+ * descriptors from the CollecTor service.</p>
+ *
+ * <p>Reading descriptors is done in a batch which starts after setting
+ * any configuration options and initiating the read process.</p>
+ *
+ * <p>Code sample:</p>
+ * <pre>{@code
+ * DescriptorReader descriptorReader =
+ * DescriptorSourceFactory.createDescriptorReader();
+ * // Read descriptors from local directory called in/.
+ * descriptorReader.addDirectory(new File("in"));
+ * Iterator<DescriptorFile> descriptorFiles =
+ * descriptorReader.readDescriptors();
+ * while (descriptorFiles.hasNext()) {
+ * DescriptorFile descriptorFile = descriptorFiles.next();
+ * for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ * if ((descriptor instanceof RelayNetworkStatusConsensus)) {
+ * // Only process network status consensuses, ignore the rest.
+ * RelayNetworkStatusConsensus consensus =
+ * (RelayNetworkStatusConsensus) descriptor;
+ * processConsensus(consensus);
+ * }
+ * }
+ * }}</pre>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorReader {
+
+ /**
+ * Add a local directory to read descriptors from, which may contain
+ * descriptor files or tarballs containing descriptor files.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectory(File directory);
+
+ /**
+ * Add a tarball to read descriptors from, which may be uncompressed,
+ * bz2-compressed, or xz-compressed.
+ *
+ * @since 1.0.0
+ */
+ public void addTarball(File tarball);
+
+ /**
+ * Exclude files that are listed in the given history file and that
+ * haven't changed since they have last been read.
+ *
+ * <p>Add a new line for each descriptor that is read in this execution
+ * and remove lines for files that don't exist anymore.</p>
+ *
+ * <p>Lines in the history file contain the last modified time in
+ * milliseconds since the epoch and the absolute path of a file.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeFiles(File historyFile);
+
+ /**
+ * Exclude files if they haven't changed since the corresponding last
+ * modified timestamps.
+ *
+ * <p>Can be used instead of (or in addition to) a history file.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setExcludedFiles(SortedMap<String, Long> excludedFiles);
+
+ /**
+ * Return files and last modified timestamps of files that exist in the
+ * input directory or directories, but that have been excluded from
+ * parsing, because they haven't changed since they were last read.
+ *
+ * <p>Can be used instead of (or in addition to) a history file when
+ * combined with the set of parsed files.</p>
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExcludedFiles();
+
+ /**
+ * Return files and last modified timestamps of files that exist in the
+ * input directory or directories and that have been parsed.
+ *
+ * <p>Can be used instead of (or in addition to) a history file when
+ * combined with the set of excluded files.</p>
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getParsedFiles();
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines();
+
+ /**
+ * Don't keep more than this number of parsed descriptor files in the
+ * queue.
+ *
+ * <p>The default is 100, but if descriptor files contain hundreds or
+ * even thousands of descriptors, that default may be too high.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setMaxDescriptorFilesInQueue(int max);
+
+ /**
+ * Read the previously configured descriptors and make them available
+ * via the returned blocking iterator.
+ *
+ * <p>Whenever the reader runs out of descriptors and expects to provide
+ * more shortly after, it blocks the caller. This method can only be
+ * run once.</p>
+ *
+ * @since 1.0.0
+ */
+ public Iterator<DescriptorFile> readDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorRequest.java b/src/main/java/org/torproject/descriptor/DescriptorRequest.java
new file mode 100644
index 0000000..c36c0c0
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorRequest.java
@@ -0,0 +1,100 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Container for descriptors downloaded from a directory authority or
+ * mirror.
+ *
+ * <p>When the {@link DescriptorDownloader} downloads descriptors from
+ * directory authorities or mirrors it provides an iterator over these
+ * containers which in turn contain references to classes implementing the
+ * {@link Descriptor} interface. This container also stores potentially
+ * useful meta-data about the descriptor request.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorRequest {
+
+ /**
+ * Return the request URL that was used in this request.
+ *
+ * @since 1.0.0
+ */
+ public String getRequestUrl();
+
+ /**
+ * Return the nickname of the directory mirror or authority as
+ * previously configured.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectoryNickname();
+
+ /**
+ * Return the first exception that was thrown when making this request
+ * or parsing the response, or null if no exception was thrown.
+ *
+ * @since 1.0.0
+ */
+ public Exception getException();
+
+ /**
+ * Return the response code that the directory mirror or authority
+ * returned.
+ *
+ * @since 1.0.0
+ */
+ public int getResponseCode();
+
+ /**
+ * Return the time in milliseconds since the epoch when this request
+ * was started.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestStart();
+
+ /**
+ * Return the time in milliseconds since the epoch when this request
+ * ended.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestEnd();
+
+ /**
+ * Return whether this request ended, because the connect timeout has
+ * expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean connectTimeoutHasExpired();
+
+ /**
+ * Return whether this request ended, because the read timeout has
+ * expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean readTimeoutHasExpired();
+
+ /**
+ * Return whether this request ended, because the global timeout for
+ * all requests has expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean globalTimeoutHasExpired();
+
+ /**
+ * Return the descriptors contained in the reply.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> getDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
new file mode 100644
index 0000000..af13f39
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
@@ -0,0 +1,187 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Factory for descriptor sources which in turn produce descriptors.
+ *
+ * <p>Descriptor sources are the only producers of classes implementing
+ * the {@link Descriptor} superinterface. There exist descriptor sources
+ * for obtaining remote descriptor data ({@link DescriptorDownloader} and
+ * {@link DescriptorCollector}) and descriptor sources for processing
+ * local descriptor data ({@link DescriptorReader} and
+ * {@link DescriptorParser}).</p>
+ *
+ * <p>By default, this factory returns implementations from the library's
+ * own impl package. This may be overridden by setting Java properties,
+ * though most users will simply use the default implementations.</p>
+ *
+ * <p>These properties can be used for setting the implementation:</p>
+ * <ul>
+ * <li>{@code descriptor.collector}</li>
+ * <li>{@code descriptor.downloader}</li>
+ * <li>{@code descriptor.parser}</li>
+ * <li>{@code descriptor.reader}</li>
+ * </ul>
+ *
+ * <p>Assuming the classpath contains the special implementation
+ * referenced, your application classes as well as a descriptor API jar
+ * the following is an example for using a different implementation of the
+ * descriptor downloader:</p>
+ *
+ * <p><code>
+ * java -Ddescriptor.downloader=my.special.descriptorimpl.Downloader my.app.Mainclass
+ * </code></p>
+ *
+ * @since 1.0.0
+ */
+public final class DescriptorSourceFactory {
+
+ /**
+ * Default implementation of the {@link DescriptorDownloader}
+ * descriptor source.
+ *
+ * @since 1.0.0
+ */
+ public final static String DOWNLOADER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorDownloaderImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorParser} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String PARSER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorParserImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorReader} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String READER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorReaderImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorCollector} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String COLLECTOR_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorCollectorImpl";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorParser} descriptor source, which is by default set
+ * to the class in {@link #PARSER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String PARSER_PROPERTY = "descriptor.parser";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorReader} descriptor source, which is by default set
+ * to the class in {@link #READER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String READER_PROPERTY = "descriptor.reader";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorDownloader} descriptor source, which is by default
+ * set to the class in {@link #DOWNLOADER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String DOWNLOADER_PROPERTY =
+ "descriptor.downloader";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorCollector} descriptor source, which is by default
+ * set to the class in {@link #COLLECTOR_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String COLLECTOR_PROPERTY = "descriptor.collector";
+
+ /**
+ * Create a new {@link DescriptorParser} by instantiating the class in
+ * {@link #PARSER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorParser createDescriptorParser() {
+ return (DescriptorParser) retrieve(PARSER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorReader} by instantiating the class in
+ * {@link #READER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorReader createDescriptorReader() {
+ return (DescriptorReader) retrieve(READER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorDownloader} by instantiating the class
+ * in {@link #DOWNLOADER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorDownloader createDescriptorDownloader() {
+ return (DescriptorDownloader) retrieve(DOWNLOADER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorCollector} by instantiating the class
+ * in {@link #COLLECTOR_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorCollector createDescriptorCollector() {
+ return (DescriptorCollector) retrieve(COLLECTOR_PROPERTY);
+ }
+
+ private final static <T> Object retrieve(String type) {
+ Object object;
+ String clazzName = null;
+ try {
+ switch (type) {
+ case PARSER_PROPERTY:
+ clazzName = System.getProperty(type, PARSER_DEFAULT);
+ break;
+ case DOWNLOADER_PROPERTY:
+ clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
+ break;
+ case READER_PROPERTY:
+ clazzName = System.getProperty(type, READER_DEFAULT);
+ break;
+ case COLLECTOR_PROPERTY:
+ clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
+ break;
+ }
+ object = ClassLoader.getSystemClassLoader().loadClass(clazzName).
+ newInstance();
+ } catch (ClassNotFoundException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ } catch (InstantiationException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ } catch (IllegalAccessException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ }
+ return object;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirSourceEntry.java b/src/main/java/org/torproject/descriptor/DirSourceEntry.java
new file mode 100644
index 0000000..96d81ee
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirSourceEntry.java
@@ -0,0 +1,96 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains details about an authority and its vote that contributed to a
+ * consensus.
+ *
+ * <p>A directory source entry is not a descriptor type of its own but is
+ * part of a network status consensus
+ * ({@link RelayNetworkStatusConsensus}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirSourceEntry {
+
+ /**
+ * Return the raw directory source entry bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getDirSourceEntryBytes();
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.2.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getIp();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return the TCP port where this authority accepts TLS connections for
+ * the main OR protocol.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return whether this directory source entry was created using a
+ * legacy key.
+ *
+ * @since 1.0.0
+ */
+ public boolean isLegacy();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the SHA-1 vote digest, encoded as 40 lower-case hexadecimal
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getVoteDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java b/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java
new file mode 100644
index 0000000..07211ef
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java
@@ -0,0 +1,109 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a key certificate in the version 3 directory protocol.
+ *
+ * <p>Every directory authority in the version 3 directory protocol uses
+ * two keys: a medium-term signing key, and a long-term authority identity
+ * key. (Authorities also have a relay identity key used in their role as
+ * a relay and by earlier versions of the directory protocol.) The
+ * identity key is used from time to time to sign new key certificates
+ * containing signing keys. The contained signing key is used to sign key
+ * certificates and status documents.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirectoryKeyCertificate extends Descriptor {
+
+ /**
+ * Return the version of this descriptor, which must be 3 or higher.
+ *
+ * @since 1.0.0
+ */
+ public int getDirKeyCertificateVersion();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the certificate does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or -1 if the certificate does not contain a port.
+ *
+ * @since 1.0.0
+ */
+ public int getPort();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the authority's identity key in PEM format.
+ *
+ * @since 1.0.0
+ */
+ public String getDirIdentityKey();
+
+ /**
+ * Return the time in milliseconds since the epoch when the authority's
+ * signing key and this key certificate were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch after which the
+ * authority's signing key is no longer valid.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyExpiresMillis();
+
+ /**
+ * Return the authority's signing key in PEM format.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return the signature of the authority's identity key made using the
+ * authority's signing key, or null if the certificate does not contain
+ * such a signature.
+ *
+ * @since 1.0.0
+ */
+ public String getDirKeyCrosscert();
+
+ /**
+ * Return the certificate signature from the initial item
+ * "dir-key-certificate-version" until the final item
+ * "dir-key-certification", signed with the authority identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirKeyCertification();
+
+ /**
+ * Return the SHA-1 certificate digest, encoded as 40 lower-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getCertificateDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirectorySignature.java b/src/main/java/org/torproject/descriptor/DirectorySignature.java
new file mode 100644
index 0000000..8877a4e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirectorySignature.java
@@ -0,0 +1,52 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains the signature of a network status consensus or vote.
+ *
+ * <p>A directory signature is not a descriptor type of its own but is
+ * part of a network status consensus
+ * ({@link RelayNetworkStatusConsensus}) or vote
+ * ({@link RelayNetworkStatusVote}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirectorySignature {
+
+ /**
+ * Return the digest algorithm, which is "sha1" by default and which
+ * can be "sha256" or another digest algorithm.
+ *
+ * @since 1.0.0
+ */
+ public String getAlgorithm();
+
+ /**
+ * Return the SHA-1 digest of the authority's long-term identity key in
+ * the version 3 directory protocol, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the SHA-1 digest of the authority's medium-term signing key
+ * in the version 3 directory protocol, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKeyDigest();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key in the version 3 directory protocol.
+ *
+ * @since 1.0.0
+ */
+ public String getSignature();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExitList.java b/src/main/java/org/torproject/descriptor/ExitList.java
new file mode 100644
index 0000000..2a5cb2e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExitList.java
@@ -0,0 +1,92 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Contains an exit list containing the IP addresses of relays that the
+ * exit list service TorDNSEL found when exiting through them.
+ *
+ * @since 1.0.0
+ */
+public interface ExitList extends Descriptor {
+
+ /**
+ * End-of-line character expected in exit lists.
+ *
+ * @since 1.0.0
+ */
+ public final static String EOL = "\n";
+
+ /**
+ * Exit list entry containing results from a single exit scan.
+ *
+ * @since 1.1.0
+ */
+ public interface Entry {
+
+ /**
+ * Return the scanned relay's fingerprint, which is a SHA-1 digest of
+ * the relays's public identity key, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.1.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when the scanned
+ * relay's last known descriptor was published.
+ *
+ * @since 1.1.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the network
+ * status that this scan was based on was published.
+ *
+ * @since 1.1.0
+ */
+ public long getLastStatusMillis();
+
+ /**
+ * Return the IP addresses that were determined in the scan with map
+ * keys being IPv4 addresses in dotted-quad format and map values
+ * being scan times in milliseconds since the epoch.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Long> getExitAddresses();
+ }
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was downloaded.
+ *
+ * @since 1.0.0
+ */
+ public long getDownloadedMillis();
+
+ /**
+ * Return the unordered set of exit scan results.
+ *
+ * @since 1.0.0
+ * @deprecated The {@link ExitListEntry} type has been deprecated and
+ * superseded by {@link ExitList.Entry} which is returned by
+ * {@link #getEntries()}.
+ */
+ @Deprecated
+ public Set<ExitListEntry> getExitListEntries();
+
+ /**
+ * Return the unordered set of exit scan results.
+ *
+ * @since 1.1.0
+ */
+ public Set<ExitList.Entry> getEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExitListEntry.java b/src/main/java/org/torproject/descriptor/ExitListEntry.java
new file mode 100644
index 0000000..2a3d79f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExitListEntry.java
@@ -0,0 +1,55 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Exit list entry containing results from a single exit scan.
+ *
+ * @since 1.0.0
+ * @deprecated Superseded by {@link ExitList.Entry}.
+ */
+@Deprecated
+public interface ExitListEntry extends ExitList.Entry {
+
+ /**
+ * Return the scanned relay's fingerprint, which is a SHA-1 digest of
+ * the relays's public identity key, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when the scanned
+ * relay's last known descriptor was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the network
+ * status that this scan was based on was published.
+ *
+ * @since 1.0.0
+ */
+ public long getLastStatusMillis();
+
+ /**
+ * Return the IPv4 address in dotted-quad format that was determined in
+ * the scan.
+ *
+ * @since 1.0.0
+ */
+ public String getExitAddress();
+
+ /**
+ * Return the scan time in milliseconds since the epoch.
+ *
+ * @since 1.0.0
+ */
+ public long getScanMillis();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
new file mode 100644
index 0000000..49efbf3
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
@@ -0,0 +1,646 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * Contains a relay or sanitized bridge extra-info descriptor.
+ *
+ * <p>Relays publish extra-info descriptors as an addendum to server
+ * descriptors ({@link ServerDescriptor}) to report extraneous information
+ * to the directory authorities that clients do not need to download in
+ * order to function. This information primarily consists of statistics
+ * gathered by the relay about its usage and can take up a lot of
+ * descriptor space. The separation of server descriptors and extra-info
+ * descriptors has become less relevant with the introduction of
+ * microdescriptors ({@link Microdescriptor}) that are derived from server
+ * descriptors by the directory authority and which clients download
+ * instead of server descriptors, but it persists.</p>
+ *
+ * <p>Bridges publish extra-info descriptors to the bridge authority for
+ * the same reason, to include statistics about their usage without
+ * increasing the directory protocol overhead for bridge clients. In this
+ * case, the separation of server descriptors and extra-info descriptors
+ * is slightly more relevant, because there are no microdescriptors for
+ * bridges, so that bridge clients still download server descriptors of
+ * bridges they're using. Another reason is that bridges need to include
+ * information like details of all the transports they support in their
+ * descriptors, and bridge clients using one such transport are not
+ * supposed to learn the details of the other transports.</p>
+ *
+ * <p>It's worth noting that all contents of extra-info descriptors are
+ * written and signed by relays and bridges without a third party
+ * verifying their correctness. The (bridge) directory authorities may
+ * decide to exclude dishonest servers from the network statuses they
+ * produce, but that wouldn't be reflected in extra-info descriptors.</p>
+ *
+ * @since 1.0.0
+ */
+public interface ExtraInfoDescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
+ * descriptors) or upper-case (bridge descriptors) hexadecimal
+ * characters, that is used to reference this descriptor from a server
+ * descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getExtraInfoDigest();
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that may be used to reference
+ * this descriptor from a server descriptor.
+ *
+ * @since 1.1.0
+ */
+ public String getExtraInfoDigestSha256();
+
+ /**
+ * Return the server's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the server's public identity key, encoded
+ * as 40 upper-case hexadecimal characters, that is typically used to
+ * uniquely identify the server.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * and the corresponding server descriptor were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the server's history of read bytes, or null if the descriptor
+ * does not contain a bandwidth history; older Tor versions included
+ * bandwidth histories in their server descriptors
+ * ({@link ServerDescriptor#getReadHistory()}).
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getReadHistory();
+
+ /**
+ * Return the server's history of written bytes, or null if the
+ * descriptor does not contain a bandwidth history; older Tor versions
+ * included bandwidth histories in their server descriptors
+ * ({@link ServerDescriptor#getWriteHistory()}).
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getWriteHistory();
+
+ /**
+ * Return a SHA-1 digest of the GeoIP database file used by this server
+ * to resolve client IP addresses to country codes, encoded as 40
+ * upper-case hexadecimal characters, or null if no GeoIP database
+ * digest is included.
+ *
+ * @since 1.0.0
+ */
+ public String getGeoipDbDigest();
+
+ /**
+ * Return a SHA-1 digest of the GeoIPv6 database file used by this
+ * server to resolve client IP addresses to country codes, encoded as 40
+ * upper-case hexadecimal characters, or null if no GeoIPv6 database
+ * digest is included.
+ *
+ * @since 1.0.0
+ */
+ public String getGeoip6DbDigest();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * directory request statistics interval ended, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getDirreqStatsEndMillis();
+
+ /**
+ * Return the interval length of the included directory request
+ * statistics in seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getDirreqStatsIntervalLength();
+
+ /**
+ * Return statistics on unique IP addresses requesting v2 network
+ * statuses with map keys being country codes and map values being
+ * numbers of unique IP addresses rounded up to the nearest multiple of
+ * 8, or null if no such statistics are included (which is the case with
+ * recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Ips();
+
+ /**
+ * Return statistics on unique IP addresses requesting v3 network
+ * status consensuses of any flavor with map keys being country codes
+ * and map values being numbers of unique IP addresses rounded up to the
+ * nearest multiple of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Ips();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses with
+ * map keys being country codes and map values being request numbers
+ * rounded up to the nearest multiple of 8, or null if no such
+ * statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Reqs();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor with map keys being country codes and map
+ * values being request numbers rounded up to the nearest multiple of 8,
+ * or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Reqs();
+
+ /**
+ * Return the share of requests for v2 network statuses that the server
+ * expects to receive from clients, or -1.0 if this share is not
+ * included (which is the case with recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public double getDirreqV2Share();
+
+ /**
+ * Return the share of requests for v3 network status consensuses of
+ * any flavor that the server expects to receive from clients, or -1.0
+ * if this share is not included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public double getDirreqV3Share();
+
+ /**
+ * Return statistics on responses to directory requests for v2 network
+ * statuses with map keys being response strings and map values being
+ * response numbers rounded up to the nearest multiple of 4, or null if
+ * no such statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Resp();
+
+ /**
+ * Return statistics on responses to directory requests for v3 network
+ * status consensuses of any flavor with map keys being response strings
+ * and map values being response numbers rounded up to the nearest
+ * multiple of 4, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Resp();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses to
+ * the server's directory port with map keys being statistic keys and
+ * map values being statistic values like counts or quantiles, or null
+ * if no such statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2DirectDl();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor to the server's directory port with map
+ * keys being statistic keys and map values being statistic values like
+ * counts or quantiles, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3DirectDl();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses
+ * tunneled through a circuit with map keys being statistic keys and map
+ * values being statistic values, or null if no such statistics are
+ * included (which is the case with recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2TunneledDl();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor tunneled through a circuit with map keys
+ * being statistic keys and map values being statistic values, or null
+ * if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3TunneledDl();
+
+ /**
+ * Return the directory request read history contained in this
+ * descriptor, or null if no such history is contained.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getDirreqReadHistory();
+
+ /**
+ * Return the directory request write history contained in this
+ * descriptor, or null if no such history is contained.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getDirreqWriteHistory();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * entry statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getEntryStatsEndMillis();
+
+ /**
+ * Return the interval length of the included entry statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getEntryStatsIntervalLength();
+
+ /**
+ * Return statistics on client IP addresses with map keys being country
+ * codes and map values being the number of unique IP addresses that
+ * have connected from that country rounded up to the nearest multiple
+ * of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getEntryIps();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * cell statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getCellStatsEndMillis();
+
+ /**
+ * Return the interval length of the included cell statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getCellStatsIntervalLength();
+
+ /**
+ * Return the mean number of processed cells per circuit by circuit
+ * decile starting with the loudest decile at index 0 and the quietest
+ * decile at index 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCellProcessedCells();
+
+ /**
+ * Return the mean number of cells contained in circuit queues by
+ * circuit decile starting with the loudest decile at index 0 and the
+ * quietest decile at index 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public List<Double> getCellQueuedCells();
+
+ /**
+ * Return the mean times in milliseconds that cells spend in circuit
+ * queues by circuit decile starting with the loudest decile at index 0
+ * and the quietest decile at index 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCellTimeInQueue();
+
+ /**
+ * Return the mean number of circuits included in any of the cell
+ * statistics deciles, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getCellCircuitsPerDecile();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * statistics on bi-directional connection usage ended, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getConnBiDirectStatsEndMillis();
+
+ /**
+ * Return the interval length of the included statistics on
+ * bi-directional connection usage in seconds, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getConnBiDirectStatsIntervalLength();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * less than 2 KiB/s in a 10-second interval, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectBelow();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval and at least 10 times more
+ * in read direction than in write direction, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectRead();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval and at least 10 times more
+ * in write direction than in read direction, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectWrite();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval but not 10 times more in
+ * either direction, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectBoth();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * exit statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getExitStatsEndMillis();
+
+ /**
+ * Return the interval length of the included exit statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getExitStatsIntervalLength();
+
+ /**
+ * Return statistics on KiB written to streams exiting the Tor network
+ * by target TCP port with map keys being string representations of
+ * ports (or {@code "other"}) and map values being KiB rounded up to the
+ * next full KiB, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitKibibytesWritten();
+
+ /**
+ * Return statistics on KiB read from streams exiting the Tor network
+ * by target TCP port with map keys being string representations of
+ * ports (or {@code "other"}) and map values being KiB rounded up to the
+ * next full KiB, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitKibibytesRead();
+
+ /**
+ * Return statistics on opened streams exiting the Tor network by
+ * target TCP port with map keys being string representations of ports
+ * (or {@code "other"}) and map values being the number of opened
+ * streams, rounded up to the nearest multiple of 4, or null if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitStreamsOpened();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * "geoip" statistics interval started, or -1 if no such statistics are
+ * included (which is the case except for very old Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public long getGeoipStartTimeMillis();
+
+ /**
+ * Return statistics on the origin of client IP addresses with map keys
+ * being country codes and map values being the number of unique IP
+ * addresses that have connected from that country between the start of
+ * the statistics interval and the descriptor publication time rounded
+ * up to the nearest multiple of 8, or null if no such statistics are
+ * included (which is the case except for very old Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getGeoipClientOrigins();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * bridge statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getBridgeStatsEndMillis();
+
+ /**
+ * Return the interval length of the included bridge statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getBridgeStatsIntervalLength();
+
+ /**
+ * Return statistics on bridge client IP addresses by country with map
+ * keys being country codes and map values being the number of unique IP
+ * addresses that have connected from that country rounded up to the
+ * nearest multiple of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIps();
+
+ /**
+ * Return statistics on bridge client IP addresses by IP version with
+ * map keys being protocol families, e.g., {@code "v4"} or {@code "v6"},
+ * and map values being the number of unique IP addresses rounded up to
+ * the nearest multiple of 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIpVersions();
+
+ /**
+ * Return statistics on bridge client IP addresses by transport with
+ * map keys being pluggable transport names, e.g., {@code "obfs2"} or
+ * {@code "obfs3"} for known transports, {@code "<OR>"} for the default
+ * onion routing protocol, or {@code "<??>"} for an unknown transport,
+ * and map values being the number of unique IP addresses rounded up to
+ * the nearest multiple of 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIpTransports();
+
+ /**
+ * Return the (possibly empty) list of pluggable transports supported
+ * by this server.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getTransports();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * hidden-service statistics interval ended, or -1 if no such statistics
+ * are included.
+ *
+ * @since 1.1.0
+ */
+ public long getHidservStatsEndMillis();
+
+ /**
+ * Return the interval length of the included hidden-service statistics
+ * in seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public long getHidservStatsIntervalLength();
+
+ /**
+ * Return the approximate number of RELAY cells seen in either
+ * direction on a circuit after receiving and successfully processing a
+ * RENDEZVOUS1 cell, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Double getHidservRendRelayedCells();
+
+ /**
+ * Return the obfuscation parameters applied to the original
+ * measurement value of RELAY cells seen in either direction on a
+ * circuit after receiving and successfully processing a RENDEZVOUS1
+ * cell, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Double> getHidservRendRelayedCellsParameters();
+
+ /**
+ * Return the approximate number of unique hidden-service identities
+ * seen in descriptors published to and accepted by this hidden-service
+ * directory, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Double getHidservDirOnionsSeen();
+
+ /**
+ * Return the obfuscation parameters applied to the original
+ * measurement value of unique hidden-service identities seen in
+ * descriptors published to and accepted by this hidden-service
+ * directory, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Double> getHidservDirOnionsSeenParameters();
+
+ /**
+ * Return the RSA-1024 signature of the PKCS1-padded descriptor digest,
+ * taken from the beginning of the router line through the newline after
+ * the router-signature line, or null if the descriptor doesn't contain
+ * a signature (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignature();
+
+ /**
+ * Return the Ed25519 certificate in PEM format, or null if the
+ * descriptor doesn't contain one.
+ *
+ * @since 1.1.0
+ */
+ public String getIdentityEd25519();
+
+ /**
+ * Return the Ed25519 master key, encoded as 43 base64 characters
+ * without padding characters, which was either parsed from the optional
+ * {@code "master-key-ed25519"} line or derived from the (likewise
+ * optional) Ed25519 certificate following the
+ * {@code "identity-ed25519"} line, or null if the descriptor contains
+ * neither Ed25519 master key nor Ed25519 certificate.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+
+ /**
+ * Return the Ed25519 signature of the SHA-256 digest of the entire
+ * descriptor, encoded as 86 base64 characters without padding
+ * characters, from the first character up to and including the first
+ * space after the {@code "router-sig-ed25519"} string, prefixed with
+ * the string {@code "Tor router descriptor signature v1"}.
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignatureEd25519();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java b/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java
new file mode 100644
index 0000000..c54e48f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java
@@ -0,0 +1,22 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Thrown if a descriptor source implementation class cannot be found,
+ * instantiated, or accessed.
+ *
+ * @see DescriptorSourceFactory
+ * @since 1.0.0
+ */
+@SuppressWarnings("serial")
+public class ImplementationNotAccessibleException
+ extends RuntimeException {
+
+ public ImplementationNotAccessibleException(String string,
+ Throwable ex) {
+ super(string, ex);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/Microdescriptor.java b/src/main/java/org/torproject/descriptor/Microdescriptor.java
new file mode 100644
index 0000000..f19b7df
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/Microdescriptor.java
@@ -0,0 +1,135 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a relay microdescriptor.
+ *
+ * <p>A microdescriptor is a stripped-down version of a relay server
+ * descriptor ({@link RelayServerDescriptor}) generated by the directory
+ * authorities by extracting and/or transforming relay server descriptor
+ * contents following strict rules without adding the authority's opinion
+ * about the relay. Microdescriptors are referenced from microdescriptor
+ * consensuses ({@link RelayNetworkStatusConsensus}) and downloaded by
+ * clients to make path-selection decisions and to build circuits.
+ * Microdescriptors contain only the most relevant parts that clients care
+ * about. Microdescriptors are expected to be relatively static and only
+ * change about once per week.</p>
+ *
+ * @since 1.0.0
+ */
+public interface Microdescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that is used to reference this
+ * descriptor from a vote or microdescriptor consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getMicrodescriptorDigest();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used to encrypt CREATE
+ * cells for this server, or null if the descriptor doesn't contain an
+ * onion key.
+ *
+ * @since 1.0.0
+ */
+ public String getOnionKey();
+
+ /**
+ * Return the curve25519 public key, encoded as 43 base64 characters
+ * without padding characters, that is used for the ntor circuit
+ * extended handshake, or null if the descriptor didn't contain an
+ * ntor-onion-key line.
+ *
+ * @since 1.0.0
+ */
+ public String getNtorOnionKey();
+
+ /**
+ * Return IP addresses and TCP ports where this server accepts TLS
+ * connections for the main OR protocol, or an empty list if the server
+ * does not support additional addresses or ports; entries are given in
+ * the order as they are listed in the descriptor; IPv4 addresses are
+ * given in dotted-quad format, IPv6 addresses use the colon-separated
+ * hexadecimal format surrounded by square brackets, and TCP ports are
+ * separated from the IP address using a colon.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return nicknames, $-prefixed identity fingerprints, or tuples of the
+ * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
+ * of servers contained in this server's family, or null if the
+ * descriptor does not contain a family line.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getFamilyEntries();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv4 port summary, or null if the descriptor didn't contain an
+ * IPv4 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv4 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getDefaultPolicy();
+
+ /**
+ * Return the port list of the IPv4 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv4 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv4 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getPortList();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv6 port summary, or null if the descriptor didn't contain an
+ * IPv6 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6DefaultPolicy();
+
+ /**
+ * Return the port list of the IPv6 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv6 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6PortList();
+
+ /**
+ * Return a SHA-1 digest of the server's RSA-1024 identity key, encoded
+ * as 27 base64 characters without padding characters, that is only
+ * included to prevent collisions between microdescriptors, or null if
+ * no such digest is included.
+ *
+ * @since 1.1.0
+ */
+ public String getRsa1024Identity();
+
+ /**
+ * Return a SHA-256 digest of the server's Ed25519 identity key,
+ * encoded as 43 base64 characters without padding characters, that is
+ * only included to prevent collisions between microdescriptors, or null
+ * if no such digest is included.
+ *
+ * @since 1.1.0
+ */
+ public String getEd25519Identity();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
new file mode 100644
index 0000000..43b3175
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
@@ -0,0 +1,177 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * Contains an entry in a network status in the version 2 or 3 directory
+ * protocol or in a bridge network status.
+ *
+ * <p>A network status entry is not a descriptor type of its own but is
+ * part of a network status in the version 2 directory protocol
+ * ({@link RelayNetworkStatus}), a vote ({@link RelayNetworkStatusVote})
+ * or flavored/unflavored consensus (@link RelayNetworkStatusConsensus})
+ * in the version 3 directory protocol, or a bridge network status
+ * ({@link BridgeNetworkStatus}). Entries in signed directories in the
+ * version 1 directory protocol are represented by router status entries
+ * ({@link RouterStatusEntry}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface NetworkStatusEntry {
+
+ /**
+ * Return the raw network status entry bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getStatusEntryBytes();
+
+ /**
+ * Return the server nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the server's identity key, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the SHA-1 digest of the server descriptor, or null if the
+ * containing network status does not contain server descriptor
+ * references, like a microdesc consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getDescriptor();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the server's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this server accepts TLS connections for
+ * the main OR protocol.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return the TCP port where this server accepts directory-related HTTP
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return the (possibly empty) set of microdescriptor digests if the
+ * containing network status is a vote or microdesc consensus, or null
+ * otherwise.
+ *
+ * @since 1.0.0
+ */
+ public Set<String> getMicrodescriptorDigests();
+
+ /**
+ * Return additional IP addresses and TCP ports where this server
+ * accepts TLS connections for the main OR protocol, or an empty list if
+ * the network status doesn't contain any such additional addresses and
+ * ports.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return the relay flags assigned to this server, or null if the
+ * status entry didn't contain any relay flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getFlags();
+
+ /**
+ * Return the Tor software version, or null if the status entry didn't
+ * contain version information.
+ *
+ * @since 1.0.0
+ */
+ public String getVersion();
+
+ /**
+ * Return the bandwidth weight of this server or -1 if the status entry
+ * didn't contain a bandwidth line.
+ *
+ * @since 1.0.0
+ */
+ public long getBandwidth();
+
+ /**
+ * Return the measured bandwidth or -1 if the status entry either
+ * didn't contain bandwidth information or didn't contain an indication
+ * that this information is based on measured bandwidth.
+ *
+ * @since 1.0.0
+ */
+ public long getMeasured();
+
+ /**
+ * Return whether the status entry is yet unmeasured by the bandwidth
+ * authorities; only included in consensuses using method 17 or higher.
+ *
+ * @since 1.0.0
+ */
+ public boolean getUnmeasured();
+
+ /**
+ * Return the default policy of the port summary, which can be either
+ * {@code "accept"} or {@code "reject"}, or null if the status entry
+ * didn't contain an exit policy summary.
+ *
+ * @since 1.0.0
+ */
+ public String getDefaultPolicy();
+
+ /**
+ * Return the list of ports or port intervals of the exit port summary,
+ * or null if the status entry didn't contain an exit policy summary.
+ *
+ * @since 1.0.0
+ */
+ public String getPortList();
+
+ /**
+ * Return the server's Ed25519 master key, encoded as 43 base64
+ * characters without padding characters, "none" if the relay doesn't
+ * have an Ed25519 identity, or null if the status entry didn't contain
+ * this information or if the status is not a vote.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayDirectory.java b/src/main/java/org/torproject/descriptor/RelayDirectory.java
new file mode 100644
index 0000000..8f3e58b
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayDirectory.java
@@ -0,0 +1,104 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a signed directory in the version 1 directory protocol.
+ *
+ * <p>Directory authorities in the (long outdated) version 1 of the
+ * directory protocol served signed directory documents containing a list
+ * of signed server descriptors ({@link ServerDescriptor}) along with
+ * short summaries of the status of each server
+ * ({@link RouterStatusEntry}).</p>
+ *
+ * <p>Clients in that version of the directory protocol would fetch this
+ * signed directory to get up-to-date information on the state of the
+ * network and be certain that the list was attested by a trusted
+ * directory authority.</p>
+ *
+ * <p>Signed directories in the version 1 directory protocol have first
+ * been superseded by network status documents in the version 2 directory
+ * protocol ({@link RelayNetworkStatus}) and later by network status
+ * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
+ * directory protocol.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayDirectory extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this authority
+ * as long-term identity key and to sign network statuses, or null if
+ * this key is not included in the descriptor header.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return recommended Tor versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedSoftware();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectorySignature();
+
+ /**
+ * Return router status entries, one for each contained relay.
+ *
+ * @since 1.0.0
+ */
+ public List<RouterStatusEntry> getRouterStatusEntries();
+
+ /**
+ * Return a list of server descriptors contained in the signed
+ * directory.
+ *
+ * @since 1.0.0
+ */
+ public List<ServerDescriptor> getServerDescriptors();
+
+ /**
+ * Return a (very likely empty) list of exceptions from parsing the
+ * contained server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public List<Exception> getServerDescriptorParseExceptions();
+
+ /**
+ * Return the directory nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the SHA-1 directory digest, encoded as 40 lower-case
+ * hexadecimal characters, that the directory authority used to sign the
+ * directory.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectoryDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java
new file mode 100644
index 0000000..73f8438
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java
@@ -0,0 +1,21 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a relay extra-info descriptor.
+ *
+ * <p>Relay extra-info descriptors share many contents with sanitized
+ * bridge extra-info descriptors ({@link BridgeExtraInfoDescriptor}),
+ * which is why they share a common superinterface
+ * ({@link ExtraInfoDescriptor}). The main purpose of having two
+ * subinterfaces is being able to distinguish descriptor types more
+ * easily.</p>
+ *
+ * @since 1.1.0
+ */
+public interface RelayExtraInfoDescriptor extends ExtraInfoDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java
new file mode 100644
index 0000000..db3ddac
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java
@@ -0,0 +1,176 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status document in the version 2 directory protocol.
+ *
+ * <p>Directory authorities in the (outdated) version 2 of the directory
+ * protocol published signed network status documents. Each network
+ * status listed, for every relay in the network
+ * ({@link NetworkStatusEntry}): a hash of its identity key, a hash of its
+ * most recent server descriptor, and a summary of what the authority
+ * believed about its status.</p>
+ *
+ * <p>Clients would download the authorities' network status documents in
+ * turn, and believe statements about routers iff they were attested to by
+ * more than half of the authorities.</p>
+ *
+ * <p>Network status documents in the version 2 directory protocol
+ * supersede signed directories in the version 1 directory protocol
+ * ({@link RelayDirectory}) and have been superseded by network status
+ * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
+ * directory protocol.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatus extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 2.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.0.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the descriptor does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirport();
+
+ /**
+ * Return a SHA-1 digest of the authority's public identity key,
+ * encoded as 40 upper-case hexadecimal characters, which is also used
+ * to sign network statuses.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this authority
+ * as long-term identity key and to sign network statuses.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * authority does not recommend server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * authority does not recommend client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the set of flags that this directory assigns to relays, or
+ * null if the status does not assign such flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getDirOptions();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectorySignature();
+
+ /**
+ * Return the SHA-1 status digest, encoded as 40 lower-case hexadecimal
+ * characters, that the directory authority used to sign the network
+ * status.
+ *
+ * @since 1.0.0
+ */
+ public String getStatusDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
new file mode 100644
index 0000000..15fdaca
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -0,0 +1,223 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status consensus in the version 3 directory protocol.
+ *
+ * <p>Directory authorities in the version 3 of the directory protocol
+ * periodically generate a view of the current descriptors and status for
+ * known relays and send a signed summary of this view to the other
+ * authorities ({@link RelayNetworkStatusVote}). The authorities compute
+ * the result of this vote and sign a network status consensus containing
+ * the result of the vote, which is this document.</p>
+ *
+ * <p>Clients use consensus documents to find out when their list of
+ * relays is out-of-date by looking at the contained network status
+ * entries ({@link NetworkStatusEntry}). If it is, they download any
+ * missing server descriptors ({@link ServerDescriptor}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatusConsensus extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 3 or
+ * higher.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the consensus flavor name, which denotes the variant of the
+ * original, unflavored consensus, encoded as a string of alphanumeric
+ * characters and dashes, or null if this descriptor is the unflavored
+ * consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getConsensusFlavor();
+
+ /**
+ * Return the consensus method number of this descriptor, which is the
+ * highest consensus method supported by more than 2/3 of voting
+ * authorities, or 0 if no consensus method is contained in the
+ * descriptor.
+ *
+ * @since 1.0.0
+ */
+ public int getConsensusMethod();
+
+ /**
+ * Return the time in milliseconds since the epoch at which this
+ * descriptor became valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidAfterMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which this
+ * descriptor is the freshest that is available.
+ *
+ * @since 1.0.0
+ */
+ public long getFreshUntilMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which this
+ * descriptor was valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidUntilMillis();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect votes from the other authorities when producing the
+ * next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getVoteSeconds();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect signatures from the other authorities when producing
+ * the next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getDistSeconds();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * consensus does not contain an opinion about server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * consensus does not contain an opinion about client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * Return a list of software packages and their versions together with a
+ * URL and one or more digests in the format <code>PackageName Version
+ * URL DIGESTS</code> that are known by at least three directory
+ * authorities and agreed upon by the majority of directory authorities,
+ * or null if the consensus does not contain package information.
+ *
+ * @since 1.3.0
+ */
+ public List<String> getPackageLines();
+
+ /**
+ * Return known relay flags in this descriptor that were contained in
+ * enough votes for this consensus to be an authoritative opinion for
+ * these relay flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getKnownFlags();
+
+ /**
+ * Return consensus parameters contained in this descriptor with map
+ * keys being case-sensitive parameter identifiers and map values being
+ * parameter values, or null if the consensus doesn't contain consensus
+ * parameters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getConsensusParams();
+
+ /**
+ * Return directory source entries for each directory authority that
+ * contributed to the consensus, with map keys being SHA-1 digests of
+ * the authorities' identity keys in the version 3 directory protocol,
+ * encoded as 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirSourceEntry> getDirSourceEntries();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return directory signatures of this consensus, with map keys being
+ * SHA-1 digests of the authorities' identity keys in the version 3
+ * directory protocol, encoded as 40 upper-case hexadecimal characters.
+ *
+ * @deprecated Replaced by {@link #getSignatures()} which permits an
+ * arbitrary number of signatures made by an authority using the same
+ * identity key digest and different algorithms.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirectorySignature> getDirectorySignatures();
+
+ /**
+ * Return the list of signatures contained in this consensus.
+ *
+ * @since 1.3.0
+ */
+ public List<DirectorySignature> getSignatures();
+
+ /**
+ * Return optional weights to be applied to router bandwidths during
+ * path selection with map keys being case-sensitive weight identifiers
+ * and map values being weight values, or null if the consensus doesn't
+ * contain such weights.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBandwidthWeights();
+
+ /**
+ * Return the SHA-1 digest of this consensus, encoded as 40 upper-case
+ * hexadecimal characters that directory authorities use to sign the
+ * consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getConsensusDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
new file mode 100644
index 0000000..1f77db6
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -0,0 +1,408 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status vote in the version 3 directory protocol.
+ *
+ * <p>Directory authorities in the version 3 of the directory protocol
+ * periodically generate a view of the current descriptors and status for
+ * known relays and send a signed summary of this view to the other
+ * authorities, which is this document. The authorities compute the
+ * result of this vote and sign a network status consensus containing the
+ * result of the vote ({@link RelayNetworkStatusConsensus}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatusVote extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 3 or
+ * higher.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the list of consensus method numbers supported by this
+ * authority, or null if the descriptor doesn't say so, which would mean
+ * that only method 1 is supported.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getConsensusMethods();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch at which the
+ * consensus is supposed to become valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidAfterMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which the
+ * consensus is supposed to be the freshest that is available.
+ *
+ * @since 1.0.0
+ */
+ public long getFreshUntilMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which the
+ * consensus is supposed to be valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidUntilMillis();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect votes from the other authorities when producing the
+ * next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getVoteSeconds();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect signatures from the other authorities when producing
+ * the next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getDistSeconds();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * authority does not recommend server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * authority does not recommend client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * Return a list of software packages and their versions together with a
+ * URL and one or more digests in the format <code>PackageName Version
+ * URL DIGESTS</code> that are known by this directory authority, or
+ * null if this descriptor does not contain package information.
+ *
+ * @since 1.3.0
+ */
+ public List<String> getPackageLines();
+
+ /**
+ * Return known relay flags by this authority.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getKnownFlags();
+
+ /**
+ * Return the minimum uptime in seconds that this authority requires
+ * for assigning the Stable flag, or -1 if the authority doesn't report
+ * this value.
+ *
+ * @since 1.0.0
+ */
+ public long getStableUptime();
+
+ /**
+ * Return the minimum MTBF (mean time between failure) that this
+ * authority requires for assigning the Stable flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getStableMtbf();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Fast flag, or -1 if the authority doesn't report this
+ * value.
+ *
+ * @since 1.0.0
+ */
+ public long getFastBandwidth();
+
+ /**
+ * Return the minimum WFU (weighted fractional uptime) in percent that
+ * this authority requires for assigning the Guard flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public double getGuardWfu();
+
+ /**
+ * Return the minimum weighted time in seconds that this authority
+ * needs to know about a relay before assigning the Guard flag, or -1 if
+ * the authority doesn't report this information.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardTk();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardBandwidthIncludingExits();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can not be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardBandwidthExcludingExits();
+
+ /**
+ * Return 1 if the authority has measured enough MTBF info to use the
+ * MTBF requirement instead of the uptime requirement for assigning the
+ * Stable flag, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public int getEnoughMtbfInfo();
+
+ /**
+ * Return 1 if the authority has enough measured bandwidths that it'll
+ * ignore the advertised bandwidth claims of routers without measured
+ * bandwidth, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getIgnoringAdvertisedBws();
+
+ /**
+ * Return consensus parameters contained in this descriptor with map
+ * keys being case-sensitive parameter identifiers and map values being
+ * parameter values, or null if the authority doesn't include consensus
+ * parameters in its vote.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getConsensusParams();
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.2.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the descriptor does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirport();
+
+ /**
+ * Return the TCP port where this authority accepts TLS connections for
+ * the main OR protocol, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getOrport();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters, or null if no contact information is included
+ * in the descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the version of the directory key certificate used by this
+ * authority, which must be 3 or higher.
+ *
+ * @since 1.0.0
+ */
+ public int getDirKeyCertificateVersion();
+
+ /**
+ * Return the SHA-1 digest for an obsolete authority identity key still
+ * used by this authority to keep older clients working, or null if this
+ * authority does not use such a key.
+ *
+ * @since 1.0.0
+ */
+ public String getLegacyDirKey();
+
+ /**
+ * Return the authority's identity key in PEM format.
+ *
+ * @since 1.2.0
+ */
+ public String getDirIdentityKey();
+
+ /**
+ * Return the time in milliseconds since the epoch when the authority's
+ * signing key and corresponding key certificate were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch after which the
+ * authority's signing key is no longer valid.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyExpiresMillis();
+
+ /**
+ * Return the authority's signing key in PEM format.
+ *
+ * @since 1.2.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return the SHA-1 digest of the authority's signing key, encoded as
+ * 40 upper-case hexadecimal characters, or null if this digest cannot
+ * be obtained from the directory signature.
+ *
+ * @deprecated Removed in order to be more explicit that authorities may
+ * use different digest algorithms than "sha1"; see
+ * {@link #getSignatures()} and
+ * {@link DirectorySignature#getSigningKeyDigest()} for
+ * alternatives.
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKeyDigest();
+
+ /**
+ * Return the signature of the authority's identity key made using the
+ * authority's signing key, or null if the vote does not contain such a
+ * signature.
+ *
+ * @since 1.2.0
+ */
+ public String getDirKeyCrosscert();
+
+ /**
+ * Return the certificate signature from the initial item
+ * "dir-key-certificate-version" until the final item
+ * "dir-key-certification", signed with the authority identity key.
+ *
+ * @since 1.2.0
+ */
+ public String getDirKeyCertification();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return the directory signature of this vote, with the single map key
+ * being the SHA-1 digest of the authority's identity key in the version
+ * 3 directory protocol, encoded as 40 upper-case hexadecimal
+ * characters.
+ *
+ * @deprecated Replaced by {@link #getSignatures()} which permits an
+ * arbitrary number of signatures made by the authority using the same
+ * identity key digest and different algorithms.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirectorySignature> getDirectorySignatures();
+
+ /**
+ * Return a list of signatures contained in this vote, which is
+ * typically a single signature made by the authority but which may also
+ * be more than one signature made with different keys or algorithms.
+ *
+ * @since 1.3.0
+ */
+ public List<DirectorySignature> getSignatures();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java b/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java
new file mode 100644
index 0000000..6ef3140
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java
@@ -0,0 +1,20 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a relay server descriptor.
+ *
+ * <p>Relay server descriptors share many contents with sanitized bridge
+ * server descriptors ({@link BridgeServerDescriptor}), which is why they
+ * share a common superinterface ({@link ServerDescriptor}). The main
+ * purpose of having two subinterfaces is being able to distinguish
+ * descriptor types more easily.</p>
+ *
+ * @since 1.1.0
+ */
+public interface RelayServerDescriptor extends ServerDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RouterStatusEntry.java b/src/main/java/org/torproject/descriptor/RouterStatusEntry.java
new file mode 100644
index 0000000..f9a56db
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RouterStatusEntry.java
@@ -0,0 +1,51 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a router status entry contained in a signed directory in the
+ * version 1 directory protocol.
+ *
+ * <p>Directory authorities in the (long outdated) version 1 of the
+ * directory protocol included router status entries with short summaries
+ * of the status of each server in the signed directories they produced
+ * ({@link RelayDirectory}). These entries contained references to server
+ * descriptors published by relays together with the authorities' opinion
+ * on whether relays were verified and live.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RouterStatusEntry {
+
+ /**
+ * Return the relay nickname consisting of 1 to 19 alphanumeric
+ * characters, or null if the relay is unverified.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the relay's identity key, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return whether the relay is verified.
+ *
+ * @since 1.0.0
+ */
+ public boolean isVerified();
+
+ /**
+ * Return whether the relay is live.
+ *
+ * @since 1.0.0
+ */
+ public boolean isLive();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ServerDescriptor.java b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
new file mode 100644
index 0000000..d1af421
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
@@ -0,0 +1,435 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a relay or sanitized bridge server descriptor.
+ *
+ * <p>Relays publish server descriptors to the directory authorities to
+ * register in the network. Server descriptors contain information about
+ * the capabilities of a server, like their exit policy, that clients use
+ * to select servers for their circuits (along with information provided
+ * by directory authorities on reachability, stability, and capacity of
+ * servers). Server descriptors also contain network addresses and
+ * cryptographic material that clients use to build circuits.</p>
+ *
+ * <p>Prior to the introduction of microdescriptors
+ * ({@link Microdescriptor}), the directory authorities included
+ * cryptographic digests of server descriptors in network statuses
+ * ({@link RelayNetworkStatusConsensus}) and clients downloaded all
+ * referenced server descriptors. Nowadays, the directory authorities
+ * derive microdescriptors from server descriptors and reference those
+ * in network statuses, and clients only download microdescriptors instead
+ * of server descriptors.</p>
+ *
+ * <p>Bridges publish server descriptors to the bridge directory
+ * authority, also to announce themselves in the network. The bridge
+ * directory authority compiles a list of available bridges
+ * ({@link BridgeNetworkStatus}) for the bridge distribution service
+ * BridgeDB. There are no microdescriptors for bridges, so that bridge
+ * clients still rely on downloading bridge server descriptors directly
+ * from the bridge they're connecting to.</p>
+ *
+ * <p>It's worth noting that all contents of server descriptors are
+ * written and signed by relays and bridges without a third party
+ * verifying their correctness. The (bridge) directory authorities may
+ * decide to exclude dishonest servers from the network statuses they
+ * produce, but that wouldn't be reflected in server descriptors.</p>
+ *
+ * @since 1.0.0
+ */
+public interface ServerDescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
+ * descriptors) or upper-case (bridge descriptors) hexadecimal
+ * characters, that is used to reference this descriptor from a network
+ * status descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getServerDescriptorDigest();
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that may be used to reference
+ * this server descriptor from a network status descriptor.
+ *
+ * @since 1.1.0
+ */
+ public String getServerDescriptorDigestSha256();
+
+ /**
+ * Return the server's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the server's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this server accepts TLS connections for
+ * the main OR protocol, or 0 if the server does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return the TCP port where this server accepts SOCKS connections,
+ * which is deprecated and should always be 0.
+ *
+ * @since 1.0.0
+ */
+ public int getSocksPort();
+
+ /**
+ * Return the TCP port where this server accepts directory-related HTTP
+ * connections, or 0 if the server does not accept such connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return IP addresses and TCP ports where this server accepts TLS
+ * connections for the main OR protocol, or an empty list if the server
+ * does not support additional addresses or ports; entries are given in
+ * the order as they are listed in the descriptor; IPv4 addresses are
+ * given in dotted-quad format, IPv6 addresses use the colon-separated
+ * hexadecimal format surrounded by square brackets, and TCP ports are
+ * separated from the IP address using a colon.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return the average bandwidth in bytes per second that the server is
+ * willing to sustain over long periods.
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthRate();
+
+ /**
+ * Return the burst bandwidth in bytes per second that the server is
+ * willing to sustain in very short intervals.
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthBurst();
+
+ /**
+ * Return the observed bandwidth in bytes per second as an estimate of
+ * the capacity that the server can handle, or -1 if the descriptor
+ * doesn't contain an observed bandwidth value (which is the case for
+ * Tor 0.0.8 or older).
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthObserved();
+
+ /**
+ * Return a human-readable string describing the Tor software version
+ * and the operating system of this server, which may contain non-ASCII
+ * characters, typically written as {@code "Tor $version on $system"},
+ * or null if this descriptor does not contain a platform line.
+ *
+ * @since 1.0.0
+ */
+ public String getPlatform();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * and the corresponding extra-info descriptor were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return a SHA-1 digest of the server's public identity key, encoded
+ * as 40 upper-case hexadecimal characters (without spaces after every 4
+ * characters as opposed to the encoding in the descriptor), that is
+ * typically used to uniquely identify the server, or null if this
+ * descriptor does not contain a fingerprint line.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return whether the server was hibernating when this descriptor was
+ * published and should not be used to build circuits.
+ *
+ * @since 1.0.0
+ */
+ public boolean isHibernating();
+
+ /**
+ * Return the number of seconds that the server process has been
+ * running (which might even be negative in a few descriptors due to a
+ * bug that was fixed in Tor 0.1.2.7-alpha), or null if the descriptor
+ * does not contain an uptime line.
+ *
+ * @since 1.0.0
+ */
+ public Long getUptime();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used to encrypt CREATE
+ * cells for this server, or null if the descriptor doesn't contain an
+ * onion key (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getOnionKey();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this server as
+ * long-term identity key, or null if the descriptor doesn't contain a
+ * signing key (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKey();
+
+ /**
+ * Return the server's exit policy consisting of one or more accept or
+ * reject rules that the server follows when deciding whether to allow a
+ * new stream to a given IP address and TCP port.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getExitPolicyLines();
+
+ /**
+ * Return the RSA-1024 signature of the PKCS1-padded descriptor digest,
+ * taken from the beginning of the router line through the newline after
+ * the router-signature line, or null if the descriptor doesn't contain
+ * a signature (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getRouterSignature();
+
+ /**
+ * Return the contact information for this server, which may contain
+ * non-ASCII characters, or null if no contact information is included
+ * in the descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getContact();
+
+ /**
+ * Return nicknames, $-prefixed identity fingerprints, or tuples of the
+ * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
+ * of servers contained in this server's family, or null if the
+ * descriptor does not contain a family line.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getFamilyEntries();
+
+ /**
+ * Return the server's history of read bytes, or null if the descriptor
+ * does not contain a bandwidth history; current Tor versions include
+ * bandwidth histories in their extra-info descriptors
+ * ({@link ExtraInfoDescriptor#getReadHistory()}), not in their server
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getReadHistory();
+
+ /**
+ * Return the server's history of written bytes, or null if the
+ * descriptor does not contain a bandwidth history; current Tor versions
+ * include bandwidth histories in their extra-info descriptors
+ * ({@link ExtraInfoDescriptor#getWriteHistory()}), not in their server
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getWriteHistory();
+
+ /**
+ * Return true if the server uses the enhanced DNS logic, or false if
+ * doesn't use it or doesn't include an eventdns line in its
+ * descriptor; current Tor versions should be presumed to have the evdns
+ * backend.
+ *
+ * @since 1.0.0
+ */
+ public boolean getUsesEnhancedDnsLogic();
+
+ /**
+ * Return whether this server is a directory cache that provides
+ * extra-info descriptors.
+ *
+ * @since 1.0.0
+ */
+ public boolean getCachesExtraInfo();
+
+ /**
+ * Return the SHA-1 digest of the server's extra-info descriptor,
+ * encoded as 40 upper-case hexadecimal characters, or null if the
+ * server did not upload a corresponding extra-info descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getExtraInfoDigest();
+
+ /**
+ * Return the SHA-256 digest of the server's extra-info descriptor,
+ * encoded as 43 base64 characters without padding characters, or null
+ * if the server either did not upload a corresponding extra-info
+ * descriptor or did not refer to it using a SHA-256 digest.
+ *
+ * @since 1.1.0
+ */
+ public String getExtraInfoDigestSha256();
+
+ /**
+ * Return the list of hidden service descriptor version numbers that
+ * this server stores and serves, or null if it doesn't store and serve
+ * any hidden service descriptors.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getHiddenServiceDirVersions();
+
+ /**
+ * Return the list of link protocol versions that this server
+ * supports.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getLinkProtocolVersions();
+
+ /**
+ * Return the list of circuit protocol versions that this server
+ * supports.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCircuitProtocolVersions();
+
+ /**
+ * Return whether this server allows single-hop circuits to make exit
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public boolean getAllowSingleHopExits();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv6 port summary, or null if the descriptor didn't contain an
+ * IPv6 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6DefaultPolicy();
+
+ /**
+ * Return the port list of the IPv6 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv6 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6PortList();
+
+ /**
+ * Return the curve25519 public key, encoded as 43 base64 characters
+ * without padding characters, that is used for the ntor circuit
+ * extended handshake, or null if the descriptor didn't contain an
+ * ntor-onion-key line. */
+ public String getNtorOnionKey();
+
+ /**
+ * Return the Ed25519 certificate in PEM format, or null if the
+ * descriptor doesn't contain one.
+ *
+ * @since 1.1.0
+ */
+ public String getIdentityEd25519();
+
+ /**
+ * Return the Ed25519 master key, encoded as 43 base64 characters
+ * without padding characters, which was either parsed from the optional
+ * {@code "master-key-ed25519"} line or derived from the (likewise
+ * optional) Ed25519 certificate following the
+ * {@code "identity-ed25519"} line, or null if the descriptor contains
+ * neither Ed25519 master key nor Ed25519 certificate.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+
+ /**
+ * Return the Ed25519 signature of the SHA-256 digest of the entire
+ * descriptor, encoded as 86 base64 characters without padding
+ * characters, from the first character up to and including the first
+ * space after the {@code "router-sig-ed25519"} string, prefixed with
+ * the string {@code "Tor router descriptor signature v1"}.
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignatureEd25519();
+
+ /**
+ * Return an RSA-1024 signature in PEM format, generated using the
+ * server's onion key, that proves that the party creating the
+ * descriptor had control over the private key corresponding to the
+ * onion key, or null if the descriptor does not contain such a
+ * signature.
+ *
+ * @since 1.1.0
+ */
+ public String getOnionKeyCrosscert();
+
+ /**
+ * Return an Ed25519 signature in PEM format, generated using the
+ * server's ntor onion key, that proves that the party creating the
+ * descriptor had control over the private key corresponding to the ntor
+ * onion key, or null if the descriptor does not contain such a
+ * signature.
+ *
+ * @since 1.1.0
+ */
+ public String getNtorOnionKeyCrosscert();
+
+ /**
+ * Return the sign of the Ed25519 public key corresponding to the ntor
+ * onion key as 0 or 1, or -1 if the descriptor does not contain this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getNtorOnionKeyCrosscertSign();
+
+ /**
+ * Return whether the server accepts "tunneled" directory requests using
+ * a BEGIN_DIR cell over the server's OR port.
+ *
+ * @since 1.3.0
+ */
+ public boolean getTunnelledDirServer();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/TorperfResult.java b/src/main/java/org/torproject/descriptor/TorperfResult.java
new file mode 100644
index 0000000..188200b
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/TorperfResult.java
@@ -0,0 +1,215 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+
+/**
+ * Contains performance measurement results from making simple HTTP
+ * requests over the Tor network.
+ *
+ * <p>The performance measurement service Torperf publishes performance
+ * data from making simple HTTP requests over the Tor network. Torperf
+ * uses a trivial SOCKS client to download files of various sizes over the
+ * Tor network and notes how long substeps take.</p>
+ *
+ * @since 1.0.0
+ */
+public interface TorperfResult extends Descriptor {
+
+ /**
+ * Return all unrecognized keys together with their values, or null if
+ * all keys were recognized.
+ *
+ * @since 1.2.0
+ */
+ public SortedMap<String, String> getUnrecognizedKeys();
+
+ /**
+ * Return the configured name of the data source.
+ *
+ * @since 1.0.0
+ */
+ public String getSource();
+
+ /**
+ * Return the configured file size in bytes.
+ *
+ * @since 1.0.0
+ */
+ public int getFileSize();
+
+ /**
+ * Return the time in milliseconds since the epoch when the connection
+ * process started.
+ *
+ * @since 1.0.0
+ */
+ public long getStartMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the socket was
+ * created.
+ *
+ * @since 1.0.0
+ */
+ public long getSocketMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the socket was
+ * connected.
+ *
+ * @since 1.0.0
+ */
+ public long getConnectMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when SOCKS 5
+ * authentication methods have been negotiated.
+ *
+ * @since 1.0.0
+ */
+ public long getNegotiateMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the SOCKS
+ * request was sent.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the SOCKS
+ * response was received.
+ *
+ * @since 1.0.0
+ */
+ public long getResponseMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the HTTP
+ * request was written.
+ *
+ * @since 1.0.0
+ */
+ public long getDataRequestMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the first
+ * response was received.
+ *
+ * @since 1.0.0
+ */
+ public long getDataResponseMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the payload was
+ * complete.
+ *
+ * @since 1.0.0
+ */
+ public long getDataCompleteMillis();
+
+ /**
+ * Return the total number of bytes written.
+ *
+ * @since 1.0.0
+ */
+ public int getWriteBytes();
+
+ /**
+ * Return the total number of bytes read.
+ *
+ * @since 1.0.0
+ */
+ public int getReadBytes();
+
+ /**
+ * Return whether the request timed out (as opposed to failing), or
+ * null if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public Boolean didTimeout();
+
+ /**
+ * Return the times in milliseconds since the epoch when {@code x%} of
+ * expected bytes were read for {@code 0 <= x <= 100}, or null if the
+ * torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<Integer, Long> getDataPercentiles();
+
+ /**
+ * Return the time in milliseconds since the epoch when the circuit was
+ * launched, or -1 if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public long getLaunchMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the circuit was
+ * used, or -1 if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public long getUsedAtMillis();
+
+ /**
+ * Return a list of fingerprints of the relays in the circuit, or null
+ * if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getPath();
+
+ /**
+ * Return a list of times in milliseconds since the epoch when circuit
+ * hops were built, or null if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public List<Long> getBuildTimes();
+
+ /**
+ * Return the circuit build timeout that the Tor client used when
+ * building this circuit, or -1 if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public long getTimeout();
+
+ /**
+ * Return the circuit build time quantile that the Tor client uses to
+ * determine its circuit-build timeout, or -1 if the torperf line
+ * didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public double getQuantile();
+
+ /**
+ * Return the identifier of the circuit used for this measurement, or
+ * -1 if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public int getCircId();
+
+ /**
+ * Return the identifier of the stream used for this measurement, or -1
+ * if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public int getUsedBy();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
new file mode 100644
index 0000000..295e0a4
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
@@ -0,0 +1,100 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.BandwidthHistory;
+
+public class BandwidthHistoryImpl implements BandwidthHistory {
+
+ protected BandwidthHistoryImpl(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ boolean isValid = false;
+ this.line = line;
+ if (partsNoOpt.length == 5 || partsNoOpt.length == 6) {
+ try {
+ this.historyEndMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ if (partsNoOpt[3].startsWith("(") &&
+ partsNoOpt[4].startsWith("s)")) {
+ this.intervalLength = Long.parseLong(partsNoOpt[3].
+ substring(1));
+ if (this.intervalLength <= 0L) {
+ throw new DescriptorParseException("Only positive interval "
+ + "lengths are allowed in line '" + line + "'.");
+ }
+ String[] values = null;
+ if (partsNoOpt.length == 5 &&
+ partsNoOpt[4].equals("s)")) {
+ /* There are no bandwidth values to parse. */
+ isValid = true;
+ } else if (partsNoOpt.length == 6) {
+ /* There are bandwidth values to parse. */
+ values = partsNoOpt[5].split(",", -1);
+ } else if (partsNoOpt[4].length() > 2) {
+ /* There are bandwidth values to parse, but there is no space
+ * between "s)" and "0,0,0,0". Very old Tor versions around
+ * Tor 0.0.8 wrote such history lines, and even though
+ * dir-spec.txt implies a space here, the old format isn't
+ * totally broken. Let's pretend there's a space. */
+ values = partsNoOpt[4].substring(2).split(",", -1);
+ }
+ if (values != null) {
+ this.bandwidthValues = new long[values.length];
+ for (int i = values.length - 1; i >= 0; i--) {
+ long bandwidthValue = Long.parseLong(values[i]);
+ if (bandwidthValue < 0L) {
+ throw new DescriptorParseException("Negative bandwidth "
+ + "values are not allowed in line '" + line + "'.");
+ }
+ this.bandwidthValues[i] = bandwidthValue;
+ }
+ isValid = true;
+ }
+ }
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Invalid bandwidth-history line "
+ + "'" + line + "'.");
+ }
+ }
+
+ private String line;
+ @Override
+ public String getLine() {
+ return this.line;
+ }
+
+ private long historyEndMillis;
+ @Override
+ public long getHistoryEndMillis() {
+ return this.historyEndMillis;
+ }
+
+ private long intervalLength;
+ @Override
+ public long getIntervalLength() {
+ return this.intervalLength;
+ }
+
+ private long[] bandwidthValues;
+ @Override
+ public SortedMap<Long, Long> getBandwidthValues() {
+ SortedMap<Long, Long> result = new TreeMap<>();
+ if (this.bandwidthValues != null) {
+ long endMillis = this.historyEndMillis;
+ for (int i = this.bandwidthValues.length - 1; i >= 0; i--) {
+ result.put(endMillis, bandwidthValues[i]);
+ endMillis -= this.intervalLength * 1000L;
+ }
+ }
+ return result;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
new file mode 100644
index 0000000..66426d8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
@@ -0,0 +1,98 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+
+/* Provide an iterator for a queue of objects and block when there are
+ * currently no objects in the queue. Allow the producer to signal that
+ * there won't be further objects and unblock any waiting consumers. */
+public class BlockingIteratorImpl<T> implements Iterator<T> {
+
+ /* Queue containing produced elemnts waiting for consumers. */
+ private Queue<T> queue = new LinkedList<>();
+
+ /* Maximum number of elements in queue. */
+ private int maxQueueSize = 100;
+
+ /* Restrict object construction to the impl package. */
+ protected BlockingIteratorImpl() {
+ }
+
+ /* Create instance with maximum queue size. */
+ protected BlockingIteratorImpl(int maxQueueSize) {
+ this.maxQueueSize = maxQueueSize;
+ }
+
+ /* Add an object to the queue if there's still room. */
+ protected synchronized void add(T object) {
+ if (this.outOfDescriptors) {
+ throw new IllegalStateException("Internal error: Adding results to "
+ + "descriptor queue not allowed after sending end-of-stream "
+ + "object.");
+ }
+ while (this.queue.size() >= this.maxQueueSize) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ this.queue.offer(object);
+ notifyAll();
+ }
+
+ /* Signalize that there won't be any further objects to be enqueued. */
+ private boolean outOfDescriptors = false;
+ protected synchronized void setOutOfDescriptors() {
+ if (this.outOfDescriptors) {
+ throw new IllegalStateException("Internal error: Sending "
+ + "end-of-stream object only permitted once.");
+ }
+ this.outOfDescriptors = true;
+ notifyAll();
+ }
+
+ /* Return whether there are more objects. Block if there are currently
+ * no objects, but the producer hasn't signalized that there won't be
+ * further objects. */
+ @Override
+ public synchronized boolean hasNext() {
+ while (!this.outOfDescriptors && this.queue.isEmpty()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ return this.queue.peek() != null;
+ }
+
+ /* Return the next object in the queue or throw an exception when there
+ * are no further objects. Block if there are currently no objects, but
+ * the producer hasn't signalized that there won't be further
+ * objects. */
+ @Override
+ public synchronized T next() {
+ while (!this.outOfDescriptors && this.queue.isEmpty()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (this.queue.peek() == null) {
+ throw new NoSuchElementException();
+ }
+ notifyAll();
+ return this.queue.remove();
+ }
+
+ /* Don't support explicitly removing objects. They are removed
+ * anyway. */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..15d40d8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.BridgeExtraInfoDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+
+public class BridgeExtraInfoDescriptorImpl
+ extends ExtraInfoDescriptorImpl implements BridgeExtraInfoDescriptor {
+
+ protected static List<ExtraInfoDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "extra-info ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ExtraInfoDescriptor parsedDescriptor =
+ new BridgeExtraInfoDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgeExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
new file mode 100644
index 0000000..bf3804d
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -0,0 +1,230 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedMap;
+import java.util.TimeZone;
+
+import org.torproject.descriptor.BridgeNetworkStatus;
+
+/* Contains a bridge network status. */
+public class BridgeNetworkStatusImpl extends NetworkStatusImpl
+ implements BridgeNetworkStatus {
+
+ protected BridgeNetworkStatusImpl(byte[] statusBytes,
+ String fileName, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(statusBytes, failUnrecognizedDescriptorLines, false, false);
+ this.setPublishedMillisFromFileName(fileName);
+ }
+
+ private void setPublishedMillisFromFileName(String fileName)
+ throws DescriptorParseException {
+ if (this.publishedMillis != 0L) {
+ /* We already learned the publication timestamp from parsing the
+ * "published" line. */
+ return;
+ }
+ if (fileName.length() ==
+ "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D".
+ length()) {
+ String publishedString = fileName.substring(0,
+ "yyyyMMdd-HHmmss".length());
+ try {
+ SimpleDateFormat fileNameFormat = new SimpleDateFormat(
+ "yyyyMMdd-HHmmss");
+ fileNameFormat.setLenient(false);
+ fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.publishedMillis = fileNameFormat.parse(publishedString).
+ getTime();
+ } catch (ParseException e) {
+ }
+ }
+ if (this.publishedMillis == 0L) {
+ throw new DescriptorParseException("Unrecognized bridge network "
+ + "status file name '" + fileName + "'.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ /* Initialize flag-thresholds values here for the case that the status
+ * doesn't contain those values. Initializing them in the constructor
+ * or when declaring variables wouldn't work, because those parts are
+ * evaluated later and would overwrite everything we parse here. */
+ this.stableUptime = -1L;
+ this.stableMtbf = -1L;
+ this.fastBandwidth = -1L;
+ this.guardWfu = -1.0;
+ this.guardTk = -1L;
+ this.guardBandwidthIncludingExits = -1L;
+ this.guardBandwidthExcludingExits = -1L;
+ this.enoughMtbfInfo = -1;
+ this.ignoringAdvertisedBws = -1;
+
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in bridge network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFlagThresholdsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No flag thresholds in line '"
+ + line + "'.");
+ }
+ SortedMap<String, String> flagThresholds =
+ ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+ try {
+ for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+ switch (e.getKey()) {
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue().
+ replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+
+ protected void parseDirSource(byte[] dirSourceBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory source expected in "
+ + "bridge network status.");
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory footer expected in "
+ + "bridge network status.");
+ }
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory signature expected "
+ + "in bridge network status.");
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long stableUptime;
+ @Override
+ public long getStableUptime() {
+ return this.stableUptime;
+ }
+
+ private long stableMtbf;
+ @Override
+ public long getStableMtbf() {
+ return this.stableMtbf;
+ }
+
+ private long fastBandwidth;
+ @Override
+ public long getFastBandwidth() {
+ return this.fastBandwidth;
+ }
+
+ private double guardWfu;
+ @Override
+ public double getGuardWfu() {
+ return this.guardWfu;
+ }
+
+ private long guardTk;
+ @Override
+ public long getGuardTk() {
+ return this.guardTk;
+ }
+
+ private long guardBandwidthIncludingExits;
+ @Override
+ public long getGuardBandwidthIncludingExits() {
+ return this.guardBandwidthIncludingExits;
+ }
+
+ private long guardBandwidthExcludingExits;
+ @Override
+ public long getGuardBandwidthExcludingExits() {
+ return this.guardBandwidthExcludingExits;
+ }
+
+ private int enoughMtbfInfo;
+ @Override
+ public int getEnoughMtbfInfo() {
+ return this.enoughMtbfInfo;
+ }
+
+ private int ignoringAdvertisedBws;
+ @Override
+ public int getIgnoringAdvertisedBws() {
+ return this.ignoringAdvertisedBws;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
new file mode 100644
index 0000000..99578e8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
@@ -0,0 +1,99 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.BridgePoolAssignment;
+
+/* TODO Write a test class. */
+public class BridgePoolAssignmentImpl extends DescriptorImpl
+ implements BridgePoolAssignment {
+
+ protected static List<BridgePoolAssignment> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<BridgePoolAssignment> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "bridge-pool-assignment ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ BridgePoolAssignment parsedDescriptor =
+ new BridgePoolAssignmentImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgePoolAssignmentImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
+ new String[] { "bridge-pool-assignment" }));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ this.checkFirstKeyword("bridge-pool-assignment");
+ this.clearParsedKeywords();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("bridge-pool-assignment ")) {
+ this.parseBridgePoolAssignmentLine(line);
+ } else {
+ this.parseBridgeLine(line);
+ }
+ }
+ }
+
+ private void parseBridgePoolAssignmentLine(String line)
+ throws DescriptorParseException {
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in bridge pool assignment.");
+ }
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseBridgeLine(String line)
+ throws DescriptorParseException {
+ String[] parts = line.split("[ \t]+");
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in bridge pool assignment.");
+ }
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[0]);
+ String poolAndDetails = line.substring(line.indexOf(" ") + 1);
+ this.entries.put(fingerprint, poolAndDetails);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private SortedMap<String, String> entries = new TreeMap<>();
+ @Override
+ public SortedMap<String, String> getEntries() {
+ return new TreeMap<>(this.entries);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
new file mode 100644
index 0000000..eb2b933
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.BridgeServerDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ServerDescriptor;
+
+public class BridgeServerDescriptorImpl extends ServerDescriptorImpl
+ implements BridgeServerDescriptor {
+
+ protected static List<ServerDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "router ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ServerDescriptor parsedDescriptor =
+ new BridgeServerDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgeServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
new file mode 100644
index 0000000..1a030ef
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
@@ -0,0 +1,249 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Stack;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+
+import org.torproject.descriptor.DescriptorCollector;
+
+public class DescriptorCollectorImpl implements DescriptorCollector {
+
+ @Override
+ public void collectDescriptors(String collecTorBaseUrl,
+ String[] remoteDirectories, long minLastModified,
+ File localDirectory, boolean deleteExtraneousLocalFiles) {
+ collecTorBaseUrl = collecTorBaseUrl.endsWith("/")
+ ? collecTorBaseUrl.substring(0, collecTorBaseUrl.length() - 1)
+ : collecTorBaseUrl;
+ if (minLastModified < 0) {
+ throw new IllegalArgumentException("A negative minimum "
+ + "last-modified time is not permitted.");
+ }
+ if (localDirectory.exists() && !localDirectory.isDirectory()) {
+ throw new IllegalArgumentException("Local directory already exists "
+ + "and is not a directory.");
+ }
+ SortedMap<String, Long> localFiles =
+ this.statLocalDirectory(localDirectory);
+ SortedMap<String, String> fetchedDirectoryListings =
+ this.fetchRemoteDirectories(collecTorBaseUrl, remoteDirectories);
+ SortedSet<String> parsedDirectories = new TreeSet<>();
+ SortedMap<String, Long> remoteFiles = new TreeMap<>();
+ for (Map.Entry<String, String> e :
+ fetchedDirectoryListings.entrySet()) {
+ String remoteDirectory = e.getKey();
+ String directoryListing = e.getValue();
+ SortedMap<String, Long> parsedRemoteFiles =
+ this.parseDirectoryListing(remoteDirectory, directoryListing);
+ if (parsedRemoteFiles == null) {
+ continue;
+ }
+ parsedDirectories.add(remoteDirectory);
+ remoteFiles.putAll(parsedRemoteFiles);
+ }
+ this.fetchRemoteFiles(collecTorBaseUrl, remoteFiles, minLastModified,
+ localDirectory, localFiles);
+ if (deleteExtraneousLocalFiles) {
+ this.deleteExtraneousLocalFiles(parsedDirectories, remoteFiles,
+ localDirectory, localFiles);
+ }
+ }
+
+ SortedMap<String, Long> statLocalDirectory(
+ File localDirectory) {
+ SortedMap<String, Long> localFiles = new TreeMap<>();
+ if (!localDirectory.exists()) {
+ return localFiles;
+ }
+ Stack<File> files = new Stack<>();
+ files.add(localDirectory);
+ while (!files.isEmpty()) {
+ File file = files.pop();
+ if (file.isDirectory()) {
+ files.addAll(Arrays.asList(file.listFiles()));
+ } else {
+ String localPath = file.getPath().substring(
+ localDirectory.getPath().length());
+ localFiles.put(localPath, file.lastModified());
+ }
+ }
+ return localFiles;
+ }
+
+ SortedMap<String, String> fetchRemoteDirectories(
+ String collecTorBaseUrl, String[] remoteDirectories) {
+ SortedMap<String, String> fetchedDirectoryListings = new TreeMap<>();
+ for (String remoteDirectory : remoteDirectories) {
+ String remoteDirectoryWithSlashAtBeginAndEnd =
+ (remoteDirectory.startsWith("/") ? "" : "/") + remoteDirectory
+ + (remoteDirectory.endsWith("/") ? "" : "/");
+ String directoryUrl = collecTorBaseUrl
+ + remoteDirectoryWithSlashAtBeginAndEnd;
+ String directoryListing = this.fetchRemoteDirectory(directoryUrl);
+ if (directoryListing.length() > 0) {
+ fetchedDirectoryListings.put(
+ remoteDirectoryWithSlashAtBeginAndEnd, directoryListing);
+ }
+ }
+ return fetchedDirectoryListings;
+ }
+
+ String fetchRemoteDirectory(String url) {
+ StringBuilder sb = new StringBuilder();
+ HttpURLConnection huc = null;
+ try {
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setRequestMethod("GET");
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ if (responseCode == 200) {
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ huc.getInputStream()));
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ br.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ if (huc != null) {
+ huc.disconnect();
+ }
+ return "";
+ }
+ return sb.toString();
+ }
+
+ final Pattern DIRECTORY_LISTING_LINE_PATTERN =
+ Pattern.compile(".* href=\"([^\"/]+)\"" /* filename */
+ + ".*>(\\d{2}-\\w{3}-\\d{4} \\d{2}:\\d{2})\\s*<.*"); /* dateTime */
+
+ SortedMap<String, Long> parseDirectoryListing(
+ String remoteDirectory, String directoryListing) {
+ SortedMap<String, Long> remoteFiles = new TreeMap<>();
+ DateFormat dateTimeFormat = ParseHelper.getDateFormat(
+ "dd-MMM-yyyy HH:mm");
+ try {
+ Scanner s = new Scanner(directoryListing);
+ s.useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ Matcher matcher = DIRECTORY_LISTING_LINE_PATTERN.matcher(line);
+ if (matcher.matches()) {
+ String filename = matcher.group(1);
+ long lastModifiedMillis = dateTimeFormat.parse(
+ matcher.group(2)).getTime();
+ remoteFiles.put(remoteDirectory + filename, lastModifiedMillis);
+ }
+ }
+ s.close();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ return null;
+ }
+ return remoteFiles;
+ }
+
+ void fetchRemoteFiles(String collecTorBaseUrl,
+ SortedMap<String, Long> remoteFiles, long minLastModified,
+ File localDirectory, SortedMap<String, Long> localFiles) {
+ for (Map.Entry<String, Long> e : remoteFiles.entrySet()) {
+ String filename = e.getKey();
+ long lastModifiedMillis = e.getValue();
+ if (lastModifiedMillis < minLastModified ||
+ (localFiles.containsKey(filename) &&
+ localFiles.get(filename) >= lastModifiedMillis)) {
+ continue;
+ }
+ String url = collecTorBaseUrl + filename;
+ File destinationFile = new File(localDirectory.getPath()
+ + filename);
+ this.fetchRemoteFile(url, destinationFile, lastModifiedMillis);
+ }
+ }
+
+ void fetchRemoteFile(String url, File destinationFile,
+ long lastModifiedMillis) {
+ HttpURLConnection huc = null;
+ try {
+ File destinationDirectory = destinationFile.getParentFile();
+ destinationDirectory.mkdirs();
+ File tempDestinationFile = new File(destinationDirectory, "."
+ + destinationFile.getName());
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(tempDestinationFile));
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setRequestMethod("GET");
+ if (!url.endsWith(".xz")) {
+ huc.addRequestProperty("Accept-Encoding", "gzip");
+ }
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ if (responseCode == 200) {
+ InputStream is;
+ if (huc.getContentEncoding() != null &&
+ huc.getContentEncoding().equalsIgnoreCase("gzip")) {
+ is = new GZIPInputStream(huc.getInputStream());
+ } else {
+ is = huc.getInputStream();
+ }
+ BufferedInputStream bis = new BufferedInputStream(is);
+ int len;
+ byte[] data = new byte[8192];
+ while ((len = bis.read(data, 0, 8192)) >= 0) {
+ bos.write(data, 0, len);
+ }
+ bis.close();
+ bos.close();
+ tempDestinationFile.renameTo(destinationFile);
+ destinationFile.setLastModified(lastModifiedMillis);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ if (huc != null) {
+ huc.disconnect();
+ }
+ }
+ }
+
+ void deleteExtraneousLocalFiles(
+ SortedSet<String> parsedDirectories,
+ SortedMap<String, Long> remoteFiles, File localDirectory,
+ SortedMap<String, Long> localFiles) {
+ for (String localPath : localFiles.keySet()) {
+ for (String remoteDirectory : parsedDirectories) {
+ if (localPath.startsWith(remoteDirectory)) {
+ if (!remoteFiles.containsKey(localPath)) {
+ new File(localDirectory.getPath() + localPath).delete();
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
new file mode 100644
index 0000000..e726ce9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
@@ -0,0 +1,283 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.DescriptorRequest;
+import org.torproject.descriptor.DescriptorDownloader;
+
+public class DescriptorDownloaderImpl
+ implements DescriptorDownloader {
+
+ private boolean hasStartedDownloading = false;
+
+ private SortedMap<String, DirectoryDownloader> directoryAuthorities =
+ new TreeMap<>();
+ @Override
+ public void addDirectoryAuthority(String nickname, String ip,
+ int dirPort) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkDirectoryParameters(nickname, ip, dirPort);
+ DirectoryDownloader directoryAuthority = new DirectoryDownloader(
+ nickname, ip, dirPort);
+ this.directoryAuthorities.put(nickname, directoryAuthority);
+ }
+
+ private SortedMap<String, DirectoryDownloader> directoryMirrors =
+ new TreeMap<>();
+ @Override
+ public void addDirectoryMirror(String nickname, String ip,
+ int dirPort) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkDirectoryParameters(nickname, ip, dirPort);
+ DirectoryDownloader directoryMirror = new DirectoryDownloader(
+ nickname, ip, dirPort);
+ this.directoryMirrors.put(nickname, directoryMirror);
+ /* TODO Implement prioritizing mirrors for non-vote downloads. */
+ throw new UnsupportedOperationException("Prioritizing directory "
+ + "mirrors over directory authorities is not implemented yet. "
+ + "Until it is, configuring directory mirrors is misleading and "
+ + "therefore not supported.");
+ }
+
+ private void checkDirectoryParameters(String nickname, String ip,
+ int dirPort) {
+ if (nickname == null || nickname.length() < 1) {
+ throw new IllegalArgumentException("'" + nickname + "' is not a "
+ + "valid nickname.");
+ }
+ if (ip == null || ip.length() < 7 || ip.split("\\.").length != 4) {
+ throw new IllegalArgumentException("'" + ip + "' is not a valid IP "
+ + "address.");
+ }
+ if (dirPort < 1 || dirPort > 65535) {
+ throw new IllegalArgumentException(String.valueOf(dirPort) + " is "
+ + "not a valid DirPort.");
+ }
+ /* TODO Relax the requirement for directory nicknames to be unique.
+ * In theory, we can identify them by ip+port. */
+ if (this.directoryAuthorities.containsKey(nickname) ||
+ this.directoryMirrors.containsKey(nickname)) {
+ throw new IllegalArgumentException("Directory nicknames must be "
+ + "unique.");
+ }
+ }
+
+ private boolean downloadConsensus = false;
+ @Override
+ public void setIncludeCurrentConsensus() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.downloadConsensus = true;
+ }
+
+ private boolean downloadConsensusFromAllAuthorities = false;
+ @Override
+ public void setIncludeCurrentConsensusFromAllDirectoryAuthorities() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.downloadConsensusFromAllAuthorities = true;
+ }
+
+ private boolean includeCurrentReferencedVotes = false;
+ @Override
+ public void setIncludeCurrentReferencedVotes() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.includeCurrentReferencedVotes = true;
+ }
+
+ private Set<String> downloadVotes = new HashSet<>();
+ @Override
+ public void setIncludeCurrentVote(String fingerprint) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkVoteFingerprint(fingerprint);
+ this.downloadVotes.add(fingerprint);
+ }
+
+ @Override
+ public void setIncludeCurrentVotes(Set<String> fingerprints) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (fingerprints == null) {
+ throw new IllegalArgumentException("Set of fingerprints must not "
+ + "be null.");
+ }
+ for (String fingerprint : fingerprints) {
+ this.checkVoteFingerprint(fingerprint);
+ }
+ for (String fingerprint : fingerprints) {
+ this.setIncludeCurrentVote(fingerprint);
+ }
+ }
+
+ private void checkVoteFingerprint(String fingerprint) {
+ if (fingerprint == null || fingerprint.length() != 40) {
+ throw new IllegalArgumentException("'" + fingerprint + "' is not a "
+ + "valid fingerprint.");
+ }
+ }
+
+ @Override
+ public void setIncludeReferencedServerDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeServerDescriptor(String identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeServerDescriptors(Set<String> identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setIncludeReferencedExtraInfoDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeExtraInfoDescriptor(String identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeExtraInfoDescriptors(Set<String> identifiers) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ private long readTimeoutMillis = 60L * 1000L;
+ @Override
+ public void setReadTimeout(long readTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (readTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Read timeout value "
+ + String.valueOf(readTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.readTimeoutMillis = readTimeoutMillis;
+ }
+
+ private long connectTimeoutMillis = 60L * 1000L;
+ @Override
+ public void setConnectTimeout(long connectTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (connectTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Connect timeout value "
+ + String.valueOf(connectTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.connectTimeoutMillis = connectTimeoutMillis;
+ }
+
+ private long globalTimeoutMillis = 60L * 60L * 1000L;
+ @Override
+ public void setGlobalTimeout(long globalTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (globalTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Global timeout value "
+ + String.valueOf(globalTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.globalTimeoutMillis = globalTimeoutMillis;
+ }
+
+ private boolean failUnrecognizedDescriptorLines = false;
+ @Override
+ public void setFailUnrecognizedDescriptorLines() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.failUnrecognizedDescriptorLines = true;
+ }
+
+ @Override
+ public Iterator<DescriptorRequest> downloadDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Initiating downloads is only "
+ + "permitted once.");
+ }
+ this.hasStartedDownloading = true;
+ DownloadCoordinatorImpl downloadCoordinator =
+ new DownloadCoordinatorImpl(this.directoryAuthorities,
+ this.directoryMirrors, this.downloadConsensus,
+ this.downloadConsensusFromAllAuthorities, this.downloadVotes,
+ this.includeCurrentReferencedVotes, this.connectTimeoutMillis,
+ this.readTimeoutMillis, this.globalTimeoutMillis,
+ this.failUnrecognizedDescriptorLines);
+ Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator.
+ getDescriptorQueue();
+ return descriptorQueue;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
new file mode 100644
index 0000000..801c546
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
@@ -0,0 +1,78 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+
+public class DescriptorFileImpl implements DescriptorFile {
+
+ private File directory;
+ protected void setDirectory(File directory) {
+ this.directory = directory;
+ }
+ @Override
+ public File getDirectory() {
+ return this.directory;
+ }
+
+ private File tarball;
+ protected void setTarball(File tarball) {
+ this.tarball = tarball;
+ }
+ @Override
+ public File getTarball() {
+ return this.tarball;
+ }
+
+ private File file;
+ protected void setFile(File file) {
+ this.file = file;
+ }
+ @Override
+ public File getFile() {
+ return this.file;
+ }
+
+ private String fileName;
+ protected void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+ @Override
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ private long lastModified;
+ protected void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+ @Override
+ public long getLastModified() {
+ return this.lastModified;
+ }
+
+ private List<Descriptor> descriptors;
+ protected void setDescriptors(List<Descriptor> descriptors) {
+ this.descriptors = descriptors;
+ }
+ @Override
+ public List<Descriptor> getDescriptors() {
+ return this.descriptors == null ? new ArrayList<Descriptor>() :
+ new ArrayList<>(this.descriptors);
+ }
+
+ private Exception exception;
+ protected void setException(Exception exception) {
+ this.exception = exception;
+ }
+ @Override
+ public Exception getException() {
+ return this.exception;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
new file mode 100644
index 0000000..5625b3f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -0,0 +1,337 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+
+import org.torproject.descriptor.Descriptor;
+
+public abstract class DescriptorImpl implements Descriptor {
+
+ protected static List<Descriptor> parseDescriptors(
+ byte[] rawDescriptorBytes, String fileName,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<Descriptor> parsedDescriptors = new ArrayList<>();
+ if (rawDescriptorBytes == null) {
+ return parsedDescriptors;
+ }
+ byte[] first100Chars = new byte[Math.min(100,
+ rawDescriptorBytes.length)];
+ System.arraycopy(rawDescriptorBytes, 0, first100Chars, 0,
+ first100Chars.length);
+ String firstLines = new String(first100Chars);
+ if (firstLines.startsWith("@type network-status-consensus-3 1.") ||
+ firstLines.startsWith("@type network-status-microdesc-"
+ + "consensus-3 1.") ||
+ ((firstLines.startsWith("network-status-version 3") ||
+ firstLines.contains("\nnetwork-status-version 3")) &&
+ firstLines.contains("\nvote-status consensus\n"))) {
+ parsedDescriptors.addAll(RelayNetworkStatusConsensusImpl.
+ parseConsensuses(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type network-status-vote-3 1.")
+ || ((firstLines.startsWith("network-status-version 3\n") ||
+ firstLines.contains("\nnetwork-status-version 3\n")) &&
+ firstLines.contains("\nvote-status vote\n"))) {
+ parsedDescriptors.addAll(RelayNetworkStatusVoteImpl.
+ parseVotes(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-network-status 1.")
+ || firstLines.startsWith("r ")) {
+ parsedDescriptors.add(new BridgeNetworkStatusImpl(
+ rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith(
+ "@type bridge-server-descriptor 1.")) {
+ parsedDescriptors.addAll(BridgeServerDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type server-descriptor 1.") ||
+ firstLines.startsWith("router ") ||
+ firstLines.contains("\nrouter ")) {
+ parsedDescriptors.addAll(RelayServerDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-extra-info 1.")) {
+ parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type extra-info 1.") ||
+ firstLines.startsWith("extra-info ") ||
+ firstLines.contains("\nextra-info ")) {
+ parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type microdescriptor 1.") ||
+ firstLines.startsWith("onion-key\n") ||
+ firstLines.contains("\nonion-key\n")) {
+ parsedDescriptors.addAll(MicrodescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-pool-assignment 1.") ||
+ firstLines.startsWith("bridge-pool-assignment ") ||
+ firstLines.contains("\nbridge-pool-assignment ")) {
+ parsedDescriptors.addAll(BridgePoolAssignmentImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type dir-key-certificate-3 1.") ||
+ firstLines.startsWith("dir-key-certificate-version ") ||
+ firstLines.contains("\ndir-key-certificate-version ")) {
+ parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type tordnsel 1.") ||
+ firstLines.startsWith("ExitNode ") ||
+ firstLines.contains("\nExitNode ")) {
+ parsedDescriptors.add(new ExitListImpl(rawDescriptorBytes, fileName,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type network-status-2 1.") ||
+ firstLines.startsWith("network-status-version 2\n") ||
+ firstLines.contains("\nnetwork-status-version 2\n")) {
+ parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type directory 1.") ||
+ firstLines.startsWith("signed-directory\n") ||
+ firstLines.contains("\nsigned-directory\n")) {
+ parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type torperf 1.")) {
+ parsedDescriptors.addAll(TorperfResultImpl.parseTorperfResults(
+ rawDescriptorBytes, failUnrecognizedDescriptorLines));
+ } else {
+ throw new DescriptorParseException("Could not detect descriptor "
+ + "type in descriptor starting with '" + firstLines + "'.");
+ }
+ return parsedDescriptors;
+ }
+
+ protected static List<byte[]> splitRawDescriptorBytes(
+ byte[] rawDescriptorBytes, String startToken) {
+ List<byte[]> rawDescriptors = new ArrayList<>();
+ String splitToken = "\n" + startToken;
+ String ascii;
+ try {
+ ascii = new String(rawDescriptorBytes, "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ return rawDescriptors;
+ }
+ int endAllDescriptors = rawDescriptorBytes.length,
+ startAnnotations = 0;
+ boolean containsAnnotations = ascii.startsWith("@") ||
+ ascii.contains("\n@");
+ while (startAnnotations < endAllDescriptors) {
+ int startDescriptor;
+ if (ascii.indexOf(startToken, startAnnotations) == 0) {
+ startDescriptor = startAnnotations;
+ } else {
+ startDescriptor = ascii.indexOf(splitToken, startAnnotations - 1);
+ if (startDescriptor < 0) {
+ break;
+ } else {
+ startDescriptor += 1;
+ }
+ }
+ int endDescriptor = -1;
+ if (containsAnnotations) {
+ endDescriptor = ascii.indexOf("\n@", startDescriptor);
+ }
+ if (endDescriptor < 0) {
+ endDescriptor = ascii.indexOf(splitToken, startDescriptor);
+ }
+ if (endDescriptor < 0) {
+ endDescriptor = endAllDescriptors - 1;
+ }
+ endDescriptor += 1;
+ byte[] rawDescriptor = new byte[endDescriptor - startAnnotations];
+ System.arraycopy(rawDescriptorBytes, startAnnotations,
+ rawDescriptor, 0, endDescriptor - startAnnotations);
+ startAnnotations = endDescriptor;
+ rawDescriptors.add(rawDescriptor);
+ }
+ return rawDescriptors;
+ }
+
+ protected byte[] rawDescriptorBytes;
+ @Override
+ public byte[] getRawDescriptorBytes() {
+ return this.rawDescriptorBytes;
+ }
+
+ protected boolean failUnrecognizedDescriptorLines = false;
+
+ protected List<String> unrecognizedLines;
+ @Override
+ public List<String> getUnrecognizedLines() {
+ return this.unrecognizedLines == null ? new ArrayList<String>() :
+ new ArrayList<>(this.unrecognizedLines);
+ }
+
+ protected DescriptorImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines, boolean blankLinesAllowed)
+ throws DescriptorParseException {
+ this.rawDescriptorBytes = rawDescriptorBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.cutOffAnnotations(rawDescriptorBytes);
+ this.countKeywords(rawDescriptorBytes, blankLinesAllowed);
+ }
+
+ /* Parse annotation lines from the descriptor bytes. */
+ private List<String> annotations = new ArrayList<>();
+ private void cutOffAnnotations(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ String ascii = new String(rawDescriptorBytes);
+ int start = 0;
+ while ((start == 0 && ascii.startsWith("@")) ||
+ (start > 0 && ascii.indexOf("\n@", start - 1) >= 0)) {
+ int end = ascii.indexOf("\n", start);
+ if (end < 0) {
+ throw new DescriptorParseException("Annotation line does not "
+ + "contain a newline.");
+ }
+ this.annotations.add(ascii.substring(start, end));
+ start = end + 1;
+ }
+ if (start > 0) {
+ int length = rawDescriptorBytes.length;
+ byte[] rawDescriptor = new byte[length - start];
+ System.arraycopy(rawDescriptorBytes, start, rawDescriptor, 0,
+ length - start);
+ this.rawDescriptorBytes = rawDescriptor;
+ }
+ }
+ @Override
+ public List<String> getAnnotations() {
+ return new ArrayList<>(this.annotations);
+ }
+
+ /* Count parsed keywords for consistency checks by subclasses. */
+ private String firstKeyword, lastKeyword;
+ private Map<String, Integer> parsedKeywords = new HashMap<>();
+ private void countKeywords(byte[] rawDescriptorBytes,
+ boolean blankLinesAllowed) throws DescriptorParseException {
+ if (rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ if (!blankLinesAllowed && (descriptorString.startsWith("\n") ||
+ descriptorString.contains("\n\n"))) {
+ throw new DescriptorParseException("Blank lines are not allowed.");
+ }
+ boolean skipCrypto = false;
+ Scanner s = new Scanner(descriptorString).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("-----BEGIN")) {
+ skipCrypto = true;
+ } else if (line.startsWith("-----END")) {
+ skipCrypto = false;
+ } else if (!line.isEmpty() && !line.startsWith("@") &&
+ !skipCrypto) {
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String keyword = lineNoOpt.split(" ", -1)[0];
+ if (keyword.equals("")) {
+ throw new DescriptorParseException("Illegal keyword in line '"
+ + line + "'.");
+ }
+ if (this.firstKeyword == null) {
+ this.firstKeyword = keyword;
+ }
+ lastKeyword = keyword;
+ if (parsedKeywords.containsKey(keyword)) {
+ parsedKeywords.put(keyword, parsedKeywords.get(keyword) + 1);
+ } else {
+ parsedKeywords.put(keyword, 1);
+ }
+ }
+ }
+ }
+
+ protected void checkFirstKeyword(String keyword)
+ throws DescriptorParseException {
+ if (this.firstKeyword == null ||
+ !this.firstKeyword.equals(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ + "be contained in the first line.");
+ }
+ }
+
+ protected void checkLastKeyword(String keyword)
+ throws DescriptorParseException {
+ if (this.lastKeyword == null ||
+ !this.lastKeyword.equals(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ + "be contained in the last line.");
+ }
+ }
+
+ protected void checkExactlyOnceKeywords(Set<String> keywords)
+ throws DescriptorParseException {
+ for (String keyword : keywords) {
+ int contained = 0;
+ if (this.parsedKeywords.containsKey(keyword)) {
+ contained = this.parsedKeywords.get(keyword);
+ }
+ if (contained != 1) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ + "contained " + contained + " times, but must be contained "
+ + "exactly once.");
+ }
+ }
+ }
+
+ protected void checkAtLeastOnceKeywords(Set<String> keywords)
+ throws DescriptorParseException {
+ for (String keyword : keywords) {
+ if (!this.parsedKeywords.containsKey(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ + "contained 0 times, but must be contained at least once.");
+ }
+ }
+ }
+
+ protected void checkAtMostOnceKeywords(Set<String> keywords)
+ throws DescriptorParseException {
+ for (String keyword : keywords) {
+ if (this.parsedKeywords.containsKey(keyword) &&
+ this.parsedKeywords.get(keyword) > 1) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ + "contained " + this.parsedKeywords.get(keyword) + " times, "
+ + "but must be contained at most once.");
+ }
+ }
+ }
+
+ protected void checkKeywordsDependOn(Set<String> dependentKeywords,
+ String dependingKeyword) throws DescriptorParseException {
+ for (String dependentKeyword : dependentKeywords) {
+ if (this.parsedKeywords.containsKey(dependentKeyword) &&
+ !this.parsedKeywords.containsKey(dependingKeyword)) {
+ throw new DescriptorParseException("Keyword '" + dependentKeyword
+ + "' is contained, but keyword '" + dependingKeyword + "' is "
+ + "not.");
+ }
+ }
+ }
+
+ protected int getKeywordCount(String keyword) {
+ if (!this.parsedKeywords.containsKey(keyword)) {
+ return 0;
+ } else {
+ return this.parsedKeywords.get(keyword);
+ }
+ }
+
+ protected void clearParsedKeywords() {
+ this.parsedKeywords = null;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
new file mode 100644
index 0000000..0f9add2
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
@@ -0,0 +1,15 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+/**
+ * @deprecated Replaced by
+ * org.torproject.descriptor.DescriptorParseException
+ */
+@Deprecated public class DescriptorParseException extends Exception {
+ private static final long serialVersionUID = 100L;
+ protected DescriptorParseException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
new file mode 100644
index 0000000..6ac53f8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
@@ -0,0 +1,28 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorParser;
+
+public class DescriptorParserImpl implements DescriptorParser {
+
+ private boolean failUnrecognizedDescriptorLines;
+
+ @Override
+ public void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines) {
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ }
+
+ @Override
+ public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
+ String fileName) throws DescriptorParseException {
+ return DescriptorImpl.parseDescriptors(rawDescriptorBytes, fileName,
+ this.failUnrecognizedDescriptorLines);
+ }
+}
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
new file mode 100644
index 0000000..8da88e9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
@@ -0,0 +1,364 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorReader;
+
+public class DescriptorReaderImpl implements DescriptorReader {
+
+ private boolean hasStartedReading = false;
+
+ private List<File> directories = new ArrayList<>();
+ @Override
+ public void addDirectory(File directory) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.directories.add(directory);
+ }
+
+ private List<File> tarballs = new ArrayList<>();
+ @Override
+ public void addTarball(File tarball) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.tarballs.add(tarball);
+ }
+
+ private File historyFile;
+ @Override
+ public void setExcludeFiles(File historyFile) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.historyFile = historyFile;
+ }
+
+ private SortedMap<String, Long> excludedFiles;
+ @Override
+ public void setExcludedFiles(SortedMap<String, Long> excludedFiles) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.excludedFiles = excludedFiles;
+ }
+
+ @Override
+ public SortedMap<String, Long> getExcludedFiles() {
+ if (this.reader == null || !this.reader.hasFinishedReading) {
+ throw new IllegalStateException("Operation is not permitted before "
+ + "finishing to read.");
+ }
+ return new TreeMap<>(this.reader.excludedFilesAfter);
+ }
+
+ @Override
+ public SortedMap<String, Long> getParsedFiles() {
+ if (this.reader == null || !this.reader.hasFinishedReading) {
+ throw new IllegalStateException("Operation is not permitted before "
+ + "finishing to read.");
+ }
+ return new TreeMap<>(this.reader.parsedFilesAfter);
+ }
+
+ private boolean failUnrecognizedDescriptorLines = false;
+ @Override
+ public void setFailUnrecognizedDescriptorLines() {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.failUnrecognizedDescriptorLines = true;
+ }
+
+ private Integer maxDescriptorFilesInQueue = null;
+ @Override
+ public void setMaxDescriptorFilesInQueue(int max) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.maxDescriptorFilesInQueue = max;
+ }
+
+ private DescriptorReaderRunnable reader;
+ @Override
+ public Iterator<DescriptorFile> readDescriptors() {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Initiating reading is only "
+ + "permitted once.");
+ }
+ this.hasStartedReading = true;
+ BlockingIteratorImpl<DescriptorFile> descriptorQueue =
+ this.maxDescriptorFilesInQueue == null
+ ? new BlockingIteratorImpl<DescriptorFile>()
+ : new BlockingIteratorImpl<DescriptorFile>(
+ this.maxDescriptorFilesInQueue);
+ this.reader = new DescriptorReaderRunnable(this.directories,
+ this.tarballs, descriptorQueue, this.historyFile,
+ this.excludedFiles, this.failUnrecognizedDescriptorLines);
+ new Thread(this.reader).start();
+ return descriptorQueue;
+ }
+
+ private static class DescriptorReaderRunnable implements Runnable {
+ private List<File> directories;
+ private List<File> tarballs;
+ private BlockingIteratorImpl<DescriptorFile> descriptorQueue;
+ private File historyFile;
+ private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>(),
+ excludedFilesAfter = new TreeMap<>(),
+ parsedFilesAfter = new TreeMap<>();
+ private DescriptorParser descriptorParser;
+ private boolean hasFinishedReading = false;
+ private DescriptorReaderRunnable(List<File> directories,
+ List<File> tarballs,
+ BlockingIteratorImpl<DescriptorFile> descriptorQueue,
+ File historyFile, SortedMap<String, Long> excludedFiles,
+ boolean failUnrecognizedDescriptorLines) {
+ this.directories = directories;
+ this.tarballs = tarballs;
+ this.descriptorQueue = descriptorQueue;
+ this.historyFile = historyFile;
+ if (excludedFiles != null) {
+ this.excludedFilesBefore = excludedFiles;
+ }
+ this.descriptorParser = new DescriptorParserImpl();
+ this.descriptorParser.setFailUnrecognizedDescriptorLines(
+ failUnrecognizedDescriptorLines);
+ }
+ public void run() {
+ try {
+ this.readOldHistory();
+ this.readDescriptors();
+ this.readTarballs();
+ this.hasFinishedReading = true;
+ } catch (Throwable t) {
+ /* We're usually not writing to stdout or stderr, but we shouldn't
+ * stay quiet about this potential bug. If we were to switch to a
+ * logging API, this would qualify as ERROR. */
+ System.err.println("Bug: uncaught exception or error while "
+ + "reading descriptors:");
+ t.printStackTrace();
+ } finally {
+ this.descriptorQueue.setOutOfDescriptors();
+ }
+ if (this.hasFinishedReading) {
+ this.writeNewHistory();
+ }
+ }
+ private void readOldHistory() {
+ if (this.historyFile == null) {
+ return;
+ }
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(
+ this.historyFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (!line.contains(" ")) {
+ /* TODO Handle this problem? */
+ continue;
+ }
+ long lastModifiedMillis = Long.parseLong(line.substring(0,
+ line.indexOf(" ")));
+ String absolutePath = line.substring(line.indexOf(" ") + 1);
+ this.excludedFilesBefore.put(absolutePath, lastModifiedMillis);
+ }
+ br.close();
+ } catch (IOException e) {
+ /* TODO Handle this exception. */
+ } catch (NumberFormatException e) {
+ /* TODO Handle this exception. */
+ }
+ }
+ private void writeNewHistory() {
+ if (this.historyFile == null) {
+ return;
+ }
+ try {
+ if (this.historyFile.getParentFile() != null) {
+ this.historyFile.getParentFile().mkdirs();
+ }
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ this.historyFile));
+ SortedMap<String, Long> newHistory = new TreeMap<>();
+ newHistory.putAll(this.excludedFilesAfter);
+ newHistory.putAll(this.parsedFilesAfter);
+ for (Map.Entry<String, Long> e : newHistory.entrySet()) {
+ String absolutePath = e.getKey();
+ long lastModifiedMillis = e.getValue();
+ bw.write(String.valueOf(lastModifiedMillis) + " " + absolutePath
+ + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ /* TODO Handle this exception. */
+ }
+ }
+ private void readDescriptors() {
+ for (File directory : this.directories) {
+ if (!directory.exists() || !directory.isDirectory()) {
+ continue;
+ }
+ Stack<File> files = new Stack<>();
+ files.add(directory);
+ boolean abortReading = false;
+ while (!abortReading && !files.isEmpty()) {
+ File file = files.pop();
+ if (file.isDirectory()) {
+ files.addAll(Arrays.asList(file.listFiles()));
+ } else if (file.getName().endsWith(".tar") ||
+ file.getName().endsWith(".tar.bz2") ||
+ file.getName().endsWith(".tar.xz")) {
+ this.tarballs.add(file);
+ } else {
+ String absolutePath = file.getAbsolutePath();
+ long lastModifiedMillis = file.lastModified();
+ if (this.excludedFilesBefore.containsKey(absolutePath) &&
+ this.excludedFilesBefore.get(absolutePath) ==
+ lastModifiedMillis) {
+ this.excludedFilesAfter.put(absolutePath,
+ lastModifiedMillis);
+ continue;
+ }
+ this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
+ DescriptorFileImpl descriptorFile = new DescriptorFileImpl();
+ try {
+ descriptorFile.setDirectory(directory);
+ descriptorFile.setFile(file);
+ descriptorFile.setFileName(file.getAbsolutePath());
+ descriptorFile.setLastModified(lastModifiedMillis);
+ descriptorFile.setDescriptors(this.readFile(file));
+ } catch (DescriptorParseException e) {
+ descriptorFile.setException(e);
+ } catch (IOException e) {
+ descriptorFile.setException(e);
+ abortReading = true;
+ }
+ this.descriptorQueue.add(descriptorFile);
+ }
+ }
+ }
+ }
+ private void readTarballs() {
+ List<File> files = new ArrayList<>(this.tarballs);
+ boolean abortReading = false;
+ while (!abortReading && !files.isEmpty()) {
+ File tarball = files.remove(0);
+ if (!tarball.getName().endsWith(".tar") &&
+ !tarball.getName().endsWith(".tar.bz2") &&
+ !tarball.getName().endsWith(".tar.xz")) {
+ continue;
+ }
+ String absolutePath = tarball.getAbsolutePath();
+ long lastModifiedMillis = tarball.lastModified();
+ if (this.excludedFilesBefore.containsKey(absolutePath) &&
+ this.excludedFilesBefore.get(absolutePath) ==
+ lastModifiedMillis) {
+ this.excludedFilesAfter.put(absolutePath, lastModifiedMillis);
+ continue;
+ }
+ this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
+ try {
+ FileInputStream in = new FileInputStream(tarball);
+ if (in.available() > 0) {
+ TarArchiveInputStream tais = null;
+ if (tarball.getName().endsWith(".tar.bz2")) {
+ tais = new TarArchiveInputStream(
+ new BZip2CompressorInputStream(in));
+ } else if (tarball.getName().endsWith(".tar.xz")) {
+ tais = new TarArchiveInputStream(
+ new XZCompressorInputStream(in));
+ } else if (tarball.getName().endsWith(".tar")) {
+ tais = new TarArchiveInputStream(in);
+ }
+ BufferedInputStream bis = new BufferedInputStream(tais);
+ TarArchiveEntry tae = null;
+ while ((tae = tais.getNextTarEntry()) != null) {
+ if (tae.isDirectory()) {
+ continue;
+ }
+ DescriptorFileImpl descriptorFile =
+ new DescriptorFileImpl();
+ descriptorFile.setTarball(tarball);
+ descriptorFile.setFileName(tae.getName());
+ descriptorFile.setLastModified(tae.getLastModifiedDate().
+ getTime());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[1024];
+ while ((len = bis.read(data, 0, 1024)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ byte[] rawDescriptorBytes = baos.toByteArray();
+ if (rawDescriptorBytes.length < 1) {
+ continue;
+ }
+ try {
+ String fileName = tae.getName().substring(
+ tae.getName().lastIndexOf("/") + 1);
+ List<Descriptor> parsedDescriptors =
+ this.descriptorParser.parseDescriptors(
+ rawDescriptorBytes, fileName);
+ descriptorFile.setDescriptors(parsedDescriptors);
+ } catch (DescriptorParseException e) {
+ descriptorFile.setException(e);
+ }
+ this.descriptorQueue.add(descriptorFile);
+ }
+ }
+ } catch (IOException e) {
+ abortReading = true;
+ }
+ }
+ }
+ private List<Descriptor> readFile(File file) throws IOException,
+ DescriptorParseException {
+ FileInputStream fis = new FileInputStream(file);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[1024];
+ while ((len = bis.read(data, 0, 1024)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ bis.close();
+ byte[] rawDescriptorBytes = baos.toByteArray();
+ return this.descriptorParser.parseDescriptors(rawDescriptorBytes,
+ file.getName());
+ }
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
new file mode 100644
index 0000000..0238f24
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
@@ -0,0 +1,114 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorRequest;
+
+public class DescriptorRequestImpl implements DescriptorRequest {
+
+ private String requestedResource;
+ protected void setRequestedResource(String requestedResource) {
+ this.requestedResource = requestedResource;
+ }
+ protected String getRequestedResource() {
+ return this.requestedResource;
+ }
+
+ private String descriptorType;
+ protected void setDescriptorType(String descriptorType) {
+ this.descriptorType = descriptorType;
+ }
+ protected String getDescriptorType() {
+ return this.descriptorType;
+ }
+
+ private byte[] responseBytes;
+ protected byte[] getResponseBytes() {
+ return this.responseBytes;
+ }
+ protected void setResponseBytes(byte[] responseBytes) {
+ this.responseBytes = responseBytes;
+ }
+
+ private String requestUrl;
+ @Override
+ public String getRequestUrl() {
+ return this.requestUrl;
+ }
+
+ private String directoryNickname;
+ protected void setDirectoryNickname(String directoryNickname) {
+ this.directoryNickname = directoryNickname;
+ }
+ @Override
+ public String getDirectoryNickname() {
+ return this.directoryNickname;
+ }
+
+ private int responseCode;
+ protected void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+ @Override
+ public int getResponseCode() {
+ return this.responseCode;
+ }
+
+ private long requestStart;
+ protected void setRequestStart(long requestStart) {
+ this.requestStart = requestStart;
+ }
+ @Override
+ public long getRequestStart() {
+ return this.requestStart;
+ }
+
+ private long requestEnd;
+ protected void setRequestEnd(long requestEnd) {
+ this.requestEnd = requestEnd;
+ }
+ @Override
+ public long getRequestEnd() {
+ return this.requestEnd;
+ }
+
+ private boolean connectTimeoutHasExpired;
+ @Override
+ public boolean connectTimeoutHasExpired() {
+ return this.connectTimeoutHasExpired;
+ }
+
+ private boolean readTimeoutHasExpired;
+ @Override
+ public boolean readTimeoutHasExpired() {
+ return this.readTimeoutHasExpired;
+ }
+
+ private boolean globalTimeoutHasExpired;
+ @Override
+ public boolean globalTimeoutHasExpired() {
+ return this.globalTimeoutHasExpired;
+ }
+
+ private List<Descriptor> descriptors;
+ protected void setDescriptors(List<Descriptor> descriptors) {
+ this.descriptors = descriptors;
+ }
+ @Override
+ public List<Descriptor> getDescriptors() {
+ return this.descriptors;
+ }
+
+ private Exception exception;
+ protected void setException(Exception exception) {
+ this.exception = exception;
+ }
+ @Override
+ public Exception getException() {
+ return this.exception;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
new file mode 100644
index 0000000..fb2f5ad
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
@@ -0,0 +1,218 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.DirSourceEntry;
+
+public class DirSourceEntryImpl implements DirSourceEntry {
+
+ private byte[] dirSourceEntryBytes;
+ @Override
+ public byte[] getDirSourceEntryBytes() {
+ return this.dirSourceEntryBytes;
+ }
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected DirSourceEntryImpl(byte[] dirSourceEntryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.dirSourceEntryBytes = dirSourceEntryBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseDirSourceEntryBytes();
+ this.checkAndClearKeywords();
+ }
+
+ private SortedSet<String> exactlyOnceKeywords, atMostOnceKeywords;
+ private void initializeKeywords() {
+ this.exactlyOnceKeywords = new TreeSet<>();
+ this.exactlyOnceKeywords.add("dir-source");
+ this.exactlyOnceKeywords.add("vote-digest");
+ this.atMostOnceKeywords = new TreeSet<>();
+ this.atMostOnceKeywords.add("contact");
+ }
+
+ private void parsedExactlyOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.exactlyOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in dir-source.");
+ }
+ this.exactlyOnceKeywords.remove(keyword);
+ }
+
+ private void parsedAtMostOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.atMostOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate " + keyword + "line "
+ + "in dir-source.");
+ }
+ this.atMostOnceKeywords.remove(keyword);
+ }
+
+ private void checkAndClearKeywords() throws DescriptorParseException {
+ if (!this.exactlyOnceKeywords.isEmpty()) {
+ throw new DescriptorParseException("dir-source does not contain a '"
+ + this.exactlyOnceKeywords.first() + "' line.");
+ }
+ this.exactlyOnceKeywords = null;
+ this.atMostOnceKeywords = null;
+ }
+
+ private void parseDirSourceEntryBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.dirSourceEntryBytes)).
+ useDelimiter("\n");
+ boolean skipCrypto = false;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ");
+ switch (parts[0]) {
+ case "dir-source":
+ this.parseDirSourceLine(line);
+ break;
+ case "contact":
+ this.parseContactLine(line);
+ break;
+ case "vote-digest":
+ this.parseVoteDigestLine(line);
+ break;
+ case "-----BEGIN":
+ skipCrypto = true;
+ break;
+ case "-----END":
+ skipCrypto = false;
+ break;
+ default:
+ if (!skipCrypto) {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseDirSourceLine(String line)
+ throws DescriptorParseException {
+ this.parsedExactlyOnceKeyword("dir-source");
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 7) {
+ throw new DescriptorParseException("Invalid line '" + line + "'.");
+ }
+ String nickname = parts[1];
+ if (nickname.endsWith("-legacy")) {
+ nickname = nickname.substring(0, nickname.length()
+ - "-legacy".length());
+ this.isLegacy = true;
+ this.parsedExactlyOnceKeyword("vote-digest");
+ }
+ this.nickname = ParseHelper.parseNickname(line, nickname);
+ this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
+ if (parts[3].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.hostname = parts[3];
+ this.ip = ParseHelper.parseIpv4Address(line, parts[4]);
+ this.dirPort = ParseHelper.parsePort(line, parts[5]);
+ this.orPort = ParseHelper.parsePort(line, parts[6]);
+ }
+
+ private void parseContactLine(String line)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("contact");
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseVoteDigestLine(String line)
+ throws DescriptorParseException {
+ this.parsedExactlyOnceKeyword("vote-digest");
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "'.");
+ }
+ this.voteDigest = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private boolean isLegacy;
+ @Override
+ public boolean isLegacy() {
+ return this.isLegacy;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String ip;
+ @Override
+ public String getIp() {
+ return this.ip;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private String voteDigest;
+ @Override
+ public String getVoteDigest() {
+ return this.voteDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
new file mode 100644
index 0000000..a27ed76
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
@@ -0,0 +1,104 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.zip.InflaterInputStream;
+
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorSourceFactory;
+
+/* Download descriptors from one directory authority or mirror. First,
+ * ask the coordinator thread to create a request, run it, and deliver
+ * the response. Repeat until the coordinator thread says there are no
+ * further requests to make. */
+public class DirectoryDownloader implements Runnable {
+
+ private String nickname;
+ private String ipPort;
+ private DescriptorParser descriptorParser;
+ protected DirectoryDownloader(String nickname, String ip, int dirPort) {
+ this.nickname = nickname;
+ this.ipPort = ip + ":" + String.valueOf(dirPort);
+ this.descriptorParser =
+ DescriptorSourceFactory.createDescriptorParser();
+ }
+
+ private DownloadCoordinator downloadCoordinator;
+ protected void setDownloadCoordinator(
+ DownloadCoordinator downloadCoordinator) {
+ this.downloadCoordinator = downloadCoordinator;
+ }
+
+ private long connectTimeout;
+ protected void setConnectTimeout(long connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ private long readTimeout;
+ protected void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+ protected void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines) {
+ this.descriptorParser.setFailUnrecognizedDescriptorLines(
+ failUnrecognizedDescriptorLines);
+ }
+
+ @Override
+ public void run() {
+ boolean keepRunning = true;
+ do {
+ DescriptorRequestImpl request =
+ this.downloadCoordinator.createRequest(this.nickname);
+ if (request != null) {
+ String url = "http://" + this.ipPort
+ + request.getRequestedResource();
+ request.setRequestStart(System.currentTimeMillis());
+ HttpURLConnection huc = null;
+ try {
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setConnectTimeout((int) this.connectTimeout);
+ huc.setReadTimeout((int) this.readTimeout);
+ huc.setRequestMethod("GET");
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ request.setResponseCode(responseCode);
+ if (responseCode == 200) {
+ BufferedInputStream in = new BufferedInputStream(
+ new InflaterInputStream(huc.getInputStream()));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[8192];
+ while ((len = in.read(data, 0, 8192)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ in.close();
+ byte[] responseBytes = baos.toByteArray();
+ request.setResponseBytes(responseBytes);
+ request.setRequestEnd(System.currentTimeMillis());
+ request.setDescriptors(this.descriptorParser.parseDescriptors(
+ responseBytes, null));
+ }
+ } catch (Exception e) {
+ request.setException(e);
+ if (huc != null) {
+ huc.disconnect();
+ }
+ /* Stop downloading from this directory if there are any
+ * problems, e.g., refused connections. */
+ keepRunning = false;
+ }
+ this.downloadCoordinator.deliverResponse(request);
+ } else {
+ keepRunning = false;
+ }
+ } while (keepRunning);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
new file mode 100644
index 0000000..b62fc8e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
@@ -0,0 +1,308 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectoryKeyCertificate;
+
+/* TODO Add test class. */
+
+public class DirectoryKeyCertificateImpl extends DescriptorImpl
+ implements DirectoryKeyCertificate {
+
+ protected static List<DirectoryKeyCertificate> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<DirectoryKeyCertificate> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DirectoryKeyCertificateImpl.splitRawDescriptorBytes(
+ descriptorsBytes, "dir-key-certificate-version ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ DirectoryKeyCertificate parsedDescriptor =
+ new DirectoryKeyCertificateImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected DirectoryKeyCertificateImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ this.calculateDigest();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "dir-key-certificate-version,fingerprint,dir-identity-key,"
+ + "dir-key-published,dir-key-expires,dir-signing-key,"
+ + "dir-key-certification").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "dir-address,dir-key-crosscert").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("dir-key-certificate-version");
+ this.checkLastKeyword("dir-key-certification");
+ this.clearParsedKeywords();
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "dir-identity-key":
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublishedLine(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "dir-key-crosscert":
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
+ break;
+ case "dir-key-certification":
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in directory key certificate.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in directory key certificate.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseDirKeyCertificateVersionLine(String line,
+ String[] parts) throws DescriptorParseException {
+ if (!line.equals("dir-key-certificate-version 3")) {
+ throw new DescriptorParseException("Illegal directory key "
+ + "certificate version number in line '" + line + "'.");
+ }
+ this.dirKeyCertificateVersion = 3;
+ }
+
+ private void parseDirAddressLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || parts[1].split(":").length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in directory key certificate.");
+ }
+ this.address = ParseHelper.parseIpv4Address(line,
+ parts[1].split(":")[0]);
+ this.port = ParseHelper.parsePort(line, parts[1].split(":")[1]);
+ }
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in directory key certificate.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parseDirIdentityKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-identity-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyPublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirKeyExpiresLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCrosscertLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-crosscert")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCertificationLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-certification")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "dir-key-certificate-version ";
+ String sigToken = "\ndir-key-certification\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.certificateDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.certificateDigest == null) {
+ throw new DescriptorParseException("Could not calculate "
+ + "certificate digest.");
+ }
+ }
+
+ private int dirKeyCertificateVersion;
+ @Override
+ public int getDirKeyCertificateVersion() {
+ return this.dirKeyCertificateVersion;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int port = -1;
+ @Override
+ public int getPort() {
+ return this.port;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String dirIdentityKey;
+ @Override
+ public String getDirIdentityKey() {
+ return this.dirIdentityKey;
+ }
+
+ private long dirKeyPublishedMillis;
+ @Override
+ public long getDirKeyPublishedMillis() {
+ return this.dirKeyPublishedMillis;
+ }
+
+ private long dirKeyExpiresMillis;
+ @Override
+ public long getDirKeyExpiresMillis() {
+ return this.dirKeyExpiresMillis;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String dirKeyCrosscert;
+ @Override
+ public String getDirKeyCrosscert() {
+ return this.dirKeyCrosscert;
+ }
+
+ private String dirKeyCertification;
+ @Override
+ public String getDirKeyCertification() {
+ return this.dirKeyCertification;
+ }
+
+ private String certificateDigest;
+ @Override
+ public String getCertificateDigest() {
+ return this.certificateDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
new file mode 100644
index 0000000..a955f62
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -0,0 +1,115 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import org.torproject.descriptor.DirectorySignature;
+
+public class DirectorySignatureImpl implements DirectorySignature {
+
+ private byte[] directorySignatureBytes;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected DirectorySignatureImpl(byte[] directorySignatureBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.directorySignatureBytes = directorySignatureBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.parseDirectorySignatureBytes();
+ }
+
+ private void parseDirectorySignatureBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.directorySignatureBytes)).
+ useDelimiter("\n");
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ", -1);
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-signature":
+ int algorithmOffset = 0;
+ switch (parts.length) {
+ case 4:
+ this.algorithm = parts[1];
+ algorithmOffset = 1;
+ break;
+ case 3:
+ break;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ this.identity = ParseHelper.parseHexString(line,
+ parts[1 + algorithmOffset]);
+ this.signingKeyDigest = ParseHelper.parseHexString(
+ line, parts[2 + algorithmOffset]);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ this.signature = cryptoString;
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ static final String DEFAULT_ALGORITHM = "sha1";
+
+ private String algorithm;
+ @Override
+ public String getAlgorithm() {
+ return this.algorithm == null ? DEFAULT_ALGORITHM : this.algorithm;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private String signingKeyDigest;
+ @Override
+ public String getSigningKeyDigest() {
+ return this.signingKeyDigest;
+ }
+
+ private String signature;
+ @Override
+ public String getSignature() {
+ return this.signature;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
new file mode 100644
index 0000000..72cfeae
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
@@ -0,0 +1,10 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+public interface DownloadCoordinator {
+
+ public DescriptorRequestImpl createRequest(String nickname);
+
+ public void deliverResponse(DescriptorRequestImpl request);
+}
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
new file mode 100644
index 0000000..a8e3731
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
@@ -0,0 +1,298 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorRequest;
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* TODO This whole download logic is a mess and needs a cleanup. */
+public class DownloadCoordinatorImpl implements DownloadCoordinator {
+
+ private BlockingIteratorImpl<DescriptorRequest> descriptorQueue =
+ new BlockingIteratorImpl<>();
+ protected Iterator<DescriptorRequest> getDescriptorQueue() {
+ return this.descriptorQueue;
+ }
+
+ private SortedSet<String> runningDirectories;
+ private SortedMap<String, DirectoryDownloader> directoryAuthorities;
+ private SortedMap<String, DirectoryDownloader> directoryMirrors;
+ private boolean downloadConsensusFromAllAuthorities;
+ private boolean includeCurrentReferencedVotes;
+ private long connectTimeoutMillis;
+ private long readTimeoutMillis;
+ private long globalTimeoutMillis;
+ private boolean failUnrecognizedDescriptorLines;
+
+ protected DownloadCoordinatorImpl(
+ SortedMap<String, DirectoryDownloader> directoryAuthorities,
+ SortedMap<String, DirectoryDownloader> directoryMirrors,
+ boolean downloadConsensus,
+ boolean downloadConsensusFromAllAuthorities,
+ Set<String> downloadVotes, boolean includeCurrentReferencedVotes,
+ long connectTimeoutMillis, long readTimeoutMillis,
+ long globalTimeoutMillis, boolean failUnrecognizedDescriptorLines) {
+ this.directoryAuthorities = directoryAuthorities;
+ this.directoryMirrors = directoryMirrors;
+ this.runningDirectories = new TreeSet<>();
+ this.runningDirectories.addAll(directoryAuthorities.keySet());
+ this.runningDirectories.addAll(directoryMirrors.keySet());
+ this.missingConsensus = downloadConsensus;
+ this.downloadConsensusFromAllAuthorities =
+ downloadConsensusFromAllAuthorities;
+ this.missingVotes = downloadVotes;
+ this.includeCurrentReferencedVotes = includeCurrentReferencedVotes;
+ this.connectTimeoutMillis = connectTimeoutMillis;
+ this.readTimeoutMillis = readTimeoutMillis;
+ this.globalTimeoutMillis = globalTimeoutMillis;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ if (this.directoryMirrors.isEmpty() &&
+ this.directoryAuthorities.isEmpty()) {
+ this.descriptorQueue.setOutOfDescriptors();
+ /* TODO Should we say anything if we don't have any directories
+ * configured? */
+ } else {
+ GlobalTimer globalTimer = new GlobalTimer(this.globalTimeoutMillis,
+ this);
+ this.globalTimerThread = new Thread(globalTimer);
+ this.globalTimerThread.start();
+ for (DirectoryDownloader directoryMirror :
+ this.directoryMirrors.values()) {
+ directoryMirror.setDownloadCoordinator(this);
+ directoryMirror.setConnectTimeout(this.connectTimeoutMillis);
+ directoryMirror.setReadTimeout(this.readTimeoutMillis);
+ directoryMirror.setFailUnrecognizedDescriptorLines(
+ this.failUnrecognizedDescriptorLines);
+ new Thread(directoryMirror).start();
+ }
+ for (DirectoryDownloader directoryAuthority :
+ this.directoryAuthorities.values()) {
+ directoryAuthority.setDownloadCoordinator(this);
+ directoryAuthority.setConnectTimeout(this.connectTimeoutMillis);
+ directoryAuthority.setReadTimeout(this.readTimeoutMillis);
+ directoryAuthority.setFailUnrecognizedDescriptorLines(
+ this.failUnrecognizedDescriptorLines);
+ new Thread(directoryAuthority).start();
+ }
+ }
+ }
+
+ /* Interrupt all downloads if the total download time exceeds a given
+ * time. */
+ private Thread globalTimerThread;
+ private static class GlobalTimer implements Runnable {
+ private long timeoutMillis;
+ private DownloadCoordinatorImpl downloadCoordinator;
+ private GlobalTimer(long timeoutMillis,
+ DownloadCoordinatorImpl downloadCoordinator) {
+ this.timeoutMillis = timeoutMillis;
+ this.downloadCoordinator = downloadCoordinator;
+ }
+ public void run() {
+ long started = System.currentTimeMillis(), sleep;
+ while ((sleep = started + this.timeoutMillis
+ - System.currentTimeMillis()) > 0L) {
+ try {
+ Thread.sleep(sleep);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ this.downloadCoordinator.interruptAllDownloads();
+ }
+ }
+
+ /* Are we missing the consensus, and should the next directory that
+ * hasn't tried downloading it before attempt to download it? */
+ private boolean missingConsensus = false;
+
+ /* Which directories are currently attempting to download the
+ * consensus? */
+ private Set<String> requestingConsensuses = new HashSet<>();
+
+ /* Which directories have attempted to download the consensus so far,
+ * including those directories that are currently attempting it? */
+ private Set<String> requestedConsensuses = new HashSet<>();
+
+ /* Which votes are we currently missing? */
+ private Set<String> missingVotes = new HashSet<>();
+
+ /* Which vote (map value) is a given directory (map key) currently
+ * attempting to download? */
+ private Map<String, String> requestingVotes = new HashMap<>();
+
+ /* Which votes (map value) has a given directory (map key) attempted or
+ * is currently attempting to download? */
+ private Map<String, Set<String>> requestedVotes = new HashMap<>();
+
+ private boolean hasFinishedDownloading = false;
+
+ /* Look up what request a directory should make next. If there is
+ * nothing to do right now, but maybe later, block the caller. If
+ * we're done downloading, return null to notify the caller. */
+ @Override
+ public synchronized DescriptorRequestImpl createRequest(
+ String nickname) {
+ while (!this.hasFinishedDownloading) {
+ DescriptorRequestImpl request = new DescriptorRequestImpl();
+ request.setDirectoryNickname(nickname);
+ if ((this.missingConsensus ||
+ (this.downloadConsensusFromAllAuthorities &&
+ this.directoryAuthorities.containsKey(nickname))) &&
+ !this.requestedConsensuses.contains(nickname)) {
+ if (!this.downloadConsensusFromAllAuthorities) {
+ this.missingConsensus = false;
+ }
+ this.requestingConsensuses.add(nickname);
+ this.requestedConsensuses.add(nickname);
+ request.setRequestedResource(
+ "/tor/status-vote/current/consensus.z");
+ request.setDescriptorType("consensus");
+ return request;
+ }
+ if (!this.missingVotes.isEmpty() &&
+ this.directoryAuthorities.containsKey(nickname)) {
+ String requestingVote = null;
+ for (String missingVote : this.missingVotes) {
+ if (!this.requestedVotes.containsKey(nickname) ||
+ !this.requestedVotes.get(nickname).contains(missingVote)) {
+ requestingVote = missingVote;
+ }
+ }
+ if (requestingVote != null) {
+ this.requestingVotes.put(nickname, requestingVote);
+ if (!this.requestedVotes.containsKey(nickname)) {
+ this.requestedVotes.put(nickname, new HashSet<String>());
+ }
+ this.requestedVotes.get(nickname).add(requestingVote);
+ this.missingVotes.remove(requestingVote);
+ request.setRequestedResource("/tor/status-vote/current/"
+ + requestingVote + ".z");
+ request.setDescriptorType("vote");
+ return request;
+ }
+ }
+ /* TODO Add server descriptors and extra-info descriptors later. */
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ /* TODO What shall we do? */
+ }
+ }
+ return null;
+ }
+
+ /* Deliver a response which may either contain one or more descriptors
+ * or a failure response code. Update the lists of missing descriptors,
+ * decide if there are more descriptors to download, and wake up any
+ * waiting downloader threads. */
+ @Override
+ public synchronized void deliverResponse(
+ DescriptorRequestImpl response) {
+ String nickname = response.getDirectoryNickname();
+ if (response.getException() != null) {
+ this.runningDirectories.remove(nickname);
+ }
+ switch (response.getDescriptorType()) {
+ case "consensus":
+ this.requestingConsensuses.remove(nickname);
+ if (response.getResponseCode() == 200 &&
+ response.getDescriptors() != null) {
+ if (this.includeCurrentReferencedVotes) {
+ /* TODO Only add votes if the consensus is not older than one
+ * hour. Or does that make no sense? */
+ for (Descriptor parsedDescriptor :
+ response.getDescriptors()) {
+ if (!(parsedDescriptor instanceof
+ RelayNetworkStatusConsensus)) {
+ continue;
+ }
+ RelayNetworkStatusConsensus parsedConsensus =
+ (RelayNetworkStatusConsensus) parsedDescriptor;
+ for (DirSourceEntry dirSource :
+ parsedConsensus.getDirSourceEntries().values()) {
+ String identity = dirSource.getIdentity();
+ if (!this.missingVotes.contains(identity)) {
+ boolean alreadyRequested = false;
+ for (Set<String> requestedBefore :
+ this.requestedVotes.values()) {
+ if (requestedBefore.contains(identity)) {
+ alreadyRequested = true;
+ break;
+ }
+ }
+ if (!alreadyRequested) {
+ this.missingVotes.add(identity);
+ }
+ }
+ }
+ }
+ /* TODO Later, add referenced server descriptors. */
+ }
+ } else {
+ this.missingConsensus = true;
+ }
+ break;
+ case "vote":
+ String requestedVote = requestingVotes.remove(nickname);
+ if (response.getResponseCode() != 200) {
+ this.missingVotes.add(requestedVote);
+ }
+ }
+ if (response.getRequestEnd() != 0L) {
+ this.descriptorQueue.add(response);
+ }
+ boolean doneDownloading = true;
+ if ((this.missingConsensus ||
+ this.downloadConsensusFromAllAuthorities) &&
+ (!this.requestedConsensuses.containsAll(
+ this.runningDirectories) ||
+ !this.requestingConsensuses.isEmpty())) {
+ doneDownloading = false;
+ }
+ if (!this.requestingVotes.isEmpty()) {
+ doneDownloading = false;
+ } else if (!this.missingVotes.isEmpty()) {
+ if (!this.requestedVotes.keySet().containsAll(
+ this.runningDirectories)) {
+ doneDownloading = false;
+ } else {
+ for (String missingVote : this.missingVotes) {
+ for (String runningDirectory : this.runningDirectories) {
+ Set<String> reqVotes = this.requestedVotes.get(
+ runningDirectory);
+ if (!reqVotes.contains(missingVote)) {
+ doneDownloading = false;
+ }
+ }
+ }
+ }
+ }
+ if (doneDownloading) {
+ this.hasFinishedDownloading = true;
+ this.globalTimerThread.interrupt();
+ this.descriptorQueue.setOutOfDescriptors();
+ }
+ /* Wake up all waiting downloader threads. Maybe they can now
+ * download something, or they'll realize we're done downloading. */
+ this.notifyAll();
+ }
+
+ private synchronized void interruptAllDownloads() {
+ this.hasFinishedDownloading = true;
+ this.notifyAll();
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
new file mode 100644
index 0000000..efbf31c
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
@@ -0,0 +1,216 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExitList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
+
+ private byte[] exitListEntryBytes;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ @Deprecated
+ private ExitListEntryImpl(String fingerprint, long publishedMillis,
+ long lastStatusMillis, String exitAddress, long scanMillis) {
+ this.fingerprint = fingerprint;
+ this.publishedMillis = publishedMillis;
+ this.lastStatusMillis = lastStatusMillis;
+ this.exitAddresses.put(exitAddress, scanMillis);
+ }
+
+ @Deprecated
+ List<ExitListEntry> oldEntries() {
+ List<ExitListEntry> result = new ArrayList<>();
+ if (this.exitAddresses.size() > 1) {
+ for (Map.Entry<String, Long> entry :
+ this.exitAddresses.entrySet()) {
+ result.add(new ExitListEntryImpl(this.fingerprint,
+ this.publishedMillis, this.lastStatusMillis, entry.getKey(),
+ entry.getValue()));
+ }
+ } else {
+ result.add(this);
+ }
+ return result;
+ }
+
+ protected ExitListEntryImpl(byte[] exitListEntryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.exitListEntryBytes = exitListEntryBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseExitListEntryBytes();
+ this.checkAndClearKeywords();
+ }
+
+ private SortedSet<String> keywordCountingSet;
+ private void initializeKeywords() {
+ this.keywordCountingSet = new TreeSet<>();
+ this.keywordCountingSet.add("ExitNode");
+ this.keywordCountingSet.add("Published");
+ this.keywordCountingSet.add("LastStatus");
+ this.keywordCountingSet.add("ExitAddress");
+ }
+
+ private void parsedExactlyOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.keywordCountingSet.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in exit list entry.");
+ }
+ this.keywordCountingSet.remove(keyword);
+ }
+
+ private void checkAndClearKeywords() throws DescriptorParseException {
+ for (String missingKeyword : this.keywordCountingSet) {
+ throw new DescriptorParseException("Missing '" + missingKeyword
+ + "' line in exit list entry.");
+ }
+ this.keywordCountingSet = null;
+ }
+
+ private void parseExitListEntryBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.exitListEntryBytes)).
+ useDelimiter(ExitList.EOL);
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "ExitNode":
+ this.parseExitNodeLine(line, parts);
+ break;
+ case "Published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "LastStatus":
+ this.parseLastStatusLine(line, parts);
+ break;
+ case "ExitAddress":
+ this.parseExitAddressLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in exit list entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseExitNodeLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseLastStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.lastStatusMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseExitAddressLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 4) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.keywordCountingSet.remove(parts[0]);
+ this.exitAddresses.put(ParseHelper.parseIpv4Address(line, parts[1]),
+ ParseHelper.parseTimestampAtIndex(line, parts, 2, 3));
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long lastStatusMillis;
+ @Override
+ public long getLastStatusMillis() {
+ return this.lastStatusMillis;
+ }
+
+ private String exitAddress;
+ @Override
+ public String getExitAddress() {
+ if (null == exitAddress) {
+ Map.Entry<String, Long> randomEntry =
+ this.exitAddresses.entrySet().iterator().next();
+ this.exitAddress = randomEntry.getKey();
+ this.scanMillis = randomEntry.getValue();
+ }
+ return this.exitAddress;
+ }
+
+ private Map<String, Long> exitAddresses = new HashMap<>();
+ @Override
+ public Map<String, Long> getExitAddresses(){
+ return new HashMap<>(this.exitAddresses);
+ }
+
+ private long scanMillis;
+ @Override
+ public long getScanMillis() {
+ if (null == exitAddress) {
+ getExitAddress();
+ }
+ return scanMillis;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
new file mode 100644
index 0000000..10619ba
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
@@ -0,0 +1,142 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.torproject.descriptor.ExitList;
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListImpl extends DescriptorImpl implements ExitList {
+
+ protected ExitListImpl(byte[] rawDescriptorBytes, String fileName,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.splitAndParseExitListEntries(rawDescriptorBytes);
+ this.setPublishedMillisFromFileName(fileName);
+ }
+
+ private void setPublishedMillisFromFileName(String fileName)
+ throws DescriptorParseException {
+ if (this.downloadedMillis == 0L &&
+ fileName.length() == "2012-02-01-04-06-24".length()) {
+ try {
+ SimpleDateFormat fileNameFormat = new SimpleDateFormat(
+ "yyyy-MM-dd-HH-mm-ss");
+ fileNameFormat.setLenient(false);
+ fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.downloadedMillis = fileNameFormat.parse(fileName).getTime();
+ } catch (ParseException e) {
+ /* Handle below. */
+ }
+ }
+ if (this.downloadedMillis == 0L) {
+ throw new DescriptorParseException("Unrecognized exit list file "
+ + "name '" + fileName + "'.");
+ }
+ }
+
+ private void splitAndParseExitListEntries(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ Scanner s = new Scanner(descriptorString).useDelimiter(EOL);
+ StringBuilder sb = new StringBuilder();
+ boolean firstEntry = true;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) { /* Skip annotation. */
+ if (!s.hasNext()) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ } else {
+ line = s.next();
+ }
+ }
+ String[] parts = line.split(" ");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "Downloaded":
+ this.downloadedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ break;
+ case "ExitNode":
+ if (!firstEntry) {
+ this.parseExitListEntry(sb.toString().getBytes());
+ } else {
+ firstEntry = false;
+ }
+ sb = new StringBuilder();
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "Published":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "LastStatus":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "ExitAddress":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in exit list.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ /* Parse the last entry. */
+ this.parseExitListEntry(sb.toString().getBytes());
+ }
+
+ protected void parseExitListEntry(byte[] exitListEntryBytes)
+ throws DescriptorParseException {
+ ExitListEntryImpl exitListEntry = new ExitListEntryImpl(
+ exitListEntryBytes, this.failUnrecognizedDescriptorLines);
+ this.exitListEntries.add(exitListEntry);
+ this.oldExitListEntries.addAll(exitListEntry.oldEntries());
+ List<String> unrecognizedExitListEntryLines = exitListEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedExitListEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedExitListEntryLines);
+ }
+ }
+
+ private long downloadedMillis;
+ @Override
+ public long getDownloadedMillis() {
+ return this.downloadedMillis;
+ }
+
+ private Set<ExitListEntry> oldExitListEntries = new HashSet<>();
+ @Deprecated
+ @Override
+ public Set<ExitListEntry> getExitListEntries() {
+ return new HashSet<>(this.oldExitListEntries);
+ }
+
+ private Set<ExitList.Entry> exitListEntries = new HashSet<>();
+ @Override
+ public Set<ExitList.Entry> getEntries() {
+ return new HashSet<>(this.exitListEntries);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..3f72616
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -0,0 +1,1284 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+
+public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
+ implements ExtraInfoDescriptor {
+
+ protected ExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ this.calculateDigest();
+ this.calculateDigestSha256();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "extra-info,published").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> dirreqStatsKeywords = new HashSet<>(Arrays.asList((
+ "dirreq-stats-end,dirreq-v2-ips,dirreq-v3-ips,dirreq-v2-reqs,"
+ + "dirreq-v3-reqs,dirreq-v2-share,dirreq-v3-share,dirreq-v2-resp,"
+ + "dirreq-v3-resp,dirreq-v2-direct-dl,dirreq-v3-direct-dl,"
+ + "dirreq-v2-tunneled-dl,dirreq-v3-tunneled-dl,").split(",")));
+ Set<String> entryStatsKeywords = new HashSet<>(Arrays.asList(
+ "entry-stats-end,entry-ips".split(",")));
+ Set<String> cellStatsKeywords = new HashSet<>(Arrays.asList((
+ "cell-stats-end,cell-processed-cells,cell-queued-cells,"
+ + "cell-time-in-queue,cell-circuits-per-decile").split(",")));
+ Set<String> connBiDirectStatsKeywords = new HashSet<>(
+ Arrays.asList("conn-bi-direct".split(",")));
+ Set<String> exitStatsKeywords = new HashSet<>(Arrays.asList((
+ "exit-stats-end,exit-kibibytes-written,exit-kibibytes-read,"
+ + "exit-streams-opened").split(",")));
+ Set<String> bridgeStatsKeywords = new HashSet<>(Arrays.asList(
+ "bridge-stats-end,bridge-stats-ips".split(",")));
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "identity-ed25519,master-key-ed25519,read-history,write-history,"
+ + "dirreq-read-history,dirreq-write-history,geoip-db-digest,"
+ + "router-sig-ed25519,router-signature,router-digest-sha256,"
+ + "router-digest").split(",")));
+ atMostOnceKeywords.addAll(dirreqStatsKeywords);
+ atMostOnceKeywords.addAll(entryStatsKeywords);
+ atMostOnceKeywords.addAll(cellStatsKeywords);
+ atMostOnceKeywords.addAll(connBiDirectStatsKeywords);
+ atMostOnceKeywords.addAll(exitStatsKeywords);
+ atMostOnceKeywords.addAll(bridgeStatsKeywords);
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkKeywordsDependOn(dirreqStatsKeywords, "dirreq-stats-end");
+ this.checkKeywordsDependOn(entryStatsKeywords, "entry-stats-end");
+ this.checkKeywordsDependOn(cellStatsKeywords, "cell-stats-end");
+ this.checkKeywordsDependOn(exitStatsKeywords, "exit-stats-end");
+ this.checkKeywordsDependOn(bridgeStatsKeywords, "bridge-stats-end");
+ this.checkFirstKeyword("extra-info");
+ this.clearParsedKeywords();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ List<String> cryptoLines = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "extra-info":
+ this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-db-digest":
+ this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip6-db-digest":
+ this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-start-time":
+ this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-client-origins":
+ this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-stats-end":
+ this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-ips":
+ this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-ips":
+ this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-reqs":
+ this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-reqs":
+ this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-share":
+ this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-share":
+ this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-resp":
+ this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-resp":
+ this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-direct-dl":
+ this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-direct-dl":
+ this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-tunneled-dl":
+ this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-tunneled-dl":
+ this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-read-history":
+ this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-write-history":
+ this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-stats-end":
+ this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-ips":
+ this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-stats-end":
+ this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-processed-cells":
+ this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-queued-cells":
+ this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-time-in-queue":
+ this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-circuits-per-decile":
+ this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "conn-bi-direct":
+ this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-stats-end":
+ this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-written":
+ this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-read":
+ this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-streams-opened":
+ this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-stats-end":
+ this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ips":
+ this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-versions":
+ this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-transports":
+ this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "transport":
+ this.parseTransportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-stats-end":
+ this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-rend-relayed-cells":
+ this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
+ partsNoOpt);
+ break;
+ case "hidserv-dir-onions-seen":
+ this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "identity-ed25519":
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
+ break;
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-signature":
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
+ cryptoLines.add(line);
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in extra-info "
+ + "descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ }
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
+ } else {
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in extra-info descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseExtraInfoLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[2]);
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseGeoipDbDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipDbDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseGeoip6DbDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoip6DbDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseGeoipStartTimeLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipStartTimeMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseGeoipClientOriginsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.geoipClientOrigins =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.dirreqStatsEndMillis = parsedStatsEndData[0];
+ this.dirreqStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private long[] parseStatsEndLine(String line, String partsNoOpt[],
+ int partsNoOptExpectedLength) throws DescriptorParseException {
+ if (partsNoOpt.length != partsNoOptExpectedLength ||
+ partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
+ !partsNoOpt[4].equals("s)")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ long[] result = new long[2];
+ result[0] = ParseHelper.parseTimestampAtIndex(line, partsNoOpt, 1, 2);
+ result[1] = ParseHelper.parseSeconds(line,
+ partsNoOpt[3].substring(1));
+ if (result[1] <= 0) {
+ throw new DescriptorParseException("Interval length must be "
+ + "positive in line '" + line + "'.");
+ }
+ return result;
+ }
+
+ private void parseDirreqV2IpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV3IpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV2ReqsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Reqs =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV3ReqsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Reqs =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV2ShareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Share = this.parseShareLine(line, partsNoOpt);
+ }
+
+ private void parseDirreqV3ShareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Share = this.parseShareLine(line, partsNoOpt);
+ }
+
+ private double parseShareLine(String line, String[] partsNoOpt)
+ throws DescriptorParseException {
+ double share = -1.0;
+ if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
+ partsNoOpt[1].endsWith("%")) {
+ String shareString = partsNoOpt[1];
+ shareString = shareString.substring(0, shareString.length() - 1);
+ try {
+ share = Double.parseDouble(shareString);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (share < 0.0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ return share;
+ }
+
+ private void parseDirreqV2RespLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Resp =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3RespLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Resp =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV2DirectDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2DirectDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3DirectDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3DirectDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV2TunneledDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2TunneledDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3TunneledDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3TunneledDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line,partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqReadHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseDirreqWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqWriteHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseEntryStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.entryStatsEndMillis = parsedStatsEndData[0];
+ this.entryStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseEntryIpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.entryIps = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseCellStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.cellStatsEndMillis = parsedStatsEndData[0];
+ this.cellStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseCellProcessedCellsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellProcessedCells = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ if (this.cellProcessedCells.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellQueuedCellsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellQueuedCells = ParseHelper.parseCommaSeparatedDoubleValueList(
+ line, partsNoOpt, 1);
+ if (this.cellQueuedCells.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellTimeInQueueLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellTimeInQueue = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ if (this.cellTimeInQueue.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellCircuitsPerDecileLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ int circuits = -1;
+ if (partsNoOpt.length == 2) {
+ try {
+ circuits = Integer.parseInt(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (circuits < 0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.cellCircuitsPerDecile = circuits;
+ }
+
+ private void parseConnBiDirectLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 6);
+ this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
+ this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
+ Integer[] parsedConnBiDirectStats = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
+ if (parsedConnBiDirectStats.length != 4) {
+ throw new DescriptorParseException("Illegal line '" + line + "' in "
+ + "extra-info descriptor.");
+ }
+ this.connBiDirectBelow = parsedConnBiDirectStats[0];
+ this.connBiDirectRead = parsedConnBiDirectStats[1];
+ this.connBiDirectWrite = parsedConnBiDirectStats[2];
+ this.connBiDirectBoth = parsedConnBiDirectStats[3];
+ }
+
+ private void parseExitStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.exitStatsEndMillis = parsedStatsEndData[0];
+ this.exitStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseExitKibibytesWrittenLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ this.exitKibibytesWritten = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitKibibytesWritten.keySet());
+ this.verifyBytesOrStreams(line, this.exitKibibytesWritten.values());
+ }
+
+ private void parseExitKibibytesReadLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.exitKibibytesRead = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitKibibytesRead.keySet());
+ this.verifyBytesOrStreams(line, this.exitKibibytesRead.values());
+ }
+
+ private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.exitStreamsOpened = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitStreamsOpened.keySet());
+ this.verifyBytesOrStreams(line, this.exitStreamsOpened.values());
+ }
+
+ private SortedMap<String, Long> sortByPorts(
+ SortedMap<String, Long> naturalOrder) {
+ SortedMap<String, Long> byPortNumber =
+ new TreeMap<String, Long>(new Comparator<String>() {
+ public int compare(String arg0, String arg1) {
+ int port0 = 0, port1 = 0;
+ try {
+ port1 = Integer.parseInt(arg1);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ try {
+ port0 = Integer.parseInt(arg0);
+ } catch (NumberFormatException e) {
+ return 1;
+ }
+ if (port0 < port1) {
+ return -1;
+ } else if (port0 > port1) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }});
+ byPortNumber.putAll(naturalOrder);
+ return byPortNumber;
+ }
+
+ private void verifyPorts(String line, Set<String> ports)
+ throws DescriptorParseException {
+ boolean valid = true;
+ try {
+ for (String port : ports) {
+ if (!port.equals("other") && Integer.parseInt(port) <= 0) {
+ valid = false;
+ break;
+ }
+ }
+ } catch (NumberFormatException e) {
+ valid = false;
+ }
+ if (!valid) {
+ throw new DescriptorParseException("Invalid port in line '" + line
+ + "'.");
+ }
+ }
+
+ private void verifyBytesOrStreams(String line,
+ Collection<Long> bytesOrStreams) throws DescriptorParseException {
+ boolean valid = true;
+ for (long s : bytesOrStreams) {
+ if (s < 0L) {
+ valid = false;
+ break;
+ }
+ }
+ if (!valid) {
+ throw new DescriptorParseException("Invalid value in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseBridgeStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.bridgeStatsEndMillis = parsedStatsEndData[0];
+ this.bridgeStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseBridgeStatsIpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIps =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseBridgeIpVersionsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIpVersions =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseBridgeIpTransportsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIpTransports =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseTransportLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.transports.add(partsNoOpt[1]);
+ }
+
+ private void parseHidservStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.hidservStatsEndMillis = parsedStatsEndData[0];
+ this.hidservStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseHidservRendRelayedCellsLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.hidservRendRelayedCells = Double.parseDouble(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hidservRendRelayedCellsParameters =
+ ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
+ partsNoOpt, 2);
+ }
+
+ private void parseHidservDirOnionsSeenLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.hidservDirOnionsSeen = Double.parseDouble(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hidservDirOnionsSeenParameters =
+ ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
+ partsNoOpt, 2);
+ }
+
+ private void parseRouterSignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("router-signature")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseRouterDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseIdentityEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseIdentityEd25519CryptoBlock(String cryptoString)
+ throws DescriptorParseException {
+ String masterKeyEd25519FromIdentityEd25519 =
+ ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
+ cryptoString);
+ if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
+ masterKeyEd25519FromIdentityEd25519)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
+ }
+
+ private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
+ if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
+ masterKeyEd25519FromMasterKeyEd25519Line)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
+ }
+
+ private void parseRouterSigEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.routerSignatureEd25519 = partsNoOpt[1];
+ }
+
+ private void parseRouterDigestSha256Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
+ this.extraInfoDigestSha256 = partsNoOpt[1];
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ if (this.extraInfoDigest != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "extra-info ";
+ String sigToken = "\nrouter-signature\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.extraInfoDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.extraInfoDigest == null) {
+ throw new DescriptorParseException("Could not calculate extra-info "
+ + "descriptor digest.");
+ }
+ }
+
+ private void calculateDigestSha256() throws DescriptorParseException {
+ if (this.extraInfoDigestSha256 != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest-sha256" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "extra-info ";
+ String sigToken = "\n-----END SIGNATURE-----\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
+ 0, sig - start);
+ this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ replaceAll("=", "");
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.extraInfoDigestSha256 == null) {
+ throw new DescriptorParseException("Could not calculate extra-info "
+ + "descriptor SHA-256 digest.");
+ }
+ }
+
+ private String extraInfoDigest;
+ @Override
+ public String getExtraInfoDigest() {
+ return this.extraInfoDigest;
+ }
+
+ private String extraInfoDigestSha256;
+ @Override
+ public String getExtraInfoDigestSha256() {
+ return this.extraInfoDigestSha256;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private BandwidthHistory readHistory;
+ @Override
+ public BandwidthHistory getReadHistory() {
+ return this.readHistory;
+ }
+
+ private BandwidthHistory writeHistory;
+ @Override
+ public BandwidthHistory getWriteHistory() {
+ return this.writeHistory;
+ }
+
+ private String geoipDbDigest;
+ @Override
+ public String getGeoipDbDigest() {
+ return this.geoipDbDigest;
+ }
+
+ private String geoip6DbDigest;
+ @Override
+ public String getGeoip6DbDigest() {
+ return this.geoip6DbDigest;
+ }
+
+ private long dirreqStatsEndMillis = -1L;
+ @Override
+ public long getDirreqStatsEndMillis() {
+ return this.dirreqStatsEndMillis;
+ }
+
+ private long dirreqStatsIntervalLength = -1L;
+ @Override
+ public long getDirreqStatsIntervalLength() {
+ return this.dirreqStatsIntervalLength;
+ }
+
+ private String dirreqV2Ips;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Ips() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Ips);
+ }
+
+ private String dirreqV3Ips;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Ips() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Ips);
+ }
+
+ private String dirreqV2Reqs;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Reqs() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Reqs);
+ }
+
+ private String dirreqV3Reqs;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Reqs() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Reqs);
+ }
+
+ private double dirreqV2Share = -1.0;
+ @Override
+ public double getDirreqV2Share() {
+ return this.dirreqV2Share;
+ }
+
+ private double dirreqV3Share = -1.0;
+ @Override
+ public double getDirreqV3Share() {
+ return this.dirreqV3Share;
+ }
+
+ private String dirreqV2Resp;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Resp() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Resp);
+ }
+
+ private String dirreqV3Resp;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Resp() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Resp);
+ }
+
+ private String dirreqV2DirectDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2DirectDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2DirectDl);
+ }
+
+ private String dirreqV3DirectDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3DirectDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3DirectDl);
+ }
+
+ private String dirreqV2TunneledDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2TunneledDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2TunneledDl);
+ }
+
+ private String dirreqV3TunneledDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3TunneledDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3TunneledDl);
+ }
+
+ private BandwidthHistory dirreqReadHistory;
+ @Override
+ public BandwidthHistory getDirreqReadHistory() {
+ return this.dirreqReadHistory;
+ }
+
+ private BandwidthHistory dirreqWriteHistory;
+ @Override
+ public BandwidthHistory getDirreqWriteHistory() {
+ return this.dirreqWriteHistory;
+ }
+
+ private long entryStatsEndMillis = -1L;
+ @Override
+ public long getEntryStatsEndMillis() {
+ return this.entryStatsEndMillis;
+ }
+
+ private long entryStatsIntervalLength = -1L;
+ @Override
+ public long getEntryStatsIntervalLength() {
+ return this.entryStatsIntervalLength;
+ }
+
+ private String entryIps;
+ @Override
+ public SortedMap<String, Integer> getEntryIps() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.entryIps);
+ }
+
+ private long cellStatsEndMillis = -1L;
+ @Override
+ public long getCellStatsEndMillis() {
+ return this.cellStatsEndMillis;
+ }
+
+ private long cellStatsIntervalLength = -1L;
+ @Override
+ public long getCellStatsIntervalLength() {
+ return this.cellStatsIntervalLength;
+ }
+
+ private Integer[] cellProcessedCells;
+ @Override
+ public List<Integer> getCellProcessedCells() {
+ return this.cellProcessedCells == null ? null :
+ Arrays.asList(this.cellProcessedCells);
+ }
+
+ private Double[] cellQueuedCells;
+ @Override
+ public List<Double> getCellQueuedCells() {
+ return this.cellQueuedCells == null ? null :
+ Arrays.asList(this.cellQueuedCells);
+ }
+
+ private Integer[] cellTimeInQueue;
+ @Override
+ public List<Integer> getCellTimeInQueue() {
+ return this.cellTimeInQueue == null ? null :
+ Arrays.asList(this.cellTimeInQueue);
+ }
+
+ private int cellCircuitsPerDecile = -1;
+ @Override
+ public int getCellCircuitsPerDecile() {
+ return this.cellCircuitsPerDecile;
+ }
+
+ private long connBiDirectStatsEndMillis = -1L;
+ @Override
+ public long getConnBiDirectStatsEndMillis() {
+ return this.connBiDirectStatsEndMillis;
+ }
+
+ private long connBiDirectStatsIntervalLength = -1L;
+ @Override
+ public long getConnBiDirectStatsIntervalLength() {
+ return this.connBiDirectStatsIntervalLength;
+ }
+
+ private int connBiDirectBelow = -1;
+ @Override
+ public int getConnBiDirectBelow() {
+ return this.connBiDirectBelow;
+ }
+
+ private int connBiDirectRead = -1;
+ @Override
+ public int getConnBiDirectRead() {
+ return this.connBiDirectRead;
+ }
+
+ private int connBiDirectWrite = -1;
+ @Override
+ public int getConnBiDirectWrite() {
+ return this.connBiDirectWrite;
+ }
+
+ private int connBiDirectBoth = -1;
+ @Override
+ public int getConnBiDirectBoth() {
+ return this.connBiDirectBoth;
+ }
+
+ private long exitStatsEndMillis = -1L;
+ @Override
+ public long getExitStatsEndMillis() {
+ return this.exitStatsEndMillis;
+ }
+
+ private long exitStatsIntervalLength = -1L;
+ @Override
+ public long getExitStatsIntervalLength() {
+ return this.exitStatsIntervalLength;
+ }
+
+ private SortedMap<String, Long> exitKibibytesWritten;
+ @Override
+ public SortedMap<String, Long> getExitKibibytesWritten() {
+ return this.exitKibibytesWritten == null ? null :
+ new TreeMap<>(this.exitKibibytesWritten);
+ }
+
+ private SortedMap<String, Long> exitKibibytesRead;
+ @Override
+ public SortedMap<String, Long> getExitKibibytesRead() {
+ return this.exitKibibytesRead == null ? null :
+ new TreeMap<>(this.exitKibibytesRead);
+ }
+
+ private SortedMap<String, Long> exitStreamsOpened;
+ @Override
+ public SortedMap<String, Long> getExitStreamsOpened() {
+ return this.exitStreamsOpened == null ? null :
+ new TreeMap<>(this.exitStreamsOpened);
+ }
+
+ private long geoipStartTimeMillis = -1L;
+ @Override
+ public long getGeoipStartTimeMillis() {
+ return this.geoipStartTimeMillis;
+ }
+
+ private String geoipClientOrigins;
+ @Override
+ public SortedMap<String, Integer> getGeoipClientOrigins() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.geoipClientOrigins);
+ }
+
+ private long bridgeStatsEndMillis = -1L;
+ @Override
+ public long getBridgeStatsEndMillis() {
+ return this.bridgeStatsEndMillis;
+ }
+
+ private long bridgeStatsIntervalLength = -1L;
+ @Override
+ public long getBridgeStatsIntervalLength() {
+ return this.bridgeStatsIntervalLength;
+ }
+
+ private String bridgeIps;
+ @Override
+ public SortedMap<String, Integer> getBridgeIps() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIps);
+ }
+
+ private String bridgeIpVersions;
+ @Override
+ public SortedMap<String, Integer> getBridgeIpVersions() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIpVersions);
+ }
+
+ private String bridgeIpTransports;
+ @Override
+ public SortedMap<String, Integer> getBridgeIpTransports() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIpTransports);
+ }
+
+ private List<String> transports = new ArrayList<>();
+ @Override
+ public List<String> getTransports() {
+ return new ArrayList<>(this.transports);
+ }
+
+ private long hidservStatsEndMillis = -1L;
+ @Override
+ public long getHidservStatsEndMillis() {
+ return this.hidservStatsEndMillis;
+ }
+
+ private long hidservStatsIntervalLength = -1L;
+ @Override
+ public long getHidservStatsIntervalLength() {
+ return this.hidservStatsIntervalLength;
+ }
+
+ private Double hidservRendRelayedCells;
+ @Override
+ public Double getHidservRendRelayedCells() {
+ return this.hidservRendRelayedCells;
+ }
+
+ private Map<String, Double> hidservRendRelayedCellsParameters;
+ @Override
+ public Map<String, Double> getHidservRendRelayedCellsParameters() {
+ return this.hidservRendRelayedCellsParameters == null ? null :
+ new HashMap<>(this.hidservRendRelayedCellsParameters);
+ }
+
+ private Double hidservDirOnionsSeen;
+ @Override
+ public Double getHidservDirOnionsSeen() {
+ return this.hidservDirOnionsSeen;
+ }
+
+ private Map<String, Double> hidservDirOnionsSeenParameters;
+ @Override
+ public Map<String, Double> getHidservDirOnionsSeenParameters() {
+ return this.hidservDirOnionsSeenParameters == null ? null :
+ new HashMap<>(this.hidservDirOnionsSeenParameters);
+ }
+
+ private String routerSignature;
+ @Override
+ public String getRouterSignature() {
+ return this.routerSignature;
+ }
+
+ private String identityEd25519;
+ @Override
+ public String getIdentityEd25519() {
+ return this.identityEd25519;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+
+ private String routerSignatureEd25519;
+ @Override
+ public String getRouterSignatureEd25519() {
+ return this.routerSignatureEd25519;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
new file mode 100644
index 0000000..4931c31
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -0,0 +1,328 @@
+/* Copyright 2014--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.Microdescriptor;
+
+/* Contains a microdescriptor. */
+public class MicrodescriptorImpl extends DescriptorImpl
+ implements Microdescriptor {
+
+ protected static List<Microdescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<Microdescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "onion-key\n");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ Microdescriptor parsedDescriptor =
+ new MicrodescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected MicrodescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ this.calculateDigest();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
+ "onion-key".split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "ntor-onion-key,family,p,p6,id").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("onion-key");
+ this.clearParsedKeywords();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) {
+ continue;
+ }
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "onion-key":
+ this.parseOnionKeyLine(line, parts);
+ nextCrypto = "onion-key";
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, parts);
+ break;
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "family":
+ this.parseFamilyLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "p6":
+ this.parseP6Line(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("onion-key")) {
+ this.onionKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in microdescriptor.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ ParseHelper.parseKeyword(line, parts[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in microdescriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseOnionKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("onion-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.ntorOnionKey = parts[1].replaceAll("=", "");
+ }
+
+ private void parseALine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(parts[1]);
+ }
+
+ private void parseFamilyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ String[] familyEntries = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ if (parts[i].startsWith("$")) {
+ if (parts[i].contains("=") ^ parts[i].contains("~")) {
+ String separator = parts[i].contains("=") ? "=" : "~";
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[i].substring(1, parts[i].indexOf(separator)));
+ String nickname = ParseHelper.parseNickname(line,
+ parts[i].substring(parts[i].indexOf(separator) + 1));
+ familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
+ } else {
+ familyEntries[i - 1] = "$"
+ + ParseHelper.parseTwentyByteHexString(line,
+ parts[i].substring(1));
+ }
+ } else {
+ familyEntries[i - 1] = ParseHelper.parseNickname(line, parts[i]);
+ }
+ }
+ this.familyEntries = familyEntries;
+ }
+
+ private void parsePLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validatePOrP6Line(line, parts);
+ this.defaultPolicy = parts[1];
+ this.portList = parts[2];
+ }
+
+ private void parseP6Line(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validatePOrP6Line(line, parts);
+ this.ipv6DefaultPolicy = parts[1];
+ this.ipv6PortList = parts[2];
+ }
+
+ private void validatePOrP6Line(String line, String[] parts)
+ throws DescriptorParseException {
+ boolean isValid = true;
+ if (parts.length != 3) {
+ isValid = false;
+ } else {
+ switch (parts[1]) {
+ case "accept":
+ case "reject":
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseIdLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else {
+ switch (parts[1]) {
+ case "ed25519":
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
+ this.ed25519Identity = parts[2];
+ break;
+ case "rsa1024":
+ ParseHelper.parseTwentyByteBase64String(line, parts[2]);
+ this.rsa1024Identity = parts[2];
+ break;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "onion-key\n";
+ int start = ascii.indexOf(startToken);
+ int end = ascii.length();
+ if (start >= 0 && end > start) {
+ byte[] forDigest = new byte[end - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, end - start);
+ this.microdescriptorDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.microdescriptorDigest == null) {
+ throw new DescriptorParseException("Could not calculate "
+ + "microdescriptor digest.");
+ }
+ }
+
+ private String microdescriptorDigest;
+ @Override
+ public String getMicrodescriptorDigest() {
+ return this.microdescriptorDigest;
+ }
+
+ private String onionKey;
+ @Override
+ public String getOnionKey() {
+ return this.onionKey;
+ }
+
+ private String ntorOnionKey;
+ @Override
+ public String getNtorOnionKey() {
+ return this.ntorOnionKey;
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private String[] familyEntries;
+ @Override
+ public List<String> getFamilyEntries() {
+ return this.familyEntries == null ? null :
+ Arrays.asList(this.familyEntries);
+ }
+ private String defaultPolicy;
+ @Override
+ public String getDefaultPolicy() {
+ return this.defaultPolicy;
+ }
+
+ private String portList;
+ @Override
+ public String getPortList() {
+ return this.portList;
+ }
+
+ private String ipv6DefaultPolicy;
+ @Override
+ public String getIpv6DefaultPolicy() {
+ return this.ipv6DefaultPolicy;
+ }
+
+ private String ipv6PortList;
+ @Override
+ public String getIpv6PortList() {
+ return this.ipv6PortList;
+ }
+
+ private String rsa1024Identity;
+ @Override
+ public String getRsa1024Identity() {
+ return this.rsa1024Identity;
+ }
+
+ private String ed25519Identity;
+ @Override
+ public String getEd25519Identity() {
+ return this.ed25519Identity;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
new file mode 100644
index 0000000..b73d211
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -0,0 +1,382 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.NetworkStatusEntry;
+
+public class NetworkStatusEntryImpl implements NetworkStatusEntry {
+
+ private byte[] statusEntryBytes;
+ @Override
+ public byte[] getStatusEntryBytes() {
+ return this.statusEntryBytes;
+ }
+
+ private boolean microdescConsensus;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected NetworkStatusEntryImpl(byte[] statusEntryBytes,
+ boolean microdescConsensus, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.statusEntryBytes = statusEntryBytes;
+ this.microdescConsensus = microdescConsensus;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseStatusEntryBytes();
+ this.clearAtMostOnceKeywords();
+ }
+
+ private SortedSet<String> atMostOnceKeywords;
+ private void initializeKeywords() {
+ this.atMostOnceKeywords = new TreeSet<>();
+ this.atMostOnceKeywords.add("s");
+ this.atMostOnceKeywords.add("v");
+ this.atMostOnceKeywords.add("w");
+ this.atMostOnceKeywords.add("p");
+ }
+
+ private void parsedAtMostOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.atMostOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in status entry.");
+ }
+ this.atMostOnceKeywords.remove(keyword);
+ }
+
+ private void parseStatusEntryBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.statusEntryBytes)).
+ useDelimiter("\n");
+ String line = null;
+ if (!s.hasNext() || !(line = s.next()).startsWith("r ")) {
+ throw new DescriptorParseException("Status entry must start with "
+ + "an r line.");
+ }
+ String[] rLineParts = line.split("[ \t]+");
+ this.parseRLine(line, rLineParts);
+ while (s.hasNext()) {
+ line = s.next();
+ String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") :
+ line.substring("opt ".length()).split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "s":
+ this.parseSLine(line, parts);
+ break;
+ case "v":
+ this.parseVLine(line, parts);
+ break;
+ case "w":
+ this.parseWLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "m":
+ this.parseMLine(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in status entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseRLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if ((!this.microdescConsensus && parts.length != 9) ||
+ (this.microdescConsensus && parts.length != 8)) {
+ throw new DescriptorParseException("r line '" + line + "' has "
+ + "fewer space-separated elements than expected.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ this.fingerprint = ParseHelper.parseTwentyByteBase64String(line,
+ parts[2]);
+ int descriptorOffset = 0;
+ if (!this.microdescConsensus) {
+ this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
+ parts[3]);
+ descriptorOffset = 1;
+ }
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 3 + descriptorOffset, 4 + descriptorOffset);
+ this.address = ParseHelper.parseIpv4Address(line,
+ parts[5 + descriptorOffset]);
+ this.orPort = ParseHelper.parsePort(line,
+ parts[6 + descriptorOffset]);
+ this.dirPort = ParseHelper.parsePort(line,
+ parts[7 + descriptorOffset]);
+ }
+
+ private void parseALine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "status entry.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(parts[1]);
+ }
+
+ private static Map<String, Integer> flagIndexes = new HashMap<>();
+ private static Map<Integer, String> flagStrings = new HashMap<>();
+
+ private void parseSLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("s");
+ BitSet flags = new BitSet(flagIndexes.size());
+ for (int i = 1; i < parts.length; i++) {
+ String flag = parts[i];
+ if (!flagIndexes.containsKey(flag)) {
+ flagStrings.put(flagIndexes.size(), flag);
+ flagIndexes.put(flag, flagIndexes.size());
+ }
+ flags.set(flagIndexes.get(flag));
+ }
+ this.flags = flags;
+ }
+
+ private void parseVLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("v");
+ String noOptLine = line;
+ if (noOptLine.startsWith("opt ")) {
+ noOptLine = noOptLine.substring(4);
+ }
+ if (noOptLine.length() < 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "status entry.");
+ } else {
+ this.version = noOptLine.substring(2);
+ }
+ }
+
+ private void parseWLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("w");
+ SortedMap<String, Integer> pairs =
+ ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "=");
+ if (pairs.isEmpty()) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ if (pairs.containsKey("Bandwidth")) {
+ this.bandwidth = pairs.remove("Bandwidth");
+ }
+ if (pairs.containsKey("Measured")) {
+ this.measured = pairs.remove("Measured");
+ }
+ if (pairs.containsKey("Unmeasured")) {
+ this.unmeasured = pairs.remove("Unmeasured") == 1L;
+ }
+ if (!pairs.isEmpty()) {
+ /* Ignore unknown key-value pair. */
+ }
+ }
+
+ private void parsePLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("p");
+ boolean isValid = true;
+ if (parts.length != 3) {
+ isValid = false;
+ } else {
+ switch (parts[1]) {
+ case "accept":
+ case "reject":
+ this.defaultPolicy = parts[1];
+ this.portList = parts[2];
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseMLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (this.microdescriptorDigests == null) {
+ this.microdescriptorDigests = new HashSet<>();
+ }
+ if (parts.length == 2) {
+ this.microdescriptorDigests.add(
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[1]));
+ } else if (parts.length == 3 && parts[2].length() > 7) {
+ /* 7 == "sha256=".length() */
+ this.microdescriptorDigests.add(
+ ParseHelper.parseThirtyTwoByteBase64String(line,
+ parts[2].substring(7)));
+ }
+ }
+
+ private void parseIdLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3 || !"ed25519".equals(parts[1])) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else if ("none".equals(parts[2])) {
+ this.masterKeyEd25519 = "none";
+ } else {
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
+ this.masterKeyEd25519 = parts[2];
+ }
+ }
+
+ private void clearAtMostOnceKeywords() {
+ this.atMostOnceKeywords = null;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String descriptor;
+ @Override
+ public String getDescriptor() {
+ return this.descriptor;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private Set<String> microdescriptorDigests;
+ @Override
+ public Set<String> getMicrodescriptorDigests() {
+ return this.microdescriptorDigests == null ? null :
+ new HashSet<>(this.microdescriptorDigests);
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private BitSet flags;
+ @Override
+ public SortedSet<String> getFlags() {
+ SortedSet<String> result = new TreeSet<>();
+ if (this.flags != null) {
+ for (int i = this.flags.nextSetBit(0); i >= 0;
+ i = this.flags.nextSetBit(i + 1)) {
+ result.add(flagStrings.get(i));
+ }
+ }
+ return result;
+ }
+
+ private String version;
+ @Override
+ public String getVersion() {
+ return this.version;
+ }
+
+ private long bandwidth = -1L;
+ @Override
+ public long getBandwidth() {
+ return this.bandwidth;
+ }
+
+ private long measured = -1L;
+ @Override
+ public long getMeasured() {
+ return this.measured;
+ }
+
+ private boolean unmeasured = false;
+ @Override
+ public boolean getUnmeasured() {
+ return this.unmeasured;
+ }
+
+ private String defaultPolicy;
+ @Override
+ public String getDefaultPolicy() {
+ return this.defaultPolicy;
+ }
+
+ private String portList;
+ @Override
+ public String getPortList() {
+ return this.portList;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
new file mode 100644
index 0000000..5fa22c7
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -0,0 +1,270 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.DirectorySignature;
+import org.torproject.descriptor.NetworkStatusEntry;
+
+/* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
+ * consensuses, v2 statuses, and sanitized bridge network statuses and
+ * delegate the specific parts to the subclasses. */
+public abstract class NetworkStatusImpl extends DescriptorImpl {
+
+ protected NetworkStatusImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines,
+ boolean containsDirSourceEntries, boolean blankLinesAllowed)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines,
+ blankLinesAllowed);
+ this.splitAndParseParts(this.rawDescriptorBytes,
+ containsDirSourceEntries);
+ }
+
+ private void splitAndParseParts(byte[] rawDescriptorBytes,
+ boolean containsDirSourceEntries) throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ int startIndex = 0;
+ int firstDirSourceIndex = !containsDirSourceEntries ? -1 :
+ this.findFirstIndexOfKeyword(descriptorString, "dir-source");
+ int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r");
+ int directoryFooterIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-footer");
+ int firstDirectorySignatureIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-signature");
+ int endIndex = descriptorString.length();
+ if (firstDirectorySignatureIndex < 0) {
+ firstDirectorySignatureIndex = endIndex;
+ }
+ if (directoryFooterIndex < 0) {
+ directoryFooterIndex = firstDirectorySignatureIndex;
+ }
+ if (firstRIndex < 0) {
+ firstRIndex = directoryFooterIndex;
+ }
+ if (firstDirSourceIndex < 0) {
+ firstDirSourceIndex = firstRIndex;
+ }
+ if (firstDirSourceIndex > startIndex) {
+ this.parseHeaderBytes(descriptorString, startIndex,
+ firstDirSourceIndex);
+ }
+ if (firstRIndex > firstDirSourceIndex) {
+ this.parseDirSourceBytes(descriptorString, firstDirSourceIndex,
+ firstRIndex);
+ }
+ if (directoryFooterIndex > firstRIndex) {
+ this.parseStatusEntryBytes(descriptorString, firstRIndex,
+ directoryFooterIndex);
+ }
+ if (firstDirectorySignatureIndex > directoryFooterIndex) {
+ this.parseDirectoryFooterBytes(descriptorString,
+ directoryFooterIndex, firstDirectorySignatureIndex);
+ }
+ if (endIndex > firstDirectorySignatureIndex) {
+ this.parseDirectorySignatureBytes(descriptorString,
+ firstDirectorySignatureIndex, endIndex);
+ }
+ }
+
+ private int findFirstIndexOfKeyword(String descriptorString,
+ String keyword) {
+ if (descriptorString.startsWith(keyword)) {
+ return 0;
+ } else if (descriptorString.contains("\n" + keyword + " ")) {
+ return descriptorString.indexOf("\n" + keyword + " ") + 1;
+ } else if (descriptorString.contains("\n" + keyword + "\n")) {
+ return descriptorString.indexOf("\n" + keyword + "\n") + 1;
+ } else {
+ return -1;
+ }
+ }
+
+ private void parseHeaderBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ byte[] headerBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ headerBytes, 0, end - start);
+ this.parseHeader(headerBytes);
+ }
+
+ private void parseDirSourceBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ List<byte[]> splitDirSourceBytes =
+ this.splitByKeyword(descriptorString, "dir-source", start, end);
+ for (byte[] dirSourceBytes : splitDirSourceBytes) {
+ this.parseDirSource(dirSourceBytes);
+ }
+ }
+
+ private void parseStatusEntryBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ List<byte[]> splitStatusEntryBytes =
+ this.splitByKeyword(descriptorString, "r", start, end);
+ for (byte[] statusEntryBytes : splitStatusEntryBytes) {
+ this.parseStatusEntry(statusEntryBytes);
+ }
+ }
+
+ private void parseDirectoryFooterBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ byte[] directoryFooterBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ directoryFooterBytes, 0, end - start);
+ this.parseFooter(directoryFooterBytes);
+ }
+
+ private void parseDirectorySignatureBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
+ descriptorString, "directory-signature", start, end);
+ for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
+ this.parseDirectorySignature(directorySignatureBytes);
+ }
+ }
+
+ private List<byte[]> splitByKeyword(String descriptorString,
+ String keyword, int start, int end) {
+ List<byte[]> splitParts = new ArrayList<>();
+ int from = start;
+ while (from < end) {
+ int to = descriptorString.indexOf("\n" + keyword + " ", from);
+ if (to < 0) {
+ to = descriptorString.indexOf("\n" + keyword + "\n", from);
+ }
+ if (to < 0) {
+ to = end;
+ } else {
+ to += 1;
+ }
+ byte[] part = new byte[to - from];
+ System.arraycopy(this.rawDescriptorBytes, from, part, 0,
+ to - from);
+ from = to;
+ splitParts.add(part);
+ }
+ return splitParts;
+ }
+
+ protected abstract void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException;
+
+ protected void parseDirSource(byte[] dirSourceBytes)
+ throws DescriptorParseException {
+ DirSourceEntryImpl dirSourceEntry = new DirSourceEntryImpl(
+ dirSourceBytes, this.failUnrecognizedDescriptorLines);
+ this.dirSourceEntries.put(dirSourceEntry.getIdentity(),
+ dirSourceEntry);
+ List<String> unrecognizedDirSourceLines = dirSourceEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedDirSourceLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedDirSourceLines);
+ }
+ }
+
+ protected String[] parseClientOrServerVersions(String line,
+ String[] parts) throws DescriptorParseException {
+ String[] result = null;
+ switch (parts.length) {
+ case 1:
+ result = new String[0];
+ break;
+ case 2:
+ result = parts[1].split(",", -1);
+ for (String version : result) {
+ if (version.length() < 1) {
+ throw new DescriptorParseException("Illegal versions line '"
+ + line + "'.");
+ }
+ }
+ break;
+ default:
+ throw new DescriptorParseException("Illegal versions line '" + line
+ + "'.");
+ }
+ return result;
+ }
+
+ protected void parseStatusEntry(byte[] statusEntryBytes)
+ throws DescriptorParseException {
+ NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
+ statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
+ this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
+ List<String> unrecognizedStatusEntryLines = statusEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected abstract void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException;
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ if (this.signatures == null) {
+ this.signatures = new ArrayList<>();
+ }
+ DirectorySignatureImpl signature = new DirectorySignatureImpl(
+ directorySignatureBytes, failUnrecognizedDescriptorLines);
+ this.signatures.add(signature);
+ List<String> unrecognizedStatusEntryLines = signature.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected SortedMap<String, DirSourceEntry> dirSourceEntries =
+ new TreeMap<>();
+ public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
+ return new TreeMap<>(this.dirSourceEntries);
+ }
+
+ protected SortedMap<String, NetworkStatusEntry> statusEntries =
+ new TreeMap<>();
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
+ return new TreeMap<>(this.statusEntries);
+ }
+ public boolean containsStatusEntry(String fingerprint) {
+ return this.statusEntries.containsKey(fingerprint);
+ }
+ public NetworkStatusEntry getStatusEntry(String fingerprint) {
+ return this.statusEntries.get(fingerprint);
+ }
+
+ protected List<DirectorySignature> signatures;
+ public List<DirectorySignature> getSignatures() {
+ return this.signatures == null ? null
+ : new ArrayList<>(this.signatures);
+ }
+ public SortedMap<String, DirectorySignature> getDirectorySignatures() {
+ SortedMap<String, DirectorySignature> directorySignatures = null;
+ if (this.signatures != null) {
+ directorySignatures = new TreeMap<>();
+ for (DirectorySignature signature : this.signatures) {
+ directorySignatures.put(signature.getIdentity(), signature);
+ }
+ }
+ return directorySignatures;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
new file mode 100644
index 0000000..82c0813
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -0,0 +1,567 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+
+public class ParseHelper {
+
+ private static Pattern keywordPattern =
+ Pattern.compile("^[A-Za-z0-9-]+$");
+ protected static String parseKeyword(String line, String keyword)
+ throws DescriptorParseException {
+ if (!keywordPattern.matcher(keyword).matches()) {
+ throw new DescriptorParseException("Unrecognized character in "
+ + "keyword '" + keyword + "' in line '" + line + "'.");
+ }
+ return keyword;
+ }
+
+ private static Pattern ipv4Pattern =
+ Pattern.compile("^[0-9\\.]{7,15}$");
+ protected static String parseIpv4Address(String line, String address)
+ throws DescriptorParseException {
+ boolean isValid = true;
+ if (!ipv4Pattern.matcher(address).matches()) {
+ isValid = false;
+ } else {
+ String[] parts = address.split("\\.", -1);
+ if (parts.length != 4) {
+ isValid = false;
+ } else {
+ for (int i = 0; i < 4; i++) {
+ try {
+ int octetValue = Integer.parseInt(parts[i]);
+ if (octetValue < 0 || octetValue > 255) {
+ isValid = false;
+ }
+ } catch (NumberFormatException e) {
+ isValid = false;
+ }
+ }
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("'" + address + "' in line '"
+ + line + "' is not a valid IPv4 address.");
+ }
+ return address;
+ }
+
+ protected static int parsePort(String line, String portString)
+ throws DescriptorParseException {
+ int port = -1;
+ try {
+ port = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("'" + portString + "' in line '"
+ + line + "' is not a valid port number.");
+ }
+ if (port < 0 || port > 65535) {
+ throw new DescriptorParseException("'" + portString + "' in line '"
+ + line + "' is not a valid port number.");
+ }
+ return port;
+ }
+
+ protected static long parseSeconds(String line, String secondsString)
+ throws DescriptorParseException {
+ try {
+ return Long.parseLong(secondsString);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("'" + secondsString + "' in "
+ + "line '" + line + "' is not a valid time in seconds.");
+ }
+ }
+
+ protected static String parseExitPattern(String line, String exitPattern)
+ throws DescriptorParseException {
+ if (!exitPattern.contains(":")) {
+ throw new DescriptorParseException("'" + exitPattern + "' in line '"
+ + line + "' must contain address and port.");
+ }
+ String[] parts = exitPattern.split(":");
+ String addressPart = parts[0];
+ /* TODO Extend to IPv6. */
+ if (addressPart.equals("*")) {
+ /* Nothing to check. */
+ } else if (addressPart.contains("/")) {
+ String[] addressParts = addressPart.split("/");
+ String address = addressParts[0];
+ String mask = addressParts[1];
+ ParseHelper.parseIpv4Address(line, address);
+ if (addressParts.length != 2) {
+ throw new DescriptorParseException("'" + addressPart + "' in "
+ + "line '" + line + "' is not a valid address part.");
+ }
+ if (mask.contains(".")) {
+ ParseHelper.parseIpv4Address(line, mask);
+ } else {
+ int maskValue = -1;
+ try {
+ maskValue = Integer.parseInt(mask);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (maskValue < 0 || maskValue > 32) {
+ throw new DescriptorParseException("'" + mask + "' in line '"
+ + line + "' is not a valid IPv4 mask.");
+ }
+ }
+ } else {
+ ParseHelper.parseIpv4Address(line, addressPart);
+ }
+ String portPart = parts[1];
+ if (portPart.equals("*")) {
+ /* Nothing to check. */
+ } else if (portPart.contains("-")) {
+ String[] portParts = portPart.split("-");
+ String fromPort = portParts[0];
+ ParseHelper.parsePort(line, fromPort);
+ String toPort = portParts[1];
+ ParseHelper.parsePort(line, toPort);
+ } else {
+ ParseHelper.parsePort(line, portPart);
+ }
+ return exitPattern;
+ }
+
+ private static ThreadLocal<Map<String, DateFormat>> dateFormats =
+ new ThreadLocal<Map<String, DateFormat>> () {
+ public Map<String, DateFormat> get() {
+ return super.get();
+ }
+ protected Map<String, DateFormat> initialValue() {
+ return new HashMap<>();
+ }
+ public void remove() {
+ super.remove();
+ }
+ public void set(Map<String, DateFormat> value) {
+ super.set(value);
+ }
+ };
+ static DateFormat getDateFormat(String format) {
+ Map<String, DateFormat> threadDateFormats = dateFormats.get();
+ if (!threadDateFormats.containsKey(format)) {
+ DateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
+ dateFormat.setLenient(false);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ threadDateFormats.put(format, dateFormat);
+ }
+ return threadDateFormats.get(format);
+ }
+
+ protected static long parseTimestampAtIndex(String line, String[] parts,
+ int dateIndex, int timeIndex) throws DescriptorParseException {
+ if (dateIndex >= parts.length || timeIndex >= parts.length) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a timestamp at the expected position.");
+ }
+ long result = -1L;
+ try {
+ DateFormat dateTimeFormat = getDateFormat("yyyy-MM-dd HH:mm:ss");
+ result = dateTimeFormat.parse(
+ parts[dateIndex] + " " + parts[timeIndex]).getTime();
+ } catch (ParseException e) {
+ /* Leave result at -1L. */
+ }
+ if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
+ throw new DescriptorParseException("Illegal timestamp format in "
+ + "line '" + line + "'.");
+ }
+ return result;
+ }
+
+ protected static long parseDateAtIndex(String line, String[] parts,
+ int dateIndex) throws DescriptorParseException {
+ if (dateIndex >= parts.length) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a date at the expected position.");
+ }
+ long result = -1L;
+ try {
+ DateFormat dateFormat = getDateFormat("yyyy-MM-dd");
+ result = dateFormat.parse(parts[dateIndex]).getTime();
+ } catch (ParseException e) {
+ /* Leave result at -1L. */
+ }
+ if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
+ throw new DescriptorParseException("Illegal date format in line '"
+ + line + "'.");
+ }
+ return result;
+ }
+
+ protected static String parseTwentyByteHexString(String line,
+ String hexString) throws DescriptorParseException {
+ return parseHexString(line, hexString, 40);
+ }
+
+ protected static String parseHexString(String line, String hexString)
+ throws DescriptorParseException {
+ return parseHexString(line, hexString, -1);
+ }
+
+ private static Pattern hexPattern = Pattern.compile("^[0-9a-fA-F]*$");
+ private static String parseHexString(String line, String hexString,
+ int expectedLength) throws DescriptorParseException {
+ if (!hexPattern.matcher(hexString).matches() ||
+ hexString.length() % 2 != 0 ||
+ (expectedLength >= 0 && hexString.length() != expectedLength)) {
+ throw new DescriptorParseException("Illegal hex string in line '"
+ + line + "'.");
+ }
+ return hexString.toUpperCase();
+ }
+
+ protected static SortedMap<String, String> parseKeyValueStringPairs(
+ String line, String[] parts, int startIndex, String separatorString)
+ throws DescriptorParseException {
+ SortedMap<String, String> result = new TreeMap<>();
+ for (int i = startIndex; i < parts.length; i++) {
+ String pair = parts[i];
+ String[] pairParts = pair.split(separatorString);
+ if (pairParts.length != 2) {
+ throw new DescriptorParseException("Illegal key-value pair in "
+ + "line '" + line + "'.");
+ }
+ result.put(pairParts[0], pairParts[1]);
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Integer> parseKeyValueIntegerPairs(
+ String line, String[] parts, int startIndex, String separatorString)
+ throws DescriptorParseException {
+ SortedMap<String, Integer> result = new TreeMap<>();
+ SortedMap<String, String> keyValueStringPairs =
+ ParseHelper.parseKeyValueStringPairs(line, parts, startIndex,
+ separatorString);
+ for (Map.Entry<String, String> e : keyValueStringPairs.entrySet()) {
+ try {
+ result.put(e.getKey(), Integer.parseInt(e.getValue()));
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+ return result;
+ }
+
+ private static Pattern nicknamePattern =
+ Pattern.compile("^[0-9a-zA-Z]{1,19}$");
+ protected static String parseNickname(String line, String nickname)
+ throws DescriptorParseException {
+ if (!nicknamePattern.matcher(nickname).matches()) {
+ throw new DescriptorParseException("Illegal nickname in line '"
+ + line + "'.");
+ }
+ return nickname;
+ }
+
+ protected static boolean parseBoolean(String b, String line)
+ throws DescriptorParseException {
+ switch (b) {
+ case "1":
+ return true;
+ case "0":
+ return false;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private static Pattern twentyByteBase64Pattern =
+ Pattern.compile("^[0-9a-zA-Z+/]{27}$");
+ protected static String parseTwentyByteBase64String(String line,
+ String base64String) throws DescriptorParseException {
+ if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
+ throw new DescriptorParseException("'" + base64String
+ + "' in line '" + line + "' is not a valid base64-encoded "
+ + "20-byte value.");
+ }
+ return DatatypeConverter.printHexBinary(
+ DatatypeConverter.parseBase64Binary(base64String + "=")).
+ toUpperCase();
+ }
+
+ private static Pattern thirtyTwoByteBase64Pattern =
+ Pattern.compile("^[0-9a-zA-Z+/]{43}$");
+ protected static String parseThirtyTwoByteBase64String(String line,
+ String base64String) throws DescriptorParseException {
+ if (!thirtyTwoByteBase64Pattern.matcher(base64String).matches()) {
+ throw new DescriptorParseException("'" + base64String
+ + "' in line '" + line + "' is not a valid base64-encoded "
+ + "32-byte value.");
+ }
+ return DatatypeConverter.printHexBinary(
+ DatatypeConverter.parseBase64Binary(base64String + "=")).
+ toUpperCase();
+ }
+
+ private static Map<Integer, Pattern>
+ commaSeparatedKeyValueListPatterns = new HashMap<>();
+ protected static String parseCommaSeparatedKeyIntegerValueList(
+ String line, String[] partsNoOpt, int index, int keyLength)
+ throws DescriptorParseException {
+ String result = "";
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list at index " + index + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected key-value list at "
+ + "index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ if (!commaSeparatedKeyValueListPatterns.containsKey(keyLength)) {
+ String keyPattern = "[0-9a-zA-Z?<>\\-_]"
+ + (keyLength == 0 ? "+" : "{" + keyLength + "}");
+ String valuePattern = "\\-?[0-9]{1,9}";
+ String patternString = String.format("^%s=%s(,%s=%s)*$",
+ keyPattern, valuePattern, keyPattern, valuePattern);
+ commaSeparatedKeyValueListPatterns.put(keyLength,
+ Pattern.compile(patternString));
+ }
+ Pattern pattern = commaSeparatedKeyValueListPatterns.get(
+ keyLength);
+ if (pattern.matcher(partsNoOpt[index]).matches()) {
+ result = partsNoOpt[index];
+ } else {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal key or value.");
+ }
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Integer>
+ convertCommaSeparatedKeyIntegerValueList(String validatedString) {
+ SortedMap<String, Integer> result = null;
+ if (validatedString != null) {
+ result = new TreeMap<>();
+ if (validatedString.contains("=")) {
+ for (String listElement : validatedString.split(",", -1)) {
+ String[] keyAndValue = listElement.split("=");
+ result.put(keyAndValue[0], Integer.parseInt(keyAndValue[1]));
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Long>
+ parseCommaSeparatedKeyLongValueList(String line,
+ String[] partsNoOpt, int index, int keyLength)
+ throws DescriptorParseException {
+ SortedMap<String, Long> result = new TreeMap<>();
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list at index " + index + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected key-value list at "
+ + "index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ for (String listElement : listElements) {
+ String[] keyAndValue = listElement.split("=");
+ String key = null;
+ long value = -1;
+ if (keyAndValue.length == 2 && (keyLength == 0 ||
+ keyAndValue[0].length() == keyLength)) {
+ try {
+ value = Long.parseLong(keyAndValue[1]);
+ key = keyAndValue[0];
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (key == null) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal key or value in list element '"
+ + listElement + "'.");
+ }
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+ protected static Integer[] parseCommaSeparatedIntegerValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ Integer[] result = null;
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ result = new Integer[listElements.length];
+ for (int i = 0; i < listElements.length; i++) {
+ try {
+ result[i] = Integer.parseInt(listElements[i]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElements[i] + "'.");
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static Double[] parseCommaSeparatedDoubleValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ Double[] result = null;
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ result = new Double[listElements.length];
+ for (int i = 0; i < listElements.length; i++) {
+ try {
+ result[i] = Double.parseDouble(listElements[i]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElements[i] + "'.");
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static Map<String, Double>
+ parseSpaceSeparatedStringKeyDoubleValueMap(String line,
+ String[] partsNoOpt, int startIndex)
+ throws DescriptorParseException {
+ Map<String, Double> result = new LinkedHashMap<>();
+ if (partsNoOpt.length < startIndex) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list starting at index " + startIndex
+ + ".");
+ }
+ for (int i = startIndex; i < partsNoOpt.length; i++) {
+ String listElement = partsNoOpt[i];
+ String[] keyAndValue = listElement.split("=");
+ String key = null;
+ Double value = null;
+ if (keyAndValue.length == 2) {
+ try {
+ value = Double.parseDouble(keyAndValue[1]);
+ key = keyAndValue[0];
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (key == null) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "an illegal key or value in list element '" + listElement
+ + "'.");
+ }
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ protected static String
+ parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
+ String identityEd25519CryptoBlock) throws DescriptorParseException {
+ String identityEd25519CryptoBlockNoNewlines =
+ identityEd25519CryptoBlock.replaceAll("\n", "");
+ String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
+ endEd25519CertLine = "-----END ED25519 CERT-----";
+ if (!identityEd25519CryptoBlockNoNewlines.startsWith(
+ beginEd25519CertLine)) {
+ throw new DescriptorParseException("Illegal start of "
+ + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ + "'.");
+ }
+ if (!identityEd25519CryptoBlockNoNewlines.endsWith(
+ endEd25519CertLine)) {
+ throw new DescriptorParseException("Illegal end of "
+ + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ + "'.");
+ }
+ String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
+ substring(beginEd25519CertLine.length(),
+ identityEd25519CryptoBlock.length()
+ - endEd25519CertLine.length()).replaceAll("=", "");
+ byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
+ identityEd25519Base64);
+ if (identityEd25519.length < 40) {
+ throw new DescriptorParseException("Invalid length of "
+ + "identity-ed25519 (in bytes): " + identityEd25519.length);
+ } else if (identityEd25519[0] != 0x01) {
+ throw new DescriptorParseException("Unknown version in "
+ + "identity-ed25519: " + identityEd25519[0]);
+ } else if (identityEd25519[1] != 0x04) {
+ throw new DescriptorParseException("Unknown cert type in "
+ + "identity-ed25519: " + identityEd25519[1]);
+ } else if (identityEd25519[6] != 0x01) {
+ throw new DescriptorParseException("Unknown certified key type in "
+ + "identity-ed25519: " + identityEd25519[1]);
+ } else if (identityEd25519[39] == 0x00) {
+ throw new DescriptorParseException("No extensions in "
+ + "identity-ed25519 (which would contain the encoded "
+ + "master-key-ed25519): " + identityEd25519[39]);
+ } else {
+ int extensionStart = 40;
+ for (int i = 0; i < (int) identityEd25519[39]; i++) {
+ if (identityEd25519.length < extensionStart + 4) {
+ throw new DescriptorParseException("Invalid extension with id "
+ + i + " in identity-ed25519.");
+ }
+ int extensionLength = identityEd25519[extensionStart];
+ extensionLength <<= 8;
+ extensionLength += identityEd25519[extensionStart + 1];
+ int extensionType = identityEd25519[extensionStart + 2];
+ if (extensionLength == 32 && extensionType == 4) {
+ if (identityEd25519.length < extensionStart + 4 + 32) {
+ throw new DescriptorParseException("Invalid extension with "
+ + "id " + i + " in identity-ed25519.");
+ }
+ byte[] masterKeyEd25519 = new byte[32];
+ System.arraycopy(identityEd25519, extensionStart + 4,
+ masterKeyEd25519, 0, masterKeyEd25519.length);
+ String masterKeyEd25519Base64 = DatatypeConverter.
+ printBase64Binary(masterKeyEd25519).replaceAll("=", "");
+ String masterKeyEd25519Base64NoTrailingEqualSigns =
+ masterKeyEd25519Base64.replaceAll("=", "");
+ return masterKeyEd25519Base64NoTrailingEqualSigns;
+ }
+ extensionStart += 4 + extensionLength;
+ }
+ }
+ throw new DescriptorParseException("Unable to locate "
+ + "master-key-ed25519 in identity-ed25519.");
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
new file mode 100644
index 0000000..1ff15cb
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
@@ -0,0 +1,547 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayDirectory;
+import org.torproject.descriptor.RouterStatusEntry;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* TODO Write unit tests. */
+
+public class RelayDirectoryImpl extends DescriptorImpl
+ implements RelayDirectory {
+
+ protected static List<RelayDirectory> parseDirectories(
+ byte[] directoriesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayDirectory> parsedDirectories = new ArrayList<>();
+ List<byte[]> splitDirectoriesBytes =
+ DescriptorImpl.splitRawDescriptorBytes(directoriesBytes,
+ "signed-directory\n");
+ for (byte[] directoryBytes : splitDirectoriesBytes) {
+ RelayDirectory parsedDirectory =
+ new RelayDirectoryImpl(directoryBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDirectories.add(parsedDirectory);
+ }
+ return parsedDirectories;
+ }
+
+ protected RelayDirectoryImpl(byte[] directoryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(directoryBytes, failUnrecognizedDescriptorLines, true);
+ this.splitAndParseParts(rawDescriptorBytes);
+ this.calculateDigest();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "signed-directory,recommended-software,"
+ + "directory-signature").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList(
+ "dir-signing-key,running-routers,router-status".split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("signed-directory");
+ this.clearParsedKeywords();
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "signed-directory\n";
+ String sigToken = "\ndirectory-signature ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ sig = ascii.indexOf("\n", sig) + 1;
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.directoryDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.directoryDigest == null) {
+ throw new DescriptorParseException("Could not calculate v1 "
+ + "directory digest.");
+ }
+ }
+
+ private void splitAndParseParts(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ int startIndex = 0;
+ int firstRouterIndex = this.findFirstIndexOfKeyword(descriptorString,
+ "router");
+ int directorySignatureIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-signature");
+ int endIndex = descriptorString.length();
+ if (directorySignatureIndex < 0) {
+ directorySignatureIndex = endIndex;
+ }
+ if (firstRouterIndex < 0) {
+ firstRouterIndex = directorySignatureIndex;
+ }
+ if (firstRouterIndex > startIndex) {
+ this.parseHeaderBytes(descriptorString, startIndex,
+ firstRouterIndex);
+ }
+ if (directorySignatureIndex > firstRouterIndex) {
+ this.parseServerDescriptorBytes(descriptorString, firstRouterIndex,
+ directorySignatureIndex);
+ }
+ if (endIndex > directorySignatureIndex) {
+ this.parseDirectorySignatureBytes(descriptorString,
+ directorySignatureIndex, endIndex);
+ }
+ }
+
+ private int findFirstIndexOfKeyword(String descriptorString,
+ String keyword) {
+ if (descriptorString.startsWith(keyword)) {
+ return 0;
+ } else if (descriptorString.contains("\n" + keyword + " ")) {
+ return descriptorString.indexOf("\n" + keyword + " ") + 1;
+ } else if (descriptorString.contains("\n" + keyword + "\n")) {
+ return descriptorString.indexOf("\n" + keyword + "\n") + 1;
+ } else {
+ return -1;
+ }
+ }
+
+ private void parseHeaderBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ byte[] headerBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ headerBytes, 0, end - start);
+ this.parseHeader(headerBytes);
+ }
+
+ private void parseServerDescriptorBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitServerDescriptorBytes =
+ this.splitByKeyword(descriptorString, "router", start, end);
+ for (byte[] statusEntryBytes : splitServerDescriptorBytes) {
+ this.parseServerDescriptor(statusEntryBytes);
+ }
+ }
+
+ private void parseDirectorySignatureBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
+ descriptorString, "directory-signature", start, end);
+ for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
+ this.parseDirectorySignature(directorySignatureBytes);
+ }
+ }
+
+ private List<byte[]> splitByKeyword(String descriptorString,
+ String keyword, int start, int end) {
+ List<byte[]> splitParts = new ArrayList<>();
+ int from = start;
+ while (from < end) {
+ int to = descriptorString.indexOf("\n" + keyword + " ", from);
+ if (to < 0) {
+ to = descriptorString.indexOf("\n" + keyword + "\n", from);
+ }
+ if (to < 0) {
+ to = end;
+ } else {
+ to += 1;
+ }
+ int toNoNewline = to;
+ while (toNoNewline > from &&
+ descriptorString.charAt(toNoNewline - 1) == '\n') {
+ toNoNewline--;
+ }
+ byte[] part = new byte[toNoNewline - from];
+ System.arraycopy(this.rawDescriptorBytes, from, part, 0,
+ toNoNewline - from);
+ from = to;
+ splitParts.add(part);
+ }
+ return splitParts;
+ }
+
+ private void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String publishedLine = null, nextCrypto = "",
+ runningRoutersLine = null, routerStatusLine = null;
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.isEmpty() || line.startsWith("@")) {
+ continue;
+ }
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "signed-directory":
+ this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ if (publishedLine != null) {
+ throw new DescriptorParseException("Keyword 'published' is "
+ + "contained more than once, but must be contained exactly "
+ + "once.");
+ } else {
+ publishedLine = line;
+ }
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "recommended-software":
+ this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "running-routers":
+ runningRoutersLine = line;
+ break;
+ case "router-status":
+ routerStatusLine = line;
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key") &&
+ this.dirSigningKey == null) {
+ this.dirSigningKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v1 directory.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v1 directory.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ if (publishedLine == null) {
+ throw new DescriptorParseException("Keyword 'published' is "
+ + "contained 0 times, but must be contained exactly once.");
+ } else {
+ String publishedLineNoOpt = publishedLine.startsWith("opt ") ?
+ publishedLine.substring("opt ".length()) : publishedLine;
+ String[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+");
+ this.parsePublishedLine(publishedLine, publishedLineNoOpt,
+ publishedPartsNoOpt);
+ }
+ if (routerStatusLine != null) {
+ String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") ?
+ routerStatusLine.substring("opt ".length()) : routerStatusLine;
+ String[] routerStatusPartsNoOpt =
+ routerStatusLineNoOpt.split("[ \t]+");
+ this.parseRouterStatusLine(routerStatusLine, routerStatusLineNoOpt,
+ routerStatusPartsNoOpt);
+ } else if (runningRoutersLine != null) {
+ String runningRoutersLineNoOpt =
+ runningRoutersLine.startsWith("opt ") ?
+ runningRoutersLine.substring("opt ".length()) :
+ runningRoutersLine;
+ String[] runningRoutersPartsNoOpt =
+ runningRoutersLineNoOpt.split("[ \t]+");
+ this.parseRunningRoutersLine(runningRoutersLine,
+ runningRoutersLineNoOpt, runningRoutersPartsNoOpt);
+ } else {
+ throw new DescriptorParseException("Either running-routers or "
+ + "router-status line must be given.");
+ }
+ }
+
+ protected void parseServerDescriptor(byte[] serverDescriptorBytes) {
+ try {
+ ServerDescriptorImpl serverDescriptor =
+ new RelayServerDescriptorImpl(serverDescriptorBytes,
+ this.failUnrecognizedDescriptorLines);
+ this.serverDescriptors.add(serverDescriptor);
+ } catch (DescriptorParseException e) {
+ this.serverDescriptorParseExceptions.add(e);
+ }
+ }
+
+ private void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(directorySignatureBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseSignedDirectoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("signed-directory")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirSigningKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length > 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else if (partsNoOpt.length == 2) {
+ /* Early directories didn't have a crypto object following the
+ * "dir-signing-key" line, but had the key base64-encoded in the
+ * same line. */
+ StringBuilder sb = new StringBuilder();
+ sb.append("-----BEGIN RSA PUBLIC KEY-----\n");
+ String keyString = partsNoOpt[1];
+ while (keyString.length() > 64) {
+ sb.append(keyString.substring(0, 64)).append("\n");
+ keyString = keyString.substring(64);
+ }
+ if (keyString.length() > 0) {
+ sb.append(keyString).append("\n");
+ }
+ sb.append("-----END RSA PUBLIC KEY-----\n");
+ this.dirSigningKey = sb.toString();
+ }
+ }
+
+ private void parseRecommendedSoftwareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ List<String> result = new ArrayList<>();
+ if (partsNoOpt.length > 2) {
+ throw new DescriptorParseException("Illegal versions line '" + line
+ + "'.");
+ } else if (partsNoOpt.length == 2) {
+ String[] versions = partsNoOpt[1].split(",", -1);
+ for (int i = 0; i < versions.length; i++) {
+ String version = versions[i];
+ if (version.length() < 1) {
+ throw new DescriptorParseException("Illegal versions line '"
+ + line + "'.");
+ }
+ result.add(version);
+ }
+ }
+ this.recommendedSoftware = result;
+ }
+
+ private void parseRunningRoutersLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ String part = partsNoOpt[i];
+ String debugLine = "running-routers [...] " + part + " [...]";
+ boolean isLive = true;
+ if (part.startsWith("!")) {
+ isLive = false;
+ part = part.substring(1);
+ }
+ boolean isVerified;
+ String fingerprint = null, nickname = null;
+ if (part.startsWith("$")) {
+ isVerified = false;
+ fingerprint = ParseHelper.parseTwentyByteHexString(debugLine,
+ part.substring(1));
+ } else {
+ isVerified = true;
+ nickname = ParseHelper.parseNickname(debugLine, part);
+ }
+ this.statusEntries.add(new RouterStatusEntryImpl(fingerprint,
+ nickname, isLive, isVerified));
+ }
+ }
+
+ private void parseRouterStatusLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ String part = partsNoOpt[i];
+ String debugLine = "router-status [...] " + part + " [...]";
+ RouterStatusEntry entry = null;
+ if (part.contains("=")) {
+ String[] partParts = part.split("=");
+ if (partParts.length == 2) {
+ boolean isVerified = true, isLive;
+ String nickname;
+ if (partParts[0].startsWith("!")) {
+ isLive = false;
+ nickname = ParseHelper.parseNickname(debugLine,
+ partParts[0].substring(1));
+ } else {
+ isLive = true;
+ nickname = ParseHelper.parseNickname(debugLine, partParts[0]);
+ }
+ String fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, partParts[1].substring(1));
+ entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
+ isVerified);
+ }
+ } else {
+ boolean isVerified = false, isLive;
+ String nickname = null, fingerprint;
+ if (part.startsWith("!")) {
+ isLive = false;
+ fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, part.substring(2));
+ } else {
+ isLive = true;
+ fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, part.substring(1));;
+ }
+ entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
+ isVerified);
+ }
+ if (entry == null) {
+ throw new DescriptorParseException("Illegal router-status entry '"
+ + part + "' in v1 directory.");
+ }
+ this.statusEntries.add(entry);
+ }
+ }
+
+ private void parseDirectorySignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private List<String> recommendedSoftware;
+ @Override
+ public List<String> getRecommendedSoftware() {
+ return this.recommendedSoftware == null ? null :
+ new ArrayList<>(this.recommendedSoftware);
+ }
+
+ private String directorySignature;
+ @Override
+ public String getDirectorySignature() {
+ return this.directorySignature;
+ }
+
+ private List<RouterStatusEntry> statusEntries = new ArrayList<>();
+ @Override
+ public List<RouterStatusEntry> getRouterStatusEntries() {
+ return new ArrayList<>(this.statusEntries);
+ }
+
+ private List<ServerDescriptor> serverDescriptors = new ArrayList<>();
+ @Override
+ public List<ServerDescriptor> getServerDescriptors() {
+ return new ArrayList<>(this.serverDescriptors);
+ }
+
+ private List<Exception> serverDescriptorParseExceptions =
+ new ArrayList<>();
+ @Override
+ public List<Exception> getServerDescriptorParseExceptions() {
+ return new ArrayList<>(this.serverDescriptorParseExceptions);
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String directoryDigest;
+ @Override
+ public String getDirectoryDigest() {
+ return this.directoryDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..73d4dfa
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.RelayExtraInfoDescriptor;
+
+public class RelayExtraInfoDescriptorImpl
+ extends ExtraInfoDescriptorImpl implements RelayExtraInfoDescriptor {
+
+ protected static List<ExtraInfoDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "extra-info ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ExtraInfoDescriptor parsedDescriptor =
+ new RelayExtraInfoDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected RelayExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
new file mode 100644
index 0000000..fe045c1
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -0,0 +1,414 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* Contains a network status consensus or microdesc consensus. */
+public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
+ implements RelayNetworkStatusConsensus {
+
+ protected static List<RelayNetworkStatusConsensus> parseConsensuses(
+ byte[] consensusesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatusConsensus> parsedConsensuses =
+ new ArrayList<>();
+ List<byte[]> splitConsensusBytes =
+ DescriptorImpl.splitRawDescriptorBytes(consensusesBytes,
+ "network-status-version 3");
+ for (byte[] consensusBytes : splitConsensusBytes) {
+ RelayNetworkStatusConsensus parsedConsensus =
+ new RelayNetworkStatusConsensusImpl(consensusBytes,
+ failUnrecognizedDescriptorLines);
+ parsedConsensuses.add(parsedConsensus);
+ }
+ return parsedConsensuses;
+ }
+
+ protected RelayNetworkStatusConsensusImpl(byte[] consensusBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(consensusBytes, failUnrecognizedDescriptorLines, true, false);
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "vote-status,consensus-method,valid-after,fresh-until,"
+ + "valid-until,voting-delay,known-flags").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "client-versions,server-versions,params,directory-footer,"
+ + "bandwidth-weights").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("network-status-version");
+ this.clearParsedKeywords();
+ this.calculateDigest();
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "network-status-version ";
+ String sigToken = "\ndirectory-signature ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.consensusDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.consensusDigest == null) {
+ throw new DescriptorParseException("Could not calculate consensus "
+ + "digest.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-method":
+ this.parseConsensusMethodLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private boolean microdescConsensus = false;
+ protected void parseStatusEntry(byte[] statusEntryBytes)
+ throws DescriptorParseException {
+ NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
+ statusEntryBytes, this.microdescConsensus,
+ this.failUnrecognizedDescriptorLines);
+ this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
+ List<String> unrecognizedStatusEntryLines = statusEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-footer":
+ break;
+ case "bandwidth-weights":
+ this.parseBandwidthWeightsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.startsWith("network-status-version 3")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 3;
+ if (parts.length == 3) {
+ this.consensusFlavor = parts[2];
+ if (this.consensusFlavor.equals("microdesc")) {
+ this.microdescConsensus = true;
+ }
+ } else if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "line '" + line + "'.");
+ }
+ }
+
+ private void parseVoteStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || !parts[1].equals("consensus")) {
+ throw new DescriptorParseException("Line '" + line + "' indicates "
+ + "that this is not a consensus.");
+ }
+ }
+
+ private void parseConsensusMethodLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in consensus.");
+ }
+ try {
+ this.consensusMethod = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ if (this.consensusMethod < 1) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ }
+
+ private void parseValidAfterLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFreshUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseVotingDelayLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ try {
+ this.voteSeconds = Long.parseLong(parts[1]);
+ this.distSeconds = Long.parseLong(parts[2]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePackageLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 5) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ if (this.packageLines == null) {
+ this.packageLines = new ArrayList<>();
+ }
+ this.packageLines.add(line.substring("package ".length()));
+ }
+
+ private void parseKnownFlagsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No known flags in line '" + line
+ + "'.");
+ }
+ String[] knownFlags = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ knownFlags[i - 1] = parts[i];
+ }
+ this.knownFlags = knownFlags;
+ }
+
+ private void parseParamsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private void parseBandwidthWeightsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private String consensusDigest;
+ @Override
+ public String getConsensusDigest() {
+ return this.consensusDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private String consensusFlavor;
+ @Override
+ public String getConsensusFlavor() {
+ return this.consensusFlavor;
+ }
+
+ private int consensusMethod;
+ @Override
+ public int getConsensusMethod() {
+ return this.consensusMethod;
+ }
+
+ private long validAfterMillis;
+ @Override
+ public long getValidAfterMillis() {
+ return this.validAfterMillis;
+ }
+
+ private long freshUntilMillis;
+ @Override
+ public long getFreshUntilMillis() {
+ return this.freshUntilMillis;
+ }
+
+ private long validUntilMillis;
+ @Override
+ public long getValidUntilMillis() {
+ return this.validUntilMillis;
+ }
+
+ private long voteSeconds;
+ @Override
+ public long getVoteSeconds() {
+ return this.voteSeconds;
+ }
+
+ private long distSeconds;
+ @Override
+ public long getDistSeconds() {
+ return this.distSeconds;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private List<String> packageLines;
+ @Override
+ public List<String> getPackageLines() {
+ return this.packageLines == null ? null
+ : new ArrayList<>(this.packageLines);
+ }
+
+ private String[] knownFlags;
+ @Override
+ public SortedSet<String> getKnownFlags() {
+ return new TreeSet<>(Arrays.asList(this.knownFlags));
+ }
+
+ private SortedMap<String, Integer> consensusParams;
+ @Override
+ public SortedMap<String, Integer> getConsensusParams() {
+ return this.consensusParams == null ? null:
+ new TreeMap<>(this.consensusParams);
+ }
+
+ private SortedMap<String, Integer> bandwidthWeights;
+ @Override
+ public SortedMap<String, Integer> getBandwidthWeights() {
+ return this.bandwidthWeights == null ? null :
+ new TreeMap<>(this.bandwidthWeights);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
new file mode 100644
index 0000000..a5469db
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
@@ -0,0 +1,384 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatus;
+
+/* TODO Write unit tests. */
+
+public class RelayNetworkStatusImpl extends NetworkStatusImpl
+ implements RelayNetworkStatus {
+
+ protected static List<RelayNetworkStatus> parseStatuses(
+ byte[] statusesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatus> parsedStatuses = new ArrayList<>();
+ List<byte[]> splitStatusBytes =
+ DescriptorImpl.splitRawDescriptorBytes(statusesBytes,
+ "network-status-version 2");
+ for (byte[] statusBytes : splitStatusBytes) {
+ RelayNetworkStatus parsedStatus = new RelayNetworkStatusImpl(
+ statusBytes, failUnrecognizedDescriptorLines);
+ parsedStatuses.add(parsedStatus);
+ }
+ return parsedStatuses;
+ }
+
+ protected RelayNetworkStatusImpl(byte[] statusBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(statusBytes, failUnrecognizedDescriptorLines, false, true);
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "network-status-version,dir-source,fingerprint,contact,"
+ + "dir-signing-key,published").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList(
+ "dir-options,client-versions,server-versions".split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("network-status-version");
+ this.clearParsedKeywords();
+ this.calculateDigest();
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "network-status-version ";
+ String sigToken = "\ndirectory-signature ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ sig = ascii.indexOf("\n", sig) + 1;
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.statusDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.statusDigest == null) {
+ throw new DescriptorParseException("Could not calculate status "
+ + "digest.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.isEmpty()) {
+ continue;
+ }
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "dir-options":
+ this.parseDirOptionsLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key")) {
+ this.dirSigningKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory footer expected in "
+ + "v2 network status.");
+ }
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(directorySignatureBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, parts);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("network-status-version 2")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 2;
+ }
+
+ private void parseDirSourceLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 4) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in v2 network status.");
+ }
+ if (parts[1].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.address = ParseHelper.parseIpv4Address(line, parts[2]);
+ this.dirPort = ParseHelper.parsePort(line, parts[3]);
+ }
+
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in v2 network status.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parseContactLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseDirOptionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ String[] dirOptions = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ dirOptions[i - 1] = parts[i];
+ }
+ this.dirOptions = dirOptions;
+ }
+
+ private void parseDirectorySignatureLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ }
+
+ private String statusDigest;
+ @Override
+ public String getStatusDigest() {
+ return this.statusDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirport() {
+ return this.dirPort;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String[] dirOptions;
+ @Override
+ public SortedSet<String> getDirOptions() {
+ return new TreeSet<>(Arrays.asList(this.dirOptions));
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String directorySignature;
+ @Override
+ public String getDirectorySignature() {
+ return this.directorySignature;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
new file mode 100644
index 0000000..384ad1f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -0,0 +1,761 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.RelayNetworkStatusVote;
+
+/* Contains a network status vote. */
+public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
+ implements RelayNetworkStatusVote {
+
+ protected static List<RelayNetworkStatusVote> parseVotes(
+ byte[] votesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatusVote> parsedVotes = new ArrayList<>();
+ List<byte[]> splitVotesBytes =
+ DescriptorImpl.splitRawDescriptorBytes(votesBytes,
+ "network-status-version 3");
+ for (byte[] voteBytes : splitVotesBytes) {
+ RelayNetworkStatusVote parsedVote =
+ new RelayNetworkStatusVoteImpl(voteBytes,
+ failUnrecognizedDescriptorLines);
+ parsedVotes.add(parsedVote);
+ }
+ return parsedVotes;
+ }
+
+ protected RelayNetworkStatusVoteImpl(byte[] voteBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(voteBytes, failUnrecognizedDescriptorLines, false, false);
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
+ "vote-status,published,valid-after,fresh-until,"
+ + "valid-until,voting-delay,known-flags,dir-source,"
+ + "dir-key-certificate-version,fingerprint,dir-key-published,"
+ + "dir-key-expires,dir-identity-key,dir-signing-key,"
+ + "dir-key-certification").split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "consensus-methods,client-versions,server-versions,"
+ + "flag-thresholds,params,contact,"
+ + "legacy-key,dir-key-crosscert,dir-address,directory-footer").
+ split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ Set<String> atLeastOnceKeywords = new HashSet<>(Arrays.asList(
+ "directory-signature"));
+ this.checkAtLeastOnceKeywords(atLeastOnceKeywords);
+ this.checkFirstKeyword("network-status-version");
+ this.clearParsedKeywords();
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ /* Initialize flag-thresholds values here for the case that the vote
+ * doesn't contain those values. Initializing them in the constructor
+ * or when declaring variables wouldn't work, because those parts are
+ * evaluated later and would overwrite everything we parse here. */
+ this.stableUptime = -1L;
+ this.stableMtbf = -1L;
+ this.fastBandwidth = -1L;
+ this.guardWfu = -1.0;
+ this.guardTk = -1L;
+ this.guardBandwidthIncludingExits = -1L;
+ this.guardBandwidthExcludingExits = -1L;
+ this.enoughMtbfInfo = -1;
+ this.ignoringAdvertisedBws = -1;
+
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-methods":
+ this.parseConsensusMethodsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "legacy-dir-key":
+ this.parseLegacyDirKeyLine(line, parts);
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublished(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
+ break;
+ case "dir-identity-key":
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "dir-key-crosscert":
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
+ break;
+ case "dir-key-certification":
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in vote.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in vote.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("network-status-version 3")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 3;
+ }
+
+ private void parseVoteStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || !parts[1].equals("vote")) {
+ throw new DescriptorParseException("Line '" + line + "' indicates "
+ + "that this is not a vote.");
+ }
+ }
+
+ private void parseConsensusMethodsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ Integer[] consensusMethods = new Integer[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ int consensusMethod = -1;
+ try {
+ consensusMethod = Integer.parseInt(parts[i]);
+ } catch (NumberFormatException e) {
+ /* We'll notice below that consensusMethod is still -1. */
+ }
+ if (consensusMethod < 1) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ consensusMethods[i - 1] = consensusMethod;
+ }
+ this.consensusMethods = consensusMethods;
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidAfterLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFreshUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseVotingDelayLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ try {
+ this.voteSeconds = Long.parseLong(parts[1]);
+ this.distSeconds = Long.parseLong(parts[2]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePackageLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 5) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ if (this.packageLines == null) {
+ this.packageLines = new ArrayList<>();
+ }
+ this.packageLines.add(line.substring("package ".length()));
+ }
+
+ private void parseKnownFlagsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No known flags in line '" + line
+ + "'.");
+ }
+ String[] knownFlags = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ knownFlags[i - 1] = parts[i];
+ }
+ this.knownFlags = knownFlags;
+ }
+
+ private void parseFlagThresholdsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No flag thresholds in line '"
+ + line + "'.");
+ }
+ SortedMap<String, String> flagThresholds =
+ ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+ try {
+ for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+ switch (e.getKey()) {
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue().
+ replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
+ default:
+ // empty
+ }
+ }
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+
+ private void parseParamsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private void parseDirSourceLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 7) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
+ if (parts[3].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.hostname = parts[3];
+ this.address = ParseHelper.parseIpv4Address(line, parts[4]);
+ this.dirPort = ParseHelper.parsePort(line, parts[5]);
+ this.orPort = ParseHelper.parsePort(line, parts[6]);
+ }
+
+ private void parseContactLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseDirKeyCertificateVersionLine(String line,
+ String[] parts) throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ try {
+ this.dirKeyCertificateVersion = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal dir key certificate "
+ + "version in line '" + line + "'.");
+ }
+ if (this.dirKeyCertificateVersion < 1) {
+ throw new DescriptorParseException("Illegal dir key certificate "
+ + "version in line '" + line + "'.");
+ }
+ }
+
+ private void parseDirAddressLine(String line, String[] parts) {
+ /* Nothing new to learn here. Also, this line hasn't been observed
+ * "in the wild" yet. Maybe it's just an urban legend. */
+ }
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ /* Nothing new to learn here. We already know the fingerprint from
+ * the dir-source line. But we should at least check that there's a
+ * valid fingerprint in this line. */
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ ParseHelper.parseTwentyByteHexString(line, parts[1]);
+ }
+
+ private void parseLegacyDirKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line, parts[1]);
+ }
+
+ private void parseDirKeyPublished(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirKeyExpiresLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirIdentityKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-identity-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCrosscertLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-crosscert")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCertificationLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-certification")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (!line.equals("directory-footer")) {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in vote.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirport() {
+ return this.dirPort;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrport() {
+ return this.orPort;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private int dirKeyCertificateVersion;
+ @Override
+ public int getDirKeyCertificateVersion() {
+ return this.dirKeyCertificateVersion;
+ }
+
+ private String legacyDirKey;
+ @Override
+ public String getLegacyDirKey() {
+ return this.legacyDirKey;
+ }
+
+ private long dirKeyPublishedMillis;
+ @Override
+ public long getDirKeyPublishedMillis() {
+ return this.dirKeyPublishedMillis;
+ }
+
+ private long dirKeyExpiresMillis;
+ @Override
+ public long getDirKeyExpiresMillis() {
+ return this.dirKeyExpiresMillis;
+ }
+
+ private String dirIdentityKey;
+ @Override
+ public String getDirIdentityKey() {
+ return this.dirIdentityKey;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String dirKeyCrosscert;
+ @Override
+ public String getDirKeyCrosscert() {
+ return this.dirKeyCrosscert;
+ }
+
+ private String dirKeyCertification;
+ @Override
+ public String getDirKeyCertification() {
+ return this.dirKeyCertification;
+ }
+
+ @Override
+ public String getSigningKeyDigest() {
+ String signingKeyDigest = null;
+ if (this.signatures != null && !this.signatures.isEmpty()) {
+ for (DirectorySignature signature : this.signatures) {
+ if (DirectorySignatureImpl.DEFAULT_ALGORITHM.equals(
+ signature.getAlgorithm())) {
+ signingKeyDigest = signature.getSigningKeyDigest();
+ break;
+ }
+ }
+ }
+ return signingKeyDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private Integer[] consensusMethods;
+ @Override
+ public List<Integer> getConsensusMethods() {
+ return this.consensusMethods == null ? null :
+ Arrays.asList(this.consensusMethods);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long validAfterMillis;
+ @Override
+ public long getValidAfterMillis() {
+ return this.validAfterMillis;
+ }
+
+ private long freshUntilMillis;
+ @Override
+ public long getFreshUntilMillis() {
+ return this.freshUntilMillis;
+ }
+
+ private long validUntilMillis;
+ @Override
+ public long getValidUntilMillis() {
+ return this.validUntilMillis;
+ }
+
+ private long voteSeconds;
+ @Override
+ public long getVoteSeconds() {
+ return this.voteSeconds;
+ }
+
+ private long distSeconds;
+ @Override
+ public long getDistSeconds() {
+ return this.distSeconds;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private List<String> packageLines;
+ @Override
+ public List<String> getPackageLines() {
+ return this.packageLines == null ? null
+ : new ArrayList<>(this.packageLines);
+ }
+
+ private String[] knownFlags;
+ @Override
+ public SortedSet<String> getKnownFlags() {
+ return new TreeSet<>(Arrays.asList(this.knownFlags));
+ }
+
+ private long stableUptime;
+ @Override
+ public long getStableUptime() {
+ return this.stableUptime;
+ }
+
+ private long stableMtbf;
+ @Override
+ public long getStableMtbf() {
+ return this.stableMtbf;
+ }
+
+ private long fastBandwidth;
+ @Override
+ public long getFastBandwidth() {
+ return this.fastBandwidth;
+ }
+
+ private double guardWfu;
+ @Override
+ public double getGuardWfu() {
+ return this.guardWfu;
+ }
+
+ private long guardTk;
+ @Override
+ public long getGuardTk() {
+ return this.guardTk;
+ }
+
+ private long guardBandwidthIncludingExits;
+ @Override
+ public long getGuardBandwidthIncludingExits() {
+ return this.guardBandwidthIncludingExits;
+ }
+
+ private long guardBandwidthExcludingExits;
+ @Override
+ public long getGuardBandwidthExcludingExits() {
+ return this.guardBandwidthExcludingExits;
+ }
+
+ private int enoughMtbfInfo;
+ @Override
+ public int getEnoughMtbfInfo() {
+ return this.enoughMtbfInfo;
+ }
+
+ private int ignoringAdvertisedBws;
+ @Override
+ public int getIgnoringAdvertisedBws() {
+ return this.ignoringAdvertisedBws;
+ }
+
+ private SortedMap<String, Integer> consensusParams;
+ @Override
+ public SortedMap<String, Integer> getConsensusParams() {
+ return this.consensusParams == null ? null:
+ new TreeMap<>(this.consensusParams);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
new file mode 100644
index 0000000..4957072
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayServerDescriptor;
+import org.torproject.descriptor.ServerDescriptor;
+
+public class RelayServerDescriptorImpl extends ServerDescriptorImpl
+ implements RelayServerDescriptor {
+
+ protected static List<ServerDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "router ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ServerDescriptor parsedDescriptor =
+ new RelayServerDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected RelayServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
new file mode 100644
index 0000000..a359c50
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
@@ -0,0 +1,41 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.RouterStatusEntry;
+
+public class RouterStatusEntryImpl implements RouterStatusEntry {
+
+ protected RouterStatusEntryImpl(String fingerprint, String nickname,
+ boolean isLive, boolean isVerified) {
+ this.fingerprint = fingerprint;
+ this.nickname = nickname;
+ this.isLive = isLive;
+ this.isVerified = isVerified;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private boolean isLive;
+ @Override
+ public boolean isLive() {
+ return this.isLive;
+ }
+
+ private boolean isVerified;
+ @Override
+ public boolean isVerified() {
+ return this.isVerified;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
new file mode 100644
index 0000000..1805dca
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -0,0 +1,985 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* Contains a server descriptor. */
+public abstract class ServerDescriptorImpl extends DescriptorImpl
+ implements ServerDescriptor {
+
+ protected ServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ this.calculateDigest();
+ this.calculateDigestSha256();
+ Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
+ "router,bandwidth,published".split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
+ "identity-ed25519,master-key-ed25519,platform,fingerprint,"
+ + "hibernating,uptime,contact,family,read-history,write-history,"
+ + "eventdns,caches-extra-info,extra-info-digest,"
+ + "hidden-service-dir,protocols,allow-single-hop-exits,onion-key,"
+ + "signing-key,ipv6-policy,ntor-onion-key,onion-key-crosscert,"
+ + "ntor-onion-key-crosscert,tunnelled-dir-server,"
+ + "router-sig-ed25519,router-signature,router-digest-sha256,"
+ + "router-digest").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("router");
+ if (this.getKeywordCount("accept") == 0 &&
+ this.getKeywordCount("reject") == 0) {
+ throw new DescriptorParseException("Either keyword 'accept' or "
+ + "'reject' must be contained at least once.");
+ }
+ this.clearParsedKeywords();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ List<String> cryptoLines = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) {
+ continue;
+ }
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "router":
+ this.parseRouterLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "or-address":
+ this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bandwidth":
+ this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "platform":
+ this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hibernating":
+ this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "uptime":
+ this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "onion-key":
+ this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key";
+ break;
+ case "signing-key":
+ this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "signing-key";
+ break;
+ case "accept":
+ this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "reject":
+ this.parseRejectLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-signature":
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "contact":
+ this.parseContactLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "family":
+ this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "eventdns":
+ this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "caches-extra-info":
+ this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "extra-info-digest":
+ this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidden-service-dir":
+ this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "protocols":
+ this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "allow-single-hop-exits":
+ this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dircacheport":
+ this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ipv6-policy":
+ this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "identity-ed25519":
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
+ break;
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "onion-key-crosscert":
+ this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key-crosscert";
+ break;
+ case "ntor-onion-key-crosscert":
+ this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "ntor-onion-key-crosscert";
+ break;
+ case "tunnelled-dir-server":
+ this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
+ cryptoLines.add(line);
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "onion-key":
+ this.onionKey = cryptoString;
+ break;
+ case "signing-key":
+ this.signingKey = cryptoString;
+ break;
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ case "onion-key-crosscert":
+ this.onionKeyCrosscert = cryptoString;
+ break;
+ case "ntor-onion-key-crosscert":
+ this.ntorOnionKeyCrosscert = cryptoString;
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
+ } else {
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseRouterLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 6) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in server descriptor.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ this.address = ParseHelper.parseIpv4Address(line, partsNoOpt[2]);
+ this.orPort = ParseHelper.parsePort(line, partsNoOpt[3]);
+ this.socksPort = ParseHelper.parsePort(line, partsNoOpt[4]);
+ this.dirPort = ParseHelper.parsePort(line, partsNoOpt[5]);
+ }
+
+ private void parseOrAddressLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(partsNoOpt[1]);
+ }
+
+ private void parseBandwidthLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 3 || partsNoOpt.length > 4) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ boolean isValid = false;
+ try {
+ this.bandwidthRate = Integer.parseInt(partsNoOpt[1]);
+ this.bandwidthBurst = Integer.parseInt(partsNoOpt[2]);
+ if (partsNoOpt.length == 4) {
+ this.bandwidthObserved = Integer.parseInt(partsNoOpt[3]);
+ }
+ if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0 &&
+ this.bandwidthObserved >= 0) {
+ isValid = true;
+ }
+ if (partsNoOpt.length < 4) {
+ /* Tor versions 0.0.8 and older only wrote bandwidth lines with
+ * rate and burst values, but no observed value. */
+ this.bandwidthObserved = -1;
+ }
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parsePlatformLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (lineNoOpt.length() > "platform ".length()) {
+ this.platform = lineNoOpt.substring("platform ".length());
+ } else {
+ this.platform = "";
+ }
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseFingerprintLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (lineNoOpt.length() != "fingerprint".length() + 5 * 10) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ lineNoOpt.substring("fingerprint ".length()).replaceAll(" ", ""));
+ }
+
+ private void parseHibernatingLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hibernating = ParseHelper.parseBoolean(partsNoOpt[1], line);
+ }
+
+ private void parseUptimeLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ boolean isValid = false;
+ try {
+ this.uptime = Long.parseLong(partsNoOpt[1]);
+ isValid = true;
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal value in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseOnionKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("onion-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseSigningKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseAcceptLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
+ }
+
+ private void parseRejectLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
+ }
+
+ private void parseExitPolicyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseExitPattern(line, partsNoOpt[1]);
+ this.exitPolicyLines.add(lineNoOpt);
+ }
+
+ private void parseRouterSignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("router-signature")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseContactLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (lineNoOpt.length() > "contact ".length()) {
+ this.contact = lineNoOpt.substring("contact ".length());
+ } else {
+ this.contact = "";
+ }
+ }
+
+ private void parseFamilyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ String[] familyEntries = new String[partsNoOpt.length - 1];
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ if (partsNoOpt[i].startsWith("$")) {
+ if (partsNoOpt[i].contains("=") ^ partsNoOpt[i].contains("~")) {
+ String separator = partsNoOpt[i].contains("=") ? "=" : "~";
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[i].substring(1, partsNoOpt[i].indexOf(
+ separator)));
+ String nickname = ParseHelper.parseNickname(line,
+ partsNoOpt[i].substring(partsNoOpt[i].indexOf(
+ separator) + 1));
+ familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
+ } else {
+ familyEntries[i - 1] = "$"
+ + ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[i].substring(1));
+ }
+ } else {
+ familyEntries[i - 1] = ParseHelper.parseNickname(line,
+ partsNoOpt[i]);
+ }
+ }
+ this.familyEntries = familyEntries;
+ }
+
+ private void parseReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseEventdnsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1], line);
+ }
+
+ private void parseCachesExtraInfoLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("caches-extra-info")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.cachesExtraInfo = true;
+ }
+
+ private void parseExtraInfoDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ if (partsNoOpt.length >= 3) {
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[2]);
+ this.extraInfoDigestSha256 = partsNoOpt[2];
+ }
+ }
+
+ private void parseHiddenServiceDirLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length == 1) {
+ this.hiddenServiceDirVersions = new Integer[] { 2 };
+ } else {
+ try {
+ Integer[] result = new Integer[partsNoOpt.length - 1];
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ result[i - 1] = Integer.parseInt(partsNoOpt[i]);
+ }
+ this.hiddenServiceDirVersions = result;
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+ }
+
+ private void parseProtocolsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ int linkIndex = -1, circuitIndex = -1;
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ switch (partsNoOpt[i]) {
+ case "Link":
+ linkIndex = i;
+ break;
+ case "Circuit":
+ circuitIndex = i;
+ break;
+ default:
+ // empty
+ }
+ }
+ if (linkIndex < 0 || circuitIndex < 0 || circuitIndex < linkIndex) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ Integer[] linkProtocolVersions =
+ new Integer[circuitIndex - linkIndex - 1];
+ for (int i = linkIndex + 1, j = 0; i < circuitIndex; i++, j++) {
+ linkProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
+ }
+ Integer[] circuitProtocolVersions =
+ new Integer[partsNoOpt.length - circuitIndex - 1];
+ for (int i = circuitIndex + 1, j = 0; i < partsNoOpt.length;
+ i++, j++) {
+ circuitProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
+ }
+ this.linkProtocolVersions = linkProtocolVersions;
+ this.circuitProtocolVersions = circuitProtocolVersions;
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseAllowSingleHopExitsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("allow-single-hop-exits")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.allowSingleHopExits = true;
+ }
+
+ private void parseDircacheportLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ /* The dircacheport line was only contained in server descriptors
+ * published by Tor 0.0.8 and before. It's only specified in old
+ * tor-spec.txt versions. */
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ if (this.dirPort != 0) {
+ throw new DescriptorParseException("At most one of dircacheport "
+ + "and the directory port in the router line may be non-zero.");
+ }
+ this.dirPort = ParseHelper.parsePort(line, partsNoOpt[1]);
+ }
+
+ private void parseRouterDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.serverDescriptorDigest = ParseHelper.parseTwentyByteHexString(
+ line, partsNoOpt[1]);
+ }
+
+ private void parseIpv6PolicyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ boolean isValid = true;
+ if (partsNoOpt.length != 3) {
+ isValid = false;
+ } else {
+ switch (partsNoOpt[1]) {
+ case "accept":
+ case "reject":
+ this.ipv6DefaultPolicy = partsNoOpt[1];
+ this.ipv6PortList = partsNoOpt[2];
+ String[] ports = partsNoOpt[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
+ }
+
+ private void parseIdentityEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseOnionKeyCrosscert(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyCrosscert(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.ntorOnionKeyCrosscertSign = Integer.parseInt(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseTunnelledDirServerLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("tunnelled-dir-server")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.tunnelledDirServer = true;
+ }
+
+ private void parseIdentityEd25519CryptoBlock(String cryptoString)
+ throws DescriptorParseException {
+ String masterKeyEd25519FromIdentityEd25519 =
+ ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
+ cryptoString);
+ if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
+ masterKeyEd25519FromIdentityEd25519)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
+ }
+
+ private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
+ if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
+ masterKeyEd25519FromMasterKeyEd25519Line)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
+ }
+
+ private void parseRouterSigEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.routerSignatureEd25519 = partsNoOpt[1];
+ }
+
+ private void parseRouterDigestSha256Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
+ this.serverDescriptorDigestSha256 = partsNoOpt[1];
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ if (this.serverDescriptorDigest != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "router ";
+ String sigToken = "\nrouter-signature\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.serverDescriptorDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.serverDescriptorDigest == null) {
+ throw new DescriptorParseException("Could not calculate server "
+ + "descriptor digest.");
+ }
+ }
+
+ private void calculateDigestSha256() throws DescriptorParseException {
+ if (this.serverDescriptorDigestSha256 != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest-sha256" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "router ";
+ String sigToken = "\n-----END SIGNATURE-----\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
+ 0, sig - start);
+ this.serverDescriptorDigestSha256 =
+ DatatypeConverter.printBase64Binary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ replaceAll("=", "");
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.serverDescriptorDigestSha256 == null) {
+ throw new DescriptorParseException("Could not calculate server "
+ + "descriptor SHA-256 digest.");
+ }
+ }
+
+ private String serverDescriptorDigest;
+ @Override
+ public String getServerDescriptorDigest() {
+ return this.serverDescriptorDigest;
+ }
+
+ private String serverDescriptorDigestSha256;
+ @Override
+ public String getServerDescriptorDigestSha256() {
+ return this.serverDescriptorDigestSha256;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private int socksPort;
+ @Override
+ public int getSocksPort() {
+ return this.socksPort;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private int bandwidthRate;
+ @Override
+ public int getBandwidthRate() {
+ return this.bandwidthRate;
+ }
+
+ private int bandwidthBurst;
+ @Override
+ public int getBandwidthBurst() {
+ return this.bandwidthBurst;
+ }
+
+ private int bandwidthObserved;
+ @Override
+ public int getBandwidthObserved() {
+ return this.bandwidthObserved;
+ }
+
+ private String platform;
+ @Override
+ public String getPlatform() {
+ return this.platform;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private boolean hibernating;
+ @Override
+ public boolean isHibernating() {
+ return this.hibernating;
+ }
+
+ private Long uptime;
+ @Override
+ public Long getUptime() {
+ return this.uptime;
+ }
+
+ private String onionKey;
+ @Override
+ public String getOnionKey() {
+ return this.onionKey;
+ }
+
+ private String signingKey;
+ @Override
+ public String getSigningKey() {
+ return this.signingKey;
+ }
+
+ private List<String> exitPolicyLines = new ArrayList<>();
+ @Override
+ public List<String> getExitPolicyLines() {
+ return new ArrayList<>(this.exitPolicyLines);
+ }
+
+ private String routerSignature;
+ @Override
+ public String getRouterSignature() {
+ return this.routerSignature;
+ }
+
+ private String contact;
+ @Override
+ public String getContact() {
+ return this.contact;
+ }
+
+ private String[] familyEntries;
+ @Override
+ public List<String> getFamilyEntries() {
+ return this.familyEntries == null ? null :
+ Arrays.asList(this.familyEntries);
+ }
+
+ private BandwidthHistory readHistory;
+ @Override
+ public BandwidthHistory getReadHistory() {
+ return this.readHistory;
+ }
+
+ private BandwidthHistory writeHistory;
+ @Override
+ public BandwidthHistory getWriteHistory() {
+ return this.writeHistory;
+ }
+
+ private boolean usesEnhancedDnsLogic;
+ @Override
+ public boolean getUsesEnhancedDnsLogic() {
+ return this.usesEnhancedDnsLogic;
+ }
+
+ private boolean cachesExtraInfo;
+ @Override
+ public boolean getCachesExtraInfo() {
+ return this.cachesExtraInfo;
+ }
+
+ private String extraInfoDigest;
+ @Override
+ public String getExtraInfoDigest() {
+ return this.extraInfoDigest;
+ }
+
+ private String extraInfoDigestSha256;
+ @Override
+ public String getExtraInfoDigestSha256() {
+ return this.extraInfoDigestSha256;
+ }
+
+ private Integer[] hiddenServiceDirVersions;
+ @Override
+ public List<Integer> getHiddenServiceDirVersions() {
+ return this.hiddenServiceDirVersions == null ? null :
+ Arrays.asList(this.hiddenServiceDirVersions);
+ }
+
+ private Integer[] linkProtocolVersions;
+ @Override
+ public List<Integer> getLinkProtocolVersions() {
+ return this.linkProtocolVersions == null ? null :
+ Arrays.asList(this.linkProtocolVersions);
+ }
+
+ private Integer[] circuitProtocolVersions;
+ @Override
+ public List<Integer> getCircuitProtocolVersions() {
+ return this.circuitProtocolVersions == null ? null :
+ Arrays.asList(this.circuitProtocolVersions);
+ }
+
+ private boolean allowSingleHopExits;
+ @Override
+ public boolean getAllowSingleHopExits() {
+ return this.allowSingleHopExits;
+ }
+
+ private String ipv6DefaultPolicy;
+ @Override
+ public String getIpv6DefaultPolicy() {
+ return this.ipv6DefaultPolicy;
+ }
+
+ private String ipv6PortList;
+ @Override
+ public String getIpv6PortList() {
+ return this.ipv6PortList;
+ }
+
+ private String ntorOnionKey;
+ @Override
+ public String getNtorOnionKey() {
+ return this.ntorOnionKey;
+ }
+
+ private String identityEd25519;
+ @Override
+ public String getIdentityEd25519() {
+ return this.identityEd25519;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+
+ private String routerSignatureEd25519;
+ @Override
+ public String getRouterSignatureEd25519() {
+ return this.routerSignatureEd25519;
+ }
+
+ private String onionKeyCrosscert;
+ @Override
+ public String getOnionKeyCrosscert() {
+ return this.onionKeyCrosscert;
+ }
+
+ private String ntorOnionKeyCrosscert;
+ @Override
+ public String getNtorOnionKeyCrosscert() {
+ return this.ntorOnionKeyCrosscert;
+ }
+
+ private int ntorOnionKeyCrosscertSign = -1;
+ @Override
+ public int getNtorOnionKeyCrosscertSign() {
+ return ntorOnionKeyCrosscertSign;
+ }
+
+ private boolean tunnelledDirServer;
+ @Override
+ public boolean getTunnelledDirServer() {
+ return this.tunnelledDirServer;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
new file mode 100644
index 0000000..0800de0
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
@@ -0,0 +1,546 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.TorperfResult;
+
+public class TorperfResultImpl extends DescriptorImpl
+ implements TorperfResult {
+
+ protected static List<Descriptor> parseTorperfResults(
+ byte[] rawDescriptorBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ if (rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ List<Descriptor> parsedDescriptors = new ArrayList<>();
+ String descriptorString = new String(rawDescriptorBytes);
+ Scanner s = new Scanner(descriptorString).useDelimiter("\r?\n");
+ String typeAnnotation = "";
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@type torperf ")) {
+ String[] parts = line.split(" ");
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ String version = parts[2];
+ if (!version.startsWith("1.")) {
+ throw new DescriptorParseException("Unsupported version in "
+ + " line '" + line + "'.");
+ }
+ typeAnnotation = line + "\n";
+ } else {
+ parsedDescriptors.add(new TorperfResultImpl(
+ (typeAnnotation + line).getBytes(),
+ failUnrecognizedDescriptorLines));
+ typeAnnotation = "";
+ }
+ }
+ return parsedDescriptors;
+ }
+
+ protected TorperfResultImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseTorperfResultLine(new String(rawDescriptorBytes));
+ }
+
+ private void parseTorperfResultLine(String inputLine)
+ throws DescriptorParseException {
+ String line = inputLine;
+ while (line.startsWith("@") && line.contains("\n")) {
+ line = line.split("\n")[1];
+ }
+ if (line.isEmpty()) {
+ throw new DescriptorParseException("Blank lines are not allowed.");
+ }
+ String[] parts = line.split(" ");
+ for (int i = 0; i < parts.length; i++) {
+ String keyValue = parts[i];
+ String[] keyValueParts = keyValue.split("=");
+ if (keyValueParts.length != 2) {
+ throw new DescriptorParseException("Illegal key-value pair in "
+ + "line '" + line + "'.");
+ }
+ String key = keyValueParts[0];
+ this.markKeyAsParsed(key, line);
+ String value = keyValueParts[1];
+ switch (key) {
+ case "SOURCE":
+ this.parseSource(value, keyValue, line);
+ break;
+ case "FILESIZE":
+ this.parseFileSize(value, keyValue, line);
+ break;
+ case "START":
+ this.parseStart(value, keyValue, line);
+ break;
+ case "SOCKET":
+ this.parseSocket(value, keyValue, line);
+ break;
+ case "CONNECT":
+ this.parseConnect(value, keyValue, line);
+ break;
+ case "NEGOTIATE":
+ this.parseNegotiate(value, keyValue, line);
+ break;
+ case "REQUEST":
+ this.parseRequest(value, keyValue, line);
+ break;
+ case "RESPONSE":
+ this.parseResponse(value, keyValue, line);
+ break;
+ case "DATAREQUEST":
+ this.parseDataRequest(value, keyValue, line);
+ break;
+ case "DATARESPONSE":
+ this.parseDataResponse(value, keyValue, line);
+ break;
+ case "DATACOMPLETE":
+ this.parseDataComplete(value, keyValue, line);
+ break;
+ case "WRITEBYTES":
+ this.parseWriteBytes(value, keyValue, line);
+ break;
+ case "READBYTES":
+ this.parseReadBytes(value, keyValue, line);
+ break;
+ case "DIDTIMEOUT":
+ this.parseDidTimeout(value, keyValue, line);
+ break;
+ case "LAUNCH":
+ this.parseLaunch(value, keyValue, line);
+ break;
+ case "USED_AT":
+ this.parseUsedAt(value, keyValue, line);
+ break;
+ case "PATH":
+ this.parsePath(value, keyValue, line);
+ break;
+ case "BUILDTIMES":
+ this.parseBuildTimes(value, keyValue, line);
+ break;
+ case "TIMEOUT":
+ this.parseTimeout(value, keyValue, line);
+ break;
+ case "QUANTILE":
+ this.parseQuantile(value, keyValue, line);
+ break;
+ case "CIRC_ID":
+ this.parseCircId(value, keyValue, line);
+ break;
+ case "USED_BY":
+ this.parseUsedBy(value, keyValue, line);
+ break;
+ default:
+ if (key.startsWith("DATAPERC")) {
+ this.parseDataPercentile(value, keyValue, line);
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized key '" + key
+ + "' in line '" + line + "'.");
+ } else {
+ if (this.unrecognizedKeys == null) {
+ this.unrecognizedKeys = new TreeMap<>();
+ }
+ this.unrecognizedKeys.put(key, value);
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ if (!this.unrecognizedLines.contains(line)) {
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ this.checkAllRequiredKeysParsed(line);
+ }
+
+ private Set<String> parsedKeys = new HashSet<>();
+ private Set<String> requiredKeys = new HashSet<>(Arrays.asList(
+ ("SOURCE,FILESIZE,START,SOCKET,CONNECT,NEGOTIATE,REQUEST,RESPONSE,"
+ + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES").
+ split(",")));
+ private void markKeyAsParsed(String key, String line)
+ throws DescriptorParseException {
+ if (this.parsedKeys.contains(key)) {
+ throw new DescriptorParseException("Key '" + key + "' is contained "
+ + "at least twice in line '" + line + "', but must be "
+ + "contained at most once.");
+ }
+ this.parsedKeys.add(key);
+ this.requiredKeys.remove(key);
+ }
+ private void checkAllRequiredKeysParsed(String line)
+ throws DescriptorParseException {
+ for (String key : this.requiredKeys) {
+ throw new DescriptorParseException("Key '" + key + "' is contained "
+ + "contained 0 times in line '" + line + "', but must be "
+ + "contained exactly once.");
+ }
+ }
+
+ private void parseSource(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.source = value;
+ }
+
+ private void parseFileSize(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ this.fileSize = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private void parseStart(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.startMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseSocket(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.socketMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseConnect(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.connectMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseNegotiate(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.negotiateMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseRequest(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.requestMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseResponse(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.responseMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataRequest(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataRequestMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataResponse(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataResponseMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataComplete(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataCompleteMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseWriteBytes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.writeBytes = parseInt(value, keyValue, line);
+ }
+
+ private void parseReadBytes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.readBytes = parseInt(value, keyValue, line);
+ }
+
+ private void parseDidTimeout(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ if (value.equals("1")) {
+ this.didTimeout = true;
+ } else if (value.equals("0")) {
+ this.didTimeout = false;
+ } else {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private void parseDataPercentile(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ String key = keyValue.substring(0, keyValue.indexOf("="));
+ String percentileString = key.substring("DATAPERC".length());
+ int percentile = -1;
+ try {
+ percentile = Integer.parseInt(percentileString);
+ } catch (NumberFormatException e) {
+ /* Treat key as unrecognized below. */
+ percentile = -1;
+ }
+ if (percentile < 0 || percentile > 100) {
+ if (this.unrecognizedKeys == null) {
+ this.unrecognizedKeys = new TreeMap<>();
+ }
+ this.unrecognizedKeys.put(key, value);
+ } else {
+ long timestamp = this.parseTimestamp(value, keyValue, line);
+ if (this.dataPercentiles == null) {
+ this.dataPercentiles = new TreeMap<>();
+ }
+ this.dataPercentiles.put(percentile, timestamp);
+ }
+ }
+
+ private void parseLaunch(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.launchMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseUsedAt(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.usedAtMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parsePath(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ String[] valueParts = value.split(",");
+ String[] result = new String[valueParts.length];
+ for (int i = 0; i < valueParts.length; i++) {
+ if (valueParts[i].length() != 41) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ result[i] = ParseHelper.parseTwentyByteHexString(line,
+ valueParts[i].substring(1));
+ }
+ this.path = result;
+ }
+
+ private void parseBuildTimes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ String[] valueParts = value.split(",");
+ Long[] result = new Long[valueParts.length];
+ for (int i = 0; i < valueParts.length; i++) {
+ result[i] = this.parseTimestamp(valueParts[i], keyValue, line);
+ }
+ this.buildTimes = result;
+ }
+
+ private void parseTimeout(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.timeout = this.parseInt(value, keyValue, line);
+ }
+
+ private void parseQuantile(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.quantile = this.parseDouble(value, keyValue, line);
+ }
+
+ private void parseCircId(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.circId = this.parseInt(value, keyValue, line);
+ }
+
+ private void parseUsedBy(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.usedBy = this.parseInt(value, keyValue, line);
+ }
+
+ private long parseTimestamp(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ long timestamp = -1L;
+ if (value.contains(".") && value.split("\\.").length == 2) {
+ String zeroPaddedValue = (value + "000");
+ String threeDecimalPlaces = zeroPaddedValue.substring(0,
+ zeroPaddedValue.indexOf(".") + 4);
+ String millisString = threeDecimalPlaces.replaceAll("\\.", "");
+ try {
+ timestamp = Long.parseLong(millisString);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (timestamp < 0L) {
+ throw new DescriptorParseException("Illegal timestamp '" + value
+ + "' in '" + keyValue + "' in line '" + line + "'.");
+ }
+ return timestamp;
+ }
+
+ private int parseInt(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private double parseDouble(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private SortedMap<String, String> unrecognizedKeys;
+ @Override
+ public SortedMap<String, String> getUnrecognizedKeys() {
+ return this.unrecognizedKeys == null ? null
+ : new TreeMap<>(this.unrecognizedKeys);
+ }
+
+ private String source;
+ @Override
+ public String getSource() {
+ return this.source;
+ }
+
+ private int fileSize;
+ @Override
+ public int getFileSize() {
+ return this.fileSize;
+ }
+
+ private long startMillis;
+ @Override
+ public long getStartMillis() {
+ return this.startMillis;
+ }
+
+ private long socketMillis;
+ @Override
+ public long getSocketMillis() {
+ return this.socketMillis;
+ }
+
+ private long connectMillis;
+ @Override
+ public long getConnectMillis() {
+ return this.connectMillis;
+ }
+
+ private long negotiateMillis;
+ @Override
+ public long getNegotiateMillis() {
+ return this.negotiateMillis;
+ }
+
+ private long requestMillis;
+ @Override
+ public long getRequestMillis() {
+ return this.requestMillis;
+ }
+
+ private long responseMillis;
+ @Override
+ public long getResponseMillis() {
+ return this.responseMillis;
+ }
+
+ private long dataRequestMillis;
+ @Override
+ public long getDataRequestMillis() {
+ return this.dataRequestMillis;
+ }
+
+ private long dataResponseMillis;
+ @Override
+ public long getDataResponseMillis() {
+ return this.dataResponseMillis;
+ }
+
+ private long dataCompleteMillis;
+ @Override
+ public long getDataCompleteMillis() {
+ return this.dataCompleteMillis;
+ }
+
+ private int writeBytes;
+ @Override
+ public int getWriteBytes() {
+ return this.writeBytes;
+ }
+
+ private int readBytes;
+ @Override
+ public int getReadBytes() {
+ return this.readBytes;
+ }
+
+ private boolean didTimeout;
+ @Override
+ public Boolean didTimeout() {
+ return this.didTimeout;
+ }
+
+ private SortedMap<Integer, Long> dataPercentiles;
+ @Override
+ public SortedMap<Integer, Long> getDataPercentiles() {
+ return this.dataPercentiles == null ? null
+ : new TreeMap<>(this.dataPercentiles);
+ }
+
+ private long launchMillis = -1L;
+ @Override
+ public long getLaunchMillis() {
+ return this.launchMillis;
+ }
+
+ private long usedAtMillis = -1L;
+ @Override
+ public long getUsedAtMillis() {
+ return this.usedAtMillis;
+ }
+
+ private String[] path;
+ @Override
+ public List<String> getPath() {
+ return this.path == null ? null : Arrays.asList(this.path);
+ }
+
+ private Long[] buildTimes;
+ @Override
+ public List<Long> getBuildTimes() {
+ return this.buildTimes == null ? null :
+ Arrays.asList(this.buildTimes);
+ }
+
+ private long timeout = -1L;
+ @Override
+ public long getTimeout() {
+ return this.timeout;
+ }
+
+ private double quantile = -1.0;
+ @Override
+ public double getQuantile() {
+ return this.quantile;
+ }
+
+ private int circId = -1;
+ @Override
+ public int getCircId() {
+ return this.circId;
+ }
+
+ private int usedBy = -1;
+ @Override
+ public int getUsedBy() {
+ return this.usedBy;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/package-info.java b/src/main/java/org/torproject/descriptor/package-info.java
new file mode 100644
index 0000000..5b34554
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/package-info.java
@@ -0,0 +1,80 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+
+/**
+ * Interfaces and essential classes for obtaining and processing Tor
+ * descriptors.
+ *
+ * <p>This package contains all relevant interfaces and
+ * classes that an application would need to use this library.
+ * Applications are strongly discouraged from accessing types from the
+ * implementation package ({@code org.torproject.descriptor.impl})
+ * directly, because those may change without prior notice.</p>
+ *
+ * <p>Interfaces and classes in this package can be grouped into
+ * general-purpose types to obtain and process any type of descriptor and
+ * descriptors produced by different components of the Tor network:</p>
+ *
+ * <ol>
+ * <li>General-purpose types comprise
+ * {@link org.torproject.descriptor.DescriptorSourceFactory} which is the
+ * main entry point into using this library. This factory is used to
+ * create the descriptor sources for obtaining remote descriptor data
+ * ({@link org.torproject.descriptor.DescriptorDownloader} and
+ * {@link org.torproject.descriptor.DescriptorCollector}) and descriptor
+ * sources for processing local descriptor data
+ * ({@link org.torproject.descriptor.DescriptorReader} and
+ * {@link org.torproject.descriptor.DescriptorParser}). General-purpose
+ * types also include descriptor containers
+ * ({@link org.torproject.descriptor.DescriptorRequest} and
+ * {@link org.torproject.descriptor.DescriptorFile}) and the
+ * superinterface for all provided descriptors
+ * ({@link org.torproject.descriptor.Descriptor}).</li>
+ *
+ * <li>The first group of descriptors is published by relays and servers
+ * in the Tor network. These interfaces include server descriptors
+ * ({@link org.torproject.descriptor.ServerDescriptor} with subinterfaces
+ * {@link org.torproject.descriptor.RelayServerDescriptor} and
+ * {@link org.torproject.descriptor.BridgeServerDescriptor}), extra-info
+ * descriptors ({@link org.torproject.descriptor.ExtraInfoDescriptor} with
+ * subinterfaces
+ * {@link org.torproject.descriptor.RelayExtraInfoDescriptor} and
+ * {@link org.torproject.descriptor.BridgeExtraInfoDescriptor}),
+ * microdescriptors which are derived from server descriptors by the
+ * directory authorities
+ * ({@link org.torproject.descriptor.Microdescriptor}), and helper types
+ * for parts of the aforementioned descriptors
+ * ({@link org.torproject.descriptor.BandwidthHistory}).</li>
+ *
+ * <li>The second group of descriptors is generated by authoritative
+ * directory servers that form an opinion about relays and bridges in the
+ * Tor network. These include descriptors specified in version 3 of the
+ * directory protocol
+ * ({@link org.torproject.descriptor.RelayNetworkStatusConsensus},
+ * {@link org.torproject.descriptor.RelayNetworkStatusVote},
+ * {@link org.torproject.descriptor.DirectoryKeyCertificate}, and helper
+ * types for descriptor parts
+ * {@link org.torproject.descriptor.DirSourceEntry},
+ * {@link org.torproject.descriptor.NetworkStatusEntry}, and
+ * {@link org.torproject.descriptor.DirectorySignature}), descriptors from
+ * earlier directory protocol version 2
+ * ({@link org.torproject.descriptor.RelayNetworkStatus}) and version 1
+ * ({@link org.torproject.descriptor.RelayDirectory} and
+ * {@link org.torproject.descriptor.RouterStatusEntry}), as well as
+ * descriptors published by the bridge authority and sanitized by the
+ * CollecTor service
+ * ({@link org.torproject.descriptor.BridgeNetworkStatus}).</li>
+ *
+ * <li>The third group of descriptors is created by auxiliary services
+ * connected to the Tor network rather than by the Tor software. This
+ * group comprises descriptors by the bridge distribution service BridgeDB
+ * ({@link org.torproject.descriptor.BridgePoolAssignment}), the exit list
+ * service TorDNSEL ({@link org.torproject.descriptor.ExitList}), and the
+ * performance measurement service Torperf
+ * ({@link org.torproject.descriptor.TorperfResult}).</li>
+ * </ol>
+ *
+ * @since 1.0.0
+ */
+package org.torproject.descriptor;
+
diff --git a/src/org/torproject/descriptor/BandwidthHistory.java b/src/org/torproject/descriptor/BandwidthHistory.java
deleted file mode 100644
index 0be1a53..0000000
--- a/src/org/torproject/descriptor/BandwidthHistory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains the bandwidth history of a relay or bridge.
- *
- * <p>A bandwidth history is not a descriptor type of its own but usually
- * part of extra-info descriptors ({@link ExtraInfoDescriptor}) or server
- * descriptors ({@link ServerDescriptor}).</p>
- *
- * @since 1.0.0
- */
-public interface BandwidthHistory {
-
- /**
- * Return the original bandwidth history line as contained in the
- * descriptor, possibly prefixed with {@code "opt "}.
- *
- * @since 1.0.0
- */
- public String getLine();
-
- /**
- * Return the time in milliseconds since the epoch when the most recent
- * interval ends.
- *
- * @since 1.0.0
- */
- public long getHistoryEndMillis();
-
- /**
- * Return the interval length in seconds.
- *
- * @since 1.0.0
- */
- public long getIntervalLength();
-
- /**
- * Return the (possibly empty) bandwidth history with map keys being
- * times in milliseconds since the epoch when intervals end and map
- * values being number of bytes used in the interval, ordered from
- * oldest to newest interval.
- *
- * @since 1.0.0
- */
- public SortedMap<Long, Long> getBandwidthValues();
-}
-
diff --git a/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java b/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
deleted file mode 100644
index a3c168d..0000000
--- a/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a sanitized bridge extra-info descriptor.
- *
- * <p>Sanitized bridge extra-info descriptors share many contents with
- * relay extra-info descriptors ({@link RelayExtraInfoDescriptor}), which
- * is why they share a common
- * superinterface ({@link ExtraInfoDescriptor}). The main purpose of
- * having two subinterfaces is being able to distinguish descriptor types
- * more easily.</p>
- *
- * <p>Details about sanitizing bridge extra-info descriptors can be found
- * <a href="https://collector.torproject.org/#type-bridge-extra-info">here</a>.
- * </p>
- *
- * @since 1.1.0
- */
-public interface BridgeExtraInfoDescriptor extends ExtraInfoDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/BridgeNetworkStatus.java b/src/org/torproject/descriptor/BridgeNetworkStatus.java
deleted file mode 100644
index c7458fd..0000000
--- a/src/org/torproject/descriptor/BridgeNetworkStatus.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains a sanitized bridge network status document.
- *
- * <p>The bridge directory authority periodically publishes a network
- * status document with one entry per known bridge in the network
- * ({@link NetworkStatusEntry}) containing: a hash of its identity key, a
- * hash of its most recent server descriptor, and a summary of what the
- * bridge authority believed about its status.</p>
- *
- * <p>The main purpose of this document is to get an authoritative list of
- * running bridges to the bridge distribution service BridgeDB.</p>
- *
- * <p>Details about sanitizing bridge network statuses can be found
- * <a href="https://collector.torproject.org/#type-bridge-network-status">here</a>.
- * </p>
- *
- * @since 1.0.0
- */
-public interface BridgeNetworkStatus extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the minimum uptime in seconds that this authority requires
- * for assigning the Stable flag, or -1 if the authority doesn't report
- * this value.
- *
- * @since 1.1.0
- */
- public long getStableUptime();
-
- /**
- * Return the minimum MTBF (mean time between failure) that this
- * authority requires for assigning the Stable flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getStableMtbf();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Fast flag, or -1 if the authority doesn't report this
- * value.
- *
- * @since 1.1.0
- */
- public long getFastBandwidth();
-
- /**
- * Return the minimum WFU (weighted fractional uptime) in percent that
- * this authority requires for assigning the Guard flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public double getGuardWfu();
-
- /**
- * Return the minimum weighted time in seconds that this authority
- * needs to know about a relay before assigning the Guard flag, or -1 if
- * the authority doesn't report this information.
- *
- * @since 1.1.0
- */
- public long getGuardTk();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getGuardBandwidthIncludingExits();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can not be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getGuardBandwidthExcludingExits();
-
- /**
- * Return 1 if the authority has measured enough MTBF info to use the
- * MTBF requirement instead of the uptime requirement for assigning the
- * Stable flag, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getEnoughMtbfInfo();
-
- /**
- * Return 1 if the authority has enough measured bandwidths that it'll
- * ignore the advertised bandwidth claims of routers without measured
- * bandwidth, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getIgnoringAdvertisedBws();
-
- /**
- * Return status entries for each contained bridge, with map keys being
- * SHA-1 digests of SHA-1 digest of the bridges' public identity keys,
- * encoded as 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-}
-
diff --git a/src/org/torproject/descriptor/BridgePoolAssignment.java b/src/org/torproject/descriptor/BridgePoolAssignment.java
deleted file mode 100644
index 2de4ee9..0000000
--- a/src/org/torproject/descriptor/BridgePoolAssignment.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains a sanitized list of bridges together with the distribution
- * pools they have been assigned to by the bridge distribution service
- * BridgeDB.
- *
- * <p>BridgeDB receives bridge network statuses
- * ({@link BridgeNetworkStatus}) from the bridge authority, assigns these
- * bridges to persistent distribution rings, and hands them out to bridge
- * users. BridgeDB periodically dumps the list of running bridges with
- * information about the rings, subrings, and file buckets to which they
- * are assigned to a local file.</p>
- *
- * <p>Details about sanitizing bridge pool assignments can be found
- * <a href="https://collector.torproject.org/#type-bridge-pool-assignment">here</a>.
- * </p>
- *
- * @since 1.0.0
- */
-public interface BridgePoolAssignment extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the entries contained in this bridge pool assignment list
- * with map keys being SHA-1 digests of SHA-1 digest of the bridges'
- * public identity keys, encoded as 40 upper-case hexadecimal
- * characters, and map values being assignment strings, e.g.
- * {@code "https ring=3 flag=stable"}.
- *
- * @since 1.0.0
- */
- public SortedMap<String, String> getEntries();
-}
-
diff --git a/src/org/torproject/descriptor/BridgeServerDescriptor.java b/src/org/torproject/descriptor/BridgeServerDescriptor.java
deleted file mode 100644
index 7d4503f..0000000
--- a/src/org/torproject/descriptor/BridgeServerDescriptor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a sanitized bridge server descriptor.
- *
- * <p>Sanitized bridge server descriptors share many contents with relay
- * server descriptors ({@link RelayServerDescriptor}), which is why they
- * share a common superinterface ({@link ServerDescriptor}). The main
- * purpose of having two subinterfaces is being able to distinguish
- * descriptor types more easily.</p>
- *
- * <p>Details about sanitizing bridge server descriptors can be found
- * <a href="https://collector.torproject.org/#type-bridge-server-descriptor">here</a>.
- * </p>
- *
- * @since 1.1.0
- */
-public interface BridgeServerDescriptor extends ServerDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/Descriptor.java b/src/org/torproject/descriptor/Descriptor.java
deleted file mode 100644
index 7cad109..0000000
--- a/src/org/torproject/descriptor/Descriptor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Superinterface for any descriptor with access to generic information
- * about the descriptor.
- *
- * @since 1.0.0
- */
-public interface Descriptor {
-
- /**
- * Return the raw descriptor bytes.
- *
- * @since 1.0.0
- */
- public byte[] getRawDescriptorBytes();
-
- /**
- * Return the (possibly empty) list of annotations in the format
- * {@code "@key( value)*"}.
- *
- * @since 1.0.0
- */
- public List<String> getAnnotations();
-
- /**
- * Return any unrecognized lines when parsing this descriptor, or an
- * empty list if there were no unrecognized lines.
- *
- * @since 1.0.0
- */
- public List<String> getUnrecognizedLines();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorCollector.java b/src/org/torproject/descriptor/DescriptorCollector.java
deleted file mode 100644
index b1027dc..0000000
--- a/src/org/torproject/descriptor/DescriptorCollector.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-
-/**
- * Descriptor source that synchronizes descriptors from the CollecTor
- * service to a given local directory.
- *
- * <p>This type is not a descriptor source in the proper sense, because it
- * does not produce descriptors by itself. But it often creates the
- * prerequisites for reading descriptors from disk using
- * {@link DescriptorReader}.</p>
- *
- * <p>Code sample:</p>
- * <pre>{@code
- * DescriptorCollector descriptorCollector =
- * DescriptorSourceFactory.createDescriptorCollector();
- * descriptorCollector.collectDescriptors(
- * // Download from Tor's main CollecTor instance,
- * "https://collector.torproject.org",
- * // include network status consensuses and relay server descriptors
- * new String[] { "/recent/relay-descriptors/consensuses/",
- * "/recent/relay-descriptors/server-descriptors/" },
- * // regardless of last-modified time,
- * 0L,
- * // write to the local directory called in/,
- * new File("in"),
- * // and delete extraneous files that do not exist remotely anymore.
- * true);
- * }</pre>
- *
- * @since 1.0.0
- */
-public interface DescriptorCollector {
-
- /**
- * Fetch remote files from a CollecTor instance that do not yet exist
- * locally and possibly delete local files that do not exist remotely
- * anymore.
- *
- * @param collecTorBaseUrl CollecTor base URL without trailing slash,
- * e.g., {@code "https://collector.torproject.org"}
- * @param remoteDirectories Remote directories to collect descriptors
- * from, e.g.,
- * {@code "/recent/relay-descriptors/server-descriptors/"}, without
- * processing subdirectories unless they are explicitly listed
- * @param minLastModified Minimum last-modified time in milliseconds of
- * files to be collected, or 0 for collecting all files
- * @param localDirectory Directory where collected files will be written
- * @param deleteExtraneousLocalFiles Whether to delete all local files
- * that do not exist remotely anymore
- *
- * @since 1.0.0
- */
- public void collectDescriptors(String collecTorBaseUrl,
- String[] remoteDirectories, long minLastModified,
- File localDirectory, boolean deleteExtraneousLocalFiles);
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorDownloader.java b/src/org/torproject/descriptor/DescriptorDownloader.java
deleted file mode 100644
index f0b1101..0000000
--- a/src/org/torproject/descriptor/DescriptorDownloader.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * Descriptor source that downloads relay descriptors from directory
- * authorities or mirrors.
- *
- * <p>Downloading descriptors is done in a batch which starts after
- * setting any configuration options and initiating the download
- * process.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorDownloader {
-
- /**
- * Add a directory authority to download descriptors from, which is
- * only required for downloading network status votes and will be used
- * when no directory mirrors are available.
- *
- * @since 1.0.0
- */
- public void addDirectoryAuthority(String nickname, String ip,
- int dirPort);
-
- /**
- * Add a directory mirror to download descriptors from, which is
- * preferred for downloading descriptors, except for network status
- * votes which are only available on directory authorities.
- *
- * @since 1.0.0
- */
- public void addDirectoryMirror(String nickname, String ip, int dirPort);
-
- /**
- * Include the current network status consensus in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentConsensus();
-
- /**
- * Include the current network status consensus in the downloads, and
- * attempt to download it from all directory authorities.
- *
- * <p>The primary purpose of doing this is to compare different
- * consensuses and download characteristics to each other. Typically,
- * downloading from a single directory mirror or authority is
- * sufficient.</p>
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentConsensusFromAllDirectoryAuthorities();
-
- /**
- * Include the current network status votes referenced from a
- * previously downloaded consensus in the downloads, which requires
- * downloading the current consensus from at least one directory mirror
- * or authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentReferencedVotes();
-
- /**
- * Include the current network status vote published by the given
- * directory authority in the downloads, which requires downloading from
- * at least one directory authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentVote(String fingerprint);
-
- /**
- * Include the current network status votes published by the given
- * directory authorities in the downloads, which requires downloading
- * from at least one directory authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentVotes(Set<String> fingerprints);
-
- /**
- * Include all server descriptors referenced from a previously
- * downloaded network status consensus in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeReferencedServerDescriptors();
-
- /**
- * Exclude the server descriptor with the given identifier from the
- * downloads even if it's referenced from a consensus and we're supposed
- * to download all referenced server descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeServerDescriptor(String identifier);
-
- /**
- * Exclude the server descriptors with the given identifiers from the
- * downloads even if they are referenced from a consensus and we're
- * supposed to download all referenced server descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeServerDescriptors(Set<String> identifier);
-
- /**
- * Include all extra-info descriptors referenced from previously
- * downloaded server descriptors in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeReferencedExtraInfoDescriptors();
-
- /**
- * Exclude the extra-info descriptor with the given identifier from the
- * downloads even if it's referenced from a server descriptor and we're
- * supposed to download all referenced extra-info descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeExtraInfoDescriptor(String identifier);
-
- /**
- * Exclude the extra-info descriptors with the given identifiers from
- * the downloads even if they are referenced from server descriptors
- * and we're supposed to download all referenced extra-info
- * descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeExtraInfoDescriptors(Set<String> identifiers);
-
- /**
- * Define a connect timeout for a single request.
- *
- * <p>If a timeout expires, no further requests will be sent to the
- * directory authority or mirror. Setting this value to 0 disables the
- * connect timeout. Default value is 1 minute (60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setConnectTimeout(long connectTimeoutMillis);
-
- /**
- * Define a read timeout for a single request.
- *
- * <p>If a timeout expires, no further requests will be sent to the
- * directory authority or mirror. Setting this value to 0 disables the
- * read timeout. Default value is 1 minute (60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setReadTimeout(long readTimeoutMillis);
-
- /**
- * Define a global timeout for all requests.
- *
- * <p>Once this timeout expires, all running requests are aborted and no
- * further requests are made. Setting this value to 0 disables the
- * global timeout. Default is 1 hour (60 * 60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setGlobalTimeout(long globalTimeoutMillis);
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines();
-
- /**
- * Download the previously configured relay descriptors and make them
- * available via the returned blocking iterator.
- *
- * <p>Whenever the downloader runs out of descriptors and expects to
- * provide more shortly after, it blocks the caller. This method can
- * only be run once.</p>
- *
- * @since 1.0.0
- */
- public Iterator<DescriptorRequest> downloadDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorFile.java b/src/org/torproject/descriptor/DescriptorFile.java
deleted file mode 100644
index 417d7f9..0000000
--- a/src/org/torproject/descriptor/DescriptorFile.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Container for descriptors read from a file.
- *
- * <p>When the {@link DescriptorReader} reads descriptors from local files
- * it provides an iterator over these containers which in turn contain
- * references to classes implementing the {@link Descriptor} interface.
- * This container also stores potentially useful meta-data about the
- * descriptor file.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorFile {
-
- /**
- * Return the directory where this descriptor file was contained, or
- * null if the file was contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getDirectory();
-
- /**
- * Return the tarball where this descriptor file was contained, or null
- * if the file was not contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getTarball();
-
- /**
- * Return the descriptor file itself, or null if the descriptor file
- * was contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getFile();
-
- /**
- * Return the descriptor file name, which is either the absolute path
- * of the file on disk, or the tar file entry name.
- *
- * @since 1.0.0
- */
- public String getFileName();
-
- /**
- * Return the time in milliseconds since the epoch when the descriptor
- * file on disk was last modified.
- *
- * @since 1.0.0
- */
- public long getLastModified();
-
- /**
- * Return the descriptors contained in the descriptor file.
- *
- * @since 1.0.0
- */
- public List<Descriptor> getDescriptors();
-
- /**
- * Return the first exception that was thrown when reading this file or
- * parsing its content, or null if no exception was thrown.
- *
- * @since 1.0.0
- */
- public Exception getException();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorParseException.java b/src/org/torproject/descriptor/DescriptorParseException.java
deleted file mode 100644
index 309d3f7..0000000
--- a/src/org/torproject/descriptor/DescriptorParseException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Thrown if raw descriptor contents cannot be parsed to one or more
- * {@link Descriptor} instances, according to descriptor specifications.
- *
- * @since 1.0.0
- */
-@SuppressWarnings("deprecation")
-public class DescriptorParseException
- extends org.torproject.descriptor.impl.DescriptorParseException {
- private static final long serialVersionUID = 100L;
- public DescriptorParseException(String message) {
- super(message);
- }
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorParser.java b/src/org/torproject/descriptor/DescriptorParser.java
deleted file mode 100644
index 680b8b2..0000000
--- a/src/org/torproject/descriptor/DescriptorParser.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Descriptor source that parses descriptors from raw descriptor contents.
- *
- * <p>Unlike most of the other descriptor sources this descriptor source
- * does not operate in a batch-processing mode. It takes the raw
- * descriptor contents of one or more descriptors, parses them, and
- * returns a list of descriptors.</p>
- *
- * <p>This descriptor source is internally used by other descriptor
- * sources but can also be used directly by applications that obtain
- * raw descriptor contents via other means than one of the existing
- * descriptor sources.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorParser {
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines);
-
- /**
- * Parse descriptors in the given byte array, possibly parsing the
- * publication time from the file name, depending on the descriptor
- * type.
- *
- * @since 1.0.0
- */
- public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
- String fileName) throws DescriptorParseException;
-}
diff --git a/src/org/torproject/descriptor/DescriptorReader.java b/src/org/torproject/descriptor/DescriptorReader.java
deleted file mode 100644
index 771755e..0000000
--- a/src/org/torproject/descriptor/DescriptorReader.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-import java.util.Iterator;
-import java.util.SortedMap;
-
-/**
- * Descriptor source that reads descriptors from local files and provides
- * an iterator over parsed descriptors.
- *
- * <p>This descriptor source is likely the most widely used one, possibly
- * in combination with {@link DescriptorCollector} to synchronize
- * descriptors from the CollecTor service.</p>
- *
- * <p>Reading descriptors is done in a batch which starts after setting
- * any configuration options and initiating the read process.</p>
- *
- * <p>Code sample:</p>
- * <pre>{@code
- * DescriptorReader descriptorReader =
- * DescriptorSourceFactory.createDescriptorReader();
- * // Read descriptors from local directory called in/.
- * descriptorReader.addDirectory(new File("in"));
- * Iterator<DescriptorFile> descriptorFiles =
- * descriptorReader.readDescriptors();
- * while (descriptorFiles.hasNext()) {
- * DescriptorFile descriptorFile = descriptorFiles.next();
- * for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- * if ((descriptor instanceof RelayNetworkStatusConsensus)) {
- * // Only process network status consensuses, ignore the rest.
- * RelayNetworkStatusConsensus consensus =
- * (RelayNetworkStatusConsensus) descriptor;
- * processConsensus(consensus);
- * }
- * }
- * }}</pre>
- *
- * @since 1.0.0
- */
-public interface DescriptorReader {
-
- /**
- * Add a local directory to read descriptors from, which may contain
- * descriptor files or tarballs containing descriptor files.
- *
- * @since 1.0.0
- */
- public void addDirectory(File directory);
-
- /**
- * Add a tarball to read descriptors from, which may be uncompressed,
- * bz2-compressed, or xz-compressed.
- *
- * @since 1.0.0
- */
- public void addTarball(File tarball);
-
- /**
- * Exclude files that are listed in the given history file and that
- * haven't changed since they have last been read.
- *
- * <p>Add a new line for each descriptor that is read in this execution
- * and remove lines for files that don't exist anymore.</p>
- *
- * <p>Lines in the history file contain the last modified time in
- * milliseconds since the epoch and the absolute path of a file.</p>
- *
- * @since 1.0.0
- */
- public void setExcludeFiles(File historyFile);
-
- /**
- * Exclude files if they haven't changed since the corresponding last
- * modified timestamps.
- *
- * <p>Can be used instead of (or in addition to) a history file.</p>
- *
- * @since 1.0.0
- */
- public void setExcludedFiles(SortedMap<String, Long> excludedFiles);
-
- /**
- * Return files and last modified timestamps of files that exist in the
- * input directory or directories, but that have been excluded from
- * parsing, because they haven't changed since they were last read.
- *
- * <p>Can be used instead of (or in addition to) a history file when
- * combined with the set of parsed files.</p>
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExcludedFiles();
-
- /**
- * Return files and last modified timestamps of files that exist in the
- * input directory or directories and that have been parsed.
- *
- * <p>Can be used instead of (or in addition to) a history file when
- * combined with the set of excluded files.</p>
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getParsedFiles();
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines();
-
- /**
- * Don't keep more than this number of parsed descriptor files in the
- * queue.
- *
- * <p>The default is 100, but if descriptor files contain hundreds or
- * even thousands of descriptors, that default may be too high.</p>
- *
- * @since 1.0.0
- */
- public void setMaxDescriptorFilesInQueue(int max);
-
- /**
- * Read the previously configured descriptors and make them available
- * via the returned blocking iterator.
- *
- * <p>Whenever the reader runs out of descriptors and expects to provide
- * more shortly after, it blocks the caller. This method can only be
- * run once.</p>
- *
- * @since 1.0.0
- */
- public Iterator<DescriptorFile> readDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorRequest.java b/src/org/torproject/descriptor/DescriptorRequest.java
deleted file mode 100644
index c36c0c0..0000000
--- a/src/org/torproject/descriptor/DescriptorRequest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Container for descriptors downloaded from a directory authority or
- * mirror.
- *
- * <p>When the {@link DescriptorDownloader} downloads descriptors from
- * directory authorities or mirrors it provides an iterator over these
- * containers which in turn contain references to classes implementing the
- * {@link Descriptor} interface. This container also stores potentially
- * useful meta-data about the descriptor request.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorRequest {
-
- /**
- * Return the request URL that was used in this request.
- *
- * @since 1.0.0
- */
- public String getRequestUrl();
-
- /**
- * Return the nickname of the directory mirror or authority as
- * previously configured.
- *
- * @since 1.0.0
- */
- public String getDirectoryNickname();
-
- /**
- * Return the first exception that was thrown when making this request
- * or parsing the response, or null if no exception was thrown.
- *
- * @since 1.0.0
- */
- public Exception getException();
-
- /**
- * Return the response code that the directory mirror or authority
- * returned.
- *
- * @since 1.0.0
- */
- public int getResponseCode();
-
- /**
- * Return the time in milliseconds since the epoch when this request
- * was started.
- *
- * @since 1.0.0
- */
- public long getRequestStart();
-
- /**
- * Return the time in milliseconds since the epoch when this request
- * ended.
- *
- * @since 1.0.0
- */
- public long getRequestEnd();
-
- /**
- * Return whether this request ended, because the connect timeout has
- * expired.
- *
- * @since 1.0.0
- */
- public boolean connectTimeoutHasExpired();
-
- /**
- * Return whether this request ended, because the read timeout has
- * expired.
- *
- * @since 1.0.0
- */
- public boolean readTimeoutHasExpired();
-
- /**
- * Return whether this request ended, because the global timeout for
- * all requests has expired.
- *
- * @since 1.0.0
- */
- public boolean globalTimeoutHasExpired();
-
- /**
- * Return the descriptors contained in the reply.
- *
- * @since 1.0.0
- */
- public List<Descriptor> getDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorSourceFactory.java b/src/org/torproject/descriptor/DescriptorSourceFactory.java
deleted file mode 100644
index af13f39..0000000
--- a/src/org/torproject/descriptor/DescriptorSourceFactory.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Factory for descriptor sources which in turn produce descriptors.
- *
- * <p>Descriptor sources are the only producers of classes implementing
- * the {@link Descriptor} superinterface. There exist descriptor sources
- * for obtaining remote descriptor data ({@link DescriptorDownloader} and
- * {@link DescriptorCollector}) and descriptor sources for processing
- * local descriptor data ({@link DescriptorReader} and
- * {@link DescriptorParser}).</p>
- *
- * <p>By default, this factory returns implementations from the library's
- * own impl package. This may be overridden by setting Java properties,
- * though most users will simply use the default implementations.</p>
- *
- * <p>These properties can be used for setting the implementation:</p>
- * <ul>
- * <li>{@code descriptor.collector}</li>
- * <li>{@code descriptor.downloader}</li>
- * <li>{@code descriptor.parser}</li>
- * <li>{@code descriptor.reader}</li>
- * </ul>
- *
- * <p>Assuming the classpath contains the special implementation
- * referenced, your application classes as well as a descriptor API jar
- * the following is an example for using a different implementation of the
- * descriptor downloader:</p>
- *
- * <p><code>
- * java -Ddescriptor.downloader=my.special.descriptorimpl.Downloader my.app.Mainclass
- * </code></p>
- *
- * @since 1.0.0
- */
-public final class DescriptorSourceFactory {
-
- /**
- * Default implementation of the {@link DescriptorDownloader}
- * descriptor source.
- *
- * @since 1.0.0
- */
- public final static String DOWNLOADER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorDownloaderImpl";
-
- /**
- * Default implementation of the {@link DescriptorParser} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String PARSER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorParserImpl";
-
- /**
- * Default implementation of the {@link DescriptorReader} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String READER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorReaderImpl";
-
- /**
- * Default implementation of the {@link DescriptorCollector} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String COLLECTOR_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorCollectorImpl";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorParser} descriptor source, which is by default set
- * to the class in {@link #PARSER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String PARSER_PROPERTY = "descriptor.parser";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorReader} descriptor source, which is by default set
- * to the class in {@link #READER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String READER_PROPERTY = "descriptor.reader";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorDownloader} descriptor source, which is by default
- * set to the class in {@link #DOWNLOADER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String DOWNLOADER_PROPERTY =
- "descriptor.downloader";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorCollector} descriptor source, which is by default
- * set to the class in {@link #COLLECTOR_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String COLLECTOR_PROPERTY = "descriptor.collector";
-
- /**
- * Create a new {@link DescriptorParser} by instantiating the class in
- * {@link #PARSER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorParser createDescriptorParser() {
- return (DescriptorParser) retrieve(PARSER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorReader} by instantiating the class in
- * {@link #READER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorReader createDescriptorReader() {
- return (DescriptorReader) retrieve(READER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorDownloader} by instantiating the class
- * in {@link #DOWNLOADER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorDownloader createDescriptorDownloader() {
- return (DescriptorDownloader) retrieve(DOWNLOADER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorCollector} by instantiating the class
- * in {@link #COLLECTOR_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorCollector createDescriptorCollector() {
- return (DescriptorCollector) retrieve(COLLECTOR_PROPERTY);
- }
-
- private final static <T> Object retrieve(String type) {
- Object object;
- String clazzName = null;
- try {
- switch (type) {
- case PARSER_PROPERTY:
- clazzName = System.getProperty(type, PARSER_DEFAULT);
- break;
- case DOWNLOADER_PROPERTY:
- clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
- break;
- case READER_PROPERTY:
- clazzName = System.getProperty(type, READER_DEFAULT);
- break;
- case COLLECTOR_PROPERTY:
- clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
- break;
- }
- object = ClassLoader.getSystemClassLoader().loadClass(clazzName).
- newInstance();
- } catch (ClassNotFoundException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- } catch (InstantiationException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- } catch (IllegalAccessException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- }
- return object;
- }
-}
-
diff --git a/src/org/torproject/descriptor/DirSourceEntry.java b/src/org/torproject/descriptor/DirSourceEntry.java
deleted file mode 100644
index 96d81ee..0000000
--- a/src/org/torproject/descriptor/DirSourceEntry.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains details about an authority and its vote that contributed to a
- * consensus.
- *
- * <p>A directory source entry is not a descriptor type of its own but is
- * part of a network status consensus
- * ({@link RelayNetworkStatusConsensus}).</p>
- *
- * @since 1.0.0
- */
-public interface DirSourceEntry {
-
- /**
- * Return the raw directory source entry bytes.
- *
- * @since 1.0.0
- */
- public byte[] getDirSourceEntryBytes();
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.2.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getIp();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return the TCP port where this authority accepts TLS connections for
- * the main OR protocol.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return whether this directory source entry was created using a
- * legacy key.
- *
- * @since 1.0.0
- */
- public boolean isLegacy();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the SHA-1 vote digest, encoded as 40 lower-case hexadecimal
- * characters.
- *
- * @since 1.0.0
- */
- public String getVoteDigest();
-}
-
diff --git a/src/org/torproject/descriptor/DirectoryKeyCertificate.java b/src/org/torproject/descriptor/DirectoryKeyCertificate.java
deleted file mode 100644
index 07211ef..0000000
--- a/src/org/torproject/descriptor/DirectoryKeyCertificate.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a key certificate in the version 3 directory protocol.
- *
- * <p>Every directory authority in the version 3 directory protocol uses
- * two keys: a medium-term signing key, and a long-term authority identity
- * key. (Authorities also have a relay identity key used in their role as
- * a relay and by earlier versions of the directory protocol.) The
- * identity key is used from time to time to sign new key certificates
- * containing signing keys. The contained signing key is used to sign key
- * certificates and status documents.</p>
- *
- * @since 1.0.0
- */
-public interface DirectoryKeyCertificate extends Descriptor {
-
- /**
- * Return the version of this descriptor, which must be 3 or higher.
- *
- * @since 1.0.0
- */
- public int getDirKeyCertificateVersion();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the certificate does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or -1 if the certificate does not contain a port.
- *
- * @since 1.0.0
- */
- public int getPort();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the authority's identity key in PEM format.
- *
- * @since 1.0.0
- */
- public String getDirIdentityKey();
-
- /**
- * Return the time in milliseconds since the epoch when the authority's
- * signing key and this key certificate were generated.
- *
- * @since 1.0.0
- */
- public long getDirKeyPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch after which the
- * authority's signing key is no longer valid.
- *
- * @since 1.0.0
- */
- public long getDirKeyExpiresMillis();
-
- /**
- * Return the authority's signing key in PEM format.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return the signature of the authority's identity key made using the
- * authority's signing key, or null if the certificate does not contain
- * such a signature.
- *
- * @since 1.0.0
- */
- public String getDirKeyCrosscert();
-
- /**
- * Return the certificate signature from the initial item
- * "dir-key-certificate-version" until the final item
- * "dir-key-certification", signed with the authority identity key.
- *
- * @since 1.0.0
- */
- public String getDirKeyCertification();
-
- /**
- * Return the SHA-1 certificate digest, encoded as 40 lower-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getCertificateDigest();
-}
-
diff --git a/src/org/torproject/descriptor/DirectorySignature.java b/src/org/torproject/descriptor/DirectorySignature.java
deleted file mode 100644
index 8877a4e..0000000
--- a/src/org/torproject/descriptor/DirectorySignature.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains the signature of a network status consensus or vote.
- *
- * <p>A directory signature is not a descriptor type of its own but is
- * part of a network status consensus
- * ({@link RelayNetworkStatusConsensus}) or vote
- * ({@link RelayNetworkStatusVote}).</p>
- *
- * @since 1.0.0
- */
-public interface DirectorySignature {
-
- /**
- * Return the digest algorithm, which is "sha1" by default and which
- * can be "sha256" or another digest algorithm.
- *
- * @since 1.0.0
- */
- public String getAlgorithm();
-
- /**
- * Return the SHA-1 digest of the authority's long-term identity key in
- * the version 3 directory protocol, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the SHA-1 digest of the authority's medium-term signing key
- * in the version 3 directory protocol, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getSigningKeyDigest();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key in the version 3 directory protocol.
- *
- * @since 1.0.0
- */
- public String getSignature();
-}
-
diff --git a/src/org/torproject/descriptor/ExitList.java b/src/org/torproject/descriptor/ExitList.java
deleted file mode 100644
index 2a5cb2e..0000000
--- a/src/org/torproject/descriptor/ExitList.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Contains an exit list containing the IP addresses of relays that the
- * exit list service TorDNSEL found when exiting through them.
- *
- * @since 1.0.0
- */
-public interface ExitList extends Descriptor {
-
- /**
- * End-of-line character expected in exit lists.
- *
- * @since 1.0.0
- */
- public final static String EOL = "\n";
-
- /**
- * Exit list entry containing results from a single exit scan.
- *
- * @since 1.1.0
- */
- public interface Entry {
-
- /**
- * Return the scanned relay's fingerprint, which is a SHA-1 digest of
- * the relays's public identity key, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.1.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when the scanned
- * relay's last known descriptor was published.
- *
- * @since 1.1.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the network
- * status that this scan was based on was published.
- *
- * @since 1.1.0
- */
- public long getLastStatusMillis();
-
- /**
- * Return the IP addresses that were determined in the scan with map
- * keys being IPv4 addresses in dotted-quad format and map values
- * being scan times in milliseconds since the epoch.
- *
- * @since 1.1.0
- */
- public Map<String, Long> getExitAddresses();
- }
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was downloaded.
- *
- * @since 1.0.0
- */
- public long getDownloadedMillis();
-
- /**
- * Return the unordered set of exit scan results.
- *
- * @since 1.0.0
- * @deprecated The {@link ExitListEntry} type has been deprecated and
- * superseded by {@link ExitList.Entry} which is returned by
- * {@link #getEntries()}.
- */
- @Deprecated
- public Set<ExitListEntry> getExitListEntries();
-
- /**
- * Return the unordered set of exit scan results.
- *
- * @since 1.1.0
- */
- public Set<ExitList.Entry> getEntries();
-}
-
diff --git a/src/org/torproject/descriptor/ExitListEntry.java b/src/org/torproject/descriptor/ExitListEntry.java
deleted file mode 100644
index 2a3d79f..0000000
--- a/src/org/torproject/descriptor/ExitListEntry.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Exit list entry containing results from a single exit scan.
- *
- * @since 1.0.0
- * @deprecated Superseded by {@link ExitList.Entry}.
- */
-@Deprecated
-public interface ExitListEntry extends ExitList.Entry {
-
- /**
- * Return the scanned relay's fingerprint, which is a SHA-1 digest of
- * the relays's public identity key, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when the scanned
- * relay's last known descriptor was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the network
- * status that this scan was based on was published.
- *
- * @since 1.0.0
- */
- public long getLastStatusMillis();
-
- /**
- * Return the IPv4 address in dotted-quad format that was determined in
- * the scan.
- *
- * @since 1.0.0
- */
- public String getExitAddress();
-
- /**
- * Return the scan time in milliseconds since the epoch.
- *
- * @since 1.0.0
- */
- public long getScanMillis();
-}
-
diff --git a/src/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/org/torproject/descriptor/ExtraInfoDescriptor.java
deleted file mode 100644
index 49efbf3..0000000
--- a/src/org/torproject/descriptor/ExtraInfoDescriptor.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-
-/**
- * Contains a relay or sanitized bridge extra-info descriptor.
- *
- * <p>Relays publish extra-info descriptors as an addendum to server
- * descriptors ({@link ServerDescriptor}) to report extraneous information
- * to the directory authorities that clients do not need to download in
- * order to function. This information primarily consists of statistics
- * gathered by the relay about its usage and can take up a lot of
- * descriptor space. The separation of server descriptors and extra-info
- * descriptors has become less relevant with the introduction of
- * microdescriptors ({@link Microdescriptor}) that are derived from server
- * descriptors by the directory authority and which clients download
- * instead of server descriptors, but it persists.</p>
- *
- * <p>Bridges publish extra-info descriptors to the bridge authority for
- * the same reason, to include statistics about their usage without
- * increasing the directory protocol overhead for bridge clients. In this
- * case, the separation of server descriptors and extra-info descriptors
- * is slightly more relevant, because there are no microdescriptors for
- * bridges, so that bridge clients still download server descriptors of
- * bridges they're using. Another reason is that bridges need to include
- * information like details of all the transports they support in their
- * descriptors, and bridge clients using one such transport are not
- * supposed to learn the details of the other transports.</p>
- *
- * <p>It's worth noting that all contents of extra-info descriptors are
- * written and signed by relays and bridges without a third party
- * verifying their correctness. The (bridge) directory authorities may
- * decide to exclude dishonest servers from the network statuses they
- * produce, but that wouldn't be reflected in extra-info descriptors.</p>
- *
- * @since 1.0.0
- */
-public interface ExtraInfoDescriptor extends Descriptor {
-
- /**
- * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
- * descriptors) or upper-case (bridge descriptors) hexadecimal
- * characters, that is used to reference this descriptor from a server
- * descriptor.
- *
- * @since 1.0.0
- */
- public String getExtraInfoDigest();
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that may be used to reference
- * this descriptor from a server descriptor.
- *
- * @since 1.1.0
- */
- public String getExtraInfoDigestSha256();
-
- /**
- * Return the server's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the server's public identity key, encoded
- * as 40 upper-case hexadecimal characters, that is typically used to
- * uniquely identify the server.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * and the corresponding server descriptor were generated.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the server's history of read bytes, or null if the descriptor
- * does not contain a bandwidth history; older Tor versions included
- * bandwidth histories in their server descriptors
- * ({@link ServerDescriptor#getReadHistory()}).
- *
- * @since 1.0.0
- */
- public BandwidthHistory getReadHistory();
-
- /**
- * Return the server's history of written bytes, or null if the
- * descriptor does not contain a bandwidth history; older Tor versions
- * included bandwidth histories in their server descriptors
- * ({@link ServerDescriptor#getWriteHistory()}).
- *
- * @since 1.0.0
- */
- public BandwidthHistory getWriteHistory();
-
- /**
- * Return a SHA-1 digest of the GeoIP database file used by this server
- * to resolve client IP addresses to country codes, encoded as 40
- * upper-case hexadecimal characters, or null if no GeoIP database
- * digest is included.
- *
- * @since 1.0.0
- */
- public String getGeoipDbDigest();
-
- /**
- * Return a SHA-1 digest of the GeoIPv6 database file used by this
- * server to resolve client IP addresses to country codes, encoded as 40
- * upper-case hexadecimal characters, or null if no GeoIPv6 database
- * digest is included.
- *
- * @since 1.0.0
- */
- public String getGeoip6DbDigest();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * directory request statistics interval ended, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getDirreqStatsEndMillis();
-
- /**
- * Return the interval length of the included directory request
- * statistics in seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getDirreqStatsIntervalLength();
-
- /**
- * Return statistics on unique IP addresses requesting v2 network
- * statuses with map keys being country codes and map values being
- * numbers of unique IP addresses rounded up to the nearest multiple of
- * 8, or null if no such statistics are included (which is the case with
- * recent Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Ips();
-
- /**
- * Return statistics on unique IP addresses requesting v3 network
- * status consensuses of any flavor with map keys being country codes
- * and map values being numbers of unique IP addresses rounded up to the
- * nearest multiple of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Ips();
-
- /**
- * Return statistics on directory requests for v2 network statuses with
- * map keys being country codes and map values being request numbers
- * rounded up to the nearest multiple of 8, or null if no such
- * statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Reqs();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor with map keys being country codes and map
- * values being request numbers rounded up to the nearest multiple of 8,
- * or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Reqs();
-
- /**
- * Return the share of requests for v2 network statuses that the server
- * expects to receive from clients, or -1.0 if this share is not
- * included (which is the case with recent Tor versions).
- *
- * @since 1.0.0
- */
- public double getDirreqV2Share();
-
- /**
- * Return the share of requests for v3 network status consensuses of
- * any flavor that the server expects to receive from clients, or -1.0
- * if this share is not included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public double getDirreqV3Share();
-
- /**
- * Return statistics on responses to directory requests for v2 network
- * statuses with map keys being response strings and map values being
- * response numbers rounded up to the nearest multiple of 4, or null if
- * no such statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Resp();
-
- /**
- * Return statistics on responses to directory requests for v3 network
- * status consensuses of any flavor with map keys being response strings
- * and map values being response numbers rounded up to the nearest
- * multiple of 4, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Resp();
-
- /**
- * Return statistics on directory requests for v2 network statuses to
- * the server's directory port with map keys being statistic keys and
- * map values being statistic values like counts or quantiles, or null
- * if no such statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2DirectDl();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor to the server's directory port with map
- * keys being statistic keys and map values being statistic values like
- * counts or quantiles, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3DirectDl();
-
- /**
- * Return statistics on directory requests for v2 network statuses
- * tunneled through a circuit with map keys being statistic keys and map
- * values being statistic values, or null if no such statistics are
- * included (which is the case with recent Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2TunneledDl();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor tunneled through a circuit with map keys
- * being statistic keys and map values being statistic values, or null
- * if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3TunneledDl();
-
- /**
- * Return the directory request read history contained in this
- * descriptor, or null if no such history is contained.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getDirreqReadHistory();
-
- /**
- * Return the directory request write history contained in this
- * descriptor, or null if no such history is contained.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getDirreqWriteHistory();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * entry statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getEntryStatsEndMillis();
-
- /**
- * Return the interval length of the included entry statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getEntryStatsIntervalLength();
-
- /**
- * Return statistics on client IP addresses with map keys being country
- * codes and map values being the number of unique IP addresses that
- * have connected from that country rounded up to the nearest multiple
- * of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getEntryIps();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * cell statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getCellStatsEndMillis();
-
- /**
- * Return the interval length of the included cell statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getCellStatsIntervalLength();
-
- /**
- * Return the mean number of processed cells per circuit by circuit
- * decile starting with the loudest decile at index 0 and the quietest
- * decile at index 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public List<Integer> getCellProcessedCells();
-
- /**
- * Return the mean number of cells contained in circuit queues by
- * circuit decile starting with the loudest decile at index 0 and the
- * quietest decile at index 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public List<Double> getCellQueuedCells();
-
- /**
- * Return the mean times in milliseconds that cells spend in circuit
- * queues by circuit decile starting with the loudest decile at index 0
- * and the quietest decile at index 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public List<Integer> getCellTimeInQueue();
-
- /**
- * Return the mean number of circuits included in any of the cell
- * statistics deciles, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public int getCellCircuitsPerDecile();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * statistics on bi-directional connection usage ended, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getConnBiDirectStatsEndMillis();
-
- /**
- * Return the interval length of the included statistics on
- * bi-directional connection usage in seconds, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getConnBiDirectStatsIntervalLength();
-
- /**
- * Return the number of connections on which this server read and wrote
- * less than 2 KiB/s in a 10-second interval, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectBelow();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval and at least 10 times more
- * in read direction than in write direction, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectRead();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval and at least 10 times more
- * in write direction than in read direction, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectWrite();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval but not 10 times more in
- * either direction, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectBoth();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * exit statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getExitStatsEndMillis();
-
- /**
- * Return the interval length of the included exit statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getExitStatsIntervalLength();
-
- /**
- * Return statistics on KiB written to streams exiting the Tor network
- * by target TCP port with map keys being string representations of
- * ports (or {@code "other"}) and map values being KiB rounded up to the
- * next full KiB, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitKibibytesWritten();
-
- /**
- * Return statistics on KiB read from streams exiting the Tor network
- * by target TCP port with map keys being string representations of
- * ports (or {@code "other"}) and map values being KiB rounded up to the
- * next full KiB, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitKibibytesRead();
-
- /**
- * Return statistics on opened streams exiting the Tor network by
- * target TCP port with map keys being string representations of ports
- * (or {@code "other"}) and map values being the number of opened
- * streams, rounded up to the nearest multiple of 4, or null if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitStreamsOpened();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * "geoip" statistics interval started, or -1 if no such statistics are
- * included (which is the case except for very old Tor versions).
- *
- * @since 1.0.0
- */
- public long getGeoipStartTimeMillis();
-
- /**
- * Return statistics on the origin of client IP addresses with map keys
- * being country codes and map values being the number of unique IP
- * addresses that have connected from that country between the start of
- * the statistics interval and the descriptor publication time rounded
- * up to the nearest multiple of 8, or null if no such statistics are
- * included (which is the case except for very old Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getGeoipClientOrigins();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * bridge statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getBridgeStatsEndMillis();
-
- /**
- * Return the interval length of the included bridge statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getBridgeStatsIntervalLength();
-
- /**
- * Return statistics on bridge client IP addresses by country with map
- * keys being country codes and map values being the number of unique IP
- * addresses that have connected from that country rounded up to the
- * nearest multiple of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIps();
-
- /**
- * Return statistics on bridge client IP addresses by IP version with
- * map keys being protocol families, e.g., {@code "v4"} or {@code "v6"},
- * and map values being the number of unique IP addresses rounded up to
- * the nearest multiple of 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIpVersions();
-
- /**
- * Return statistics on bridge client IP addresses by transport with
- * map keys being pluggable transport names, e.g., {@code "obfs2"} or
- * {@code "obfs3"} for known transports, {@code "<OR>"} for the default
- * onion routing protocol, or {@code "<??>"} for an unknown transport,
- * and map values being the number of unique IP addresses rounded up to
- * the nearest multiple of 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIpTransports();
-
- /**
- * Return the (possibly empty) list of pluggable transports supported
- * by this server.
- *
- * @since 1.0.0
- */
- public List<String> getTransports();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * hidden-service statistics interval ended, or -1 if no such statistics
- * are included.
- *
- * @since 1.1.0
- */
- public long getHidservStatsEndMillis();
-
- /**
- * Return the interval length of the included hidden-service statistics
- * in seconds, or -1 if no such statistics are included.
- *
- * @since 1.1.0
- */
- public long getHidservStatsIntervalLength();
-
- /**
- * Return the approximate number of RELAY cells seen in either
- * direction on a circuit after receiving and successfully processing a
- * RENDEZVOUS1 cell, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Double getHidservRendRelayedCells();
-
- /**
- * Return the obfuscation parameters applied to the original
- * measurement value of RELAY cells seen in either direction on a
- * circuit after receiving and successfully processing a RENDEZVOUS1
- * cell, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Map<String, Double> getHidservRendRelayedCellsParameters();
-
- /**
- * Return the approximate number of unique hidden-service identities
- * seen in descriptors published to and accepted by this hidden-service
- * directory, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Double getHidservDirOnionsSeen();
-
- /**
- * Return the obfuscation parameters applied to the original
- * measurement value of unique hidden-service identities seen in
- * descriptors published to and accepted by this hidden-service
- * directory, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Map<String, Double> getHidservDirOnionsSeenParameters();
-
- /**
- * Return the RSA-1024 signature of the PKCS1-padded descriptor digest,
- * taken from the beginning of the router line through the newline after
- * the router-signature line, or null if the descriptor doesn't contain
- * a signature (which is the case in sanitized bridge descriptors).
- *
- * @since 1.1.0
- */
- public String getRouterSignature();
-
- /**
- * Return the Ed25519 certificate in PEM format, or null if the
- * descriptor doesn't contain one.
- *
- * @since 1.1.0
- */
- public String getIdentityEd25519();
-
- /**
- * Return the Ed25519 master key, encoded as 43 base64 characters
- * without padding characters, which was either parsed from the optional
- * {@code "master-key-ed25519"} line or derived from the (likewise
- * optional) Ed25519 certificate following the
- * {@code "identity-ed25519"} line, or null if the descriptor contains
- * neither Ed25519 master key nor Ed25519 certificate.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-
- /**
- * Return the Ed25519 signature of the SHA-256 digest of the entire
- * descriptor, encoded as 86 base64 characters without padding
- * characters, from the first character up to and including the first
- * space after the {@code "router-sig-ed25519"} string, prefixed with
- * the string {@code "Tor router descriptor signature v1"}.
- *
- * @since 1.1.0
- */
- public String getRouterSignatureEd25519();
-}
-
diff --git a/src/org/torproject/descriptor/ImplementationNotAccessibleException.java b/src/org/torproject/descriptor/ImplementationNotAccessibleException.java
deleted file mode 100644
index c54e48f..0000000
--- a/src/org/torproject/descriptor/ImplementationNotAccessibleException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Thrown if a descriptor source implementation class cannot be found,
- * instantiated, or accessed.
- *
- * @see DescriptorSourceFactory
- * @since 1.0.0
- */
-@SuppressWarnings("serial")
-public class ImplementationNotAccessibleException
- extends RuntimeException {
-
- public ImplementationNotAccessibleException(String string,
- Throwable ex) {
- super(string, ex);
- }
-}
-
diff --git a/src/org/torproject/descriptor/Microdescriptor.java b/src/org/torproject/descriptor/Microdescriptor.java
deleted file mode 100644
index f19b7df..0000000
--- a/src/org/torproject/descriptor/Microdescriptor.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a relay microdescriptor.
- *
- * <p>A microdescriptor is a stripped-down version of a relay server
- * descriptor ({@link RelayServerDescriptor}) generated by the directory
- * authorities by extracting and/or transforming relay server descriptor
- * contents following strict rules without adding the authority's opinion
- * about the relay. Microdescriptors are referenced from microdescriptor
- * consensuses ({@link RelayNetworkStatusConsensus}) and downloaded by
- * clients to make path-selection decisions and to build circuits.
- * Microdescriptors contain only the most relevant parts that clients care
- * about. Microdescriptors are expected to be relatively static and only
- * change about once per week.</p>
- *
- * @since 1.0.0
- */
-public interface Microdescriptor extends Descriptor {
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that is used to reference this
- * descriptor from a vote or microdescriptor consensus.
- *
- * @since 1.0.0
- */
- public String getMicrodescriptorDigest();
-
- /**
- * Return the RSA-1024 public key in PEM format used to encrypt CREATE
- * cells for this server, or null if the descriptor doesn't contain an
- * onion key.
- *
- * @since 1.0.0
- */
- public String getOnionKey();
-
- /**
- * Return the curve25519 public key, encoded as 43 base64 characters
- * without padding characters, that is used for the ntor circuit
- * extended handshake, or null if the descriptor didn't contain an
- * ntor-onion-key line.
- *
- * @since 1.0.0
- */
- public String getNtorOnionKey();
-
- /**
- * Return IP addresses and TCP ports where this server accepts TLS
- * connections for the main OR protocol, or an empty list if the server
- * does not support additional addresses or ports; entries are given in
- * the order as they are listed in the descriptor; IPv4 addresses are
- * given in dotted-quad format, IPv6 addresses use the colon-separated
- * hexadecimal format surrounded by square brackets, and TCP ports are
- * separated from the IP address using a colon.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return nicknames, $-prefixed identity fingerprints, or tuples of the
- * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
- * of servers contained in this server's family, or null if the
- * descriptor does not contain a family line.
- *
- * @since 1.0.0
- */
- public List<String> getFamilyEntries();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv4 port summary, or null if the descriptor didn't contain an
- * IPv4 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv4 targets.
- *
- * @since 1.0.0
- */
- public String getDefaultPolicy();
-
- /**
- * Return the port list of the IPv4 exit-policy summary, or null if the
- * descriptor didn't contain an IPv4 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv4 targets.
- *
- * @since 1.0.0
- */
- public String getPortList();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv6 port summary, or null if the descriptor didn't contain an
- * IPv6 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6DefaultPolicy();
-
- /**
- * Return the port list of the IPv6 exit-policy summary, or null if the
- * descriptor didn't contain an IPv6 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6PortList();
-
- /**
- * Return a SHA-1 digest of the server's RSA-1024 identity key, encoded
- * as 27 base64 characters without padding characters, that is only
- * included to prevent collisions between microdescriptors, or null if
- * no such digest is included.
- *
- * @since 1.1.0
- */
- public String getRsa1024Identity();
-
- /**
- * Return a SHA-256 digest of the server's Ed25519 identity key,
- * encoded as 43 base64 characters without padding characters, that is
- * only included to prevent collisions between microdescriptors, or null
- * if no such digest is included.
- *
- * @since 1.1.0
- */
- public String getEd25519Identity();
-}
-
diff --git a/src/org/torproject/descriptor/NetworkStatusEntry.java b/src/org/torproject/descriptor/NetworkStatusEntry.java
deleted file mode 100644
index 43b3175..0000000
--- a/src/org/torproject/descriptor/NetworkStatusEntry.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-
-/**
- * Contains an entry in a network status in the version 2 or 3 directory
- * protocol or in a bridge network status.
- *
- * <p>A network status entry is not a descriptor type of its own but is
- * part of a network status in the version 2 directory protocol
- * ({@link RelayNetworkStatus}), a vote ({@link RelayNetworkStatusVote})
- * or flavored/unflavored consensus (@link RelayNetworkStatusConsensus})
- * in the version 3 directory protocol, or a bridge network status
- * ({@link BridgeNetworkStatus}). Entries in signed directories in the
- * version 1 directory protocol are represented by router status entries
- * ({@link RouterStatusEntry}).</p>
- *
- * @since 1.0.0
- */
-public interface NetworkStatusEntry {
-
- /**
- * Return the raw network status entry bytes.
- *
- * @since 1.0.0
- */
- public byte[] getStatusEntryBytes();
-
- /**
- * Return the server nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the server's identity key, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the SHA-1 digest of the server descriptor, or null if the
- * containing network status does not contain server descriptor
- * references, like a microdesc consensus.
- *
- * @since 1.0.0
- */
- public String getDescriptor();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the server's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this server accepts TLS connections for
- * the main OR protocol.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return the TCP port where this server accepts directory-related HTTP
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return the (possibly empty) set of microdescriptor digests if the
- * containing network status is a vote or microdesc consensus, or null
- * otherwise.
- *
- * @since 1.0.0
- */
- public Set<String> getMicrodescriptorDigests();
-
- /**
- * Return additional IP addresses and TCP ports where this server
- * accepts TLS connections for the main OR protocol, or an empty list if
- * the network status doesn't contain any such additional addresses and
- * ports.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return the relay flags assigned to this server, or null if the
- * status entry didn't contain any relay flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getFlags();
-
- /**
- * Return the Tor software version, or null if the status entry didn't
- * contain version information.
- *
- * @since 1.0.0
- */
- public String getVersion();
-
- /**
- * Return the bandwidth weight of this server or -1 if the status entry
- * didn't contain a bandwidth line.
- *
- * @since 1.0.0
- */
- public long getBandwidth();
-
- /**
- * Return the measured bandwidth or -1 if the status entry either
- * didn't contain bandwidth information or didn't contain an indication
- * that this information is based on measured bandwidth.
- *
- * @since 1.0.0
- */
- public long getMeasured();
-
- /**
- * Return whether the status entry is yet unmeasured by the bandwidth
- * authorities; only included in consensuses using method 17 or higher.
- *
- * @since 1.0.0
- */
- public boolean getUnmeasured();
-
- /**
- * Return the default policy of the port summary, which can be either
- * {@code "accept"} or {@code "reject"}, or null if the status entry
- * didn't contain an exit policy summary.
- *
- * @since 1.0.0
- */
- public String getDefaultPolicy();
-
- /**
- * Return the list of ports or port intervals of the exit port summary,
- * or null if the status entry didn't contain an exit policy summary.
- *
- * @since 1.0.0
- */
- public String getPortList();
-
- /**
- * Return the server's Ed25519 master key, encoded as 43 base64
- * characters without padding characters, "none" if the relay doesn't
- * have an Ed25519 identity, or null if the status entry didn't contain
- * this information or if the status is not a vote.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-}
-
diff --git a/src/org/torproject/descriptor/RelayDirectory.java b/src/org/torproject/descriptor/RelayDirectory.java
deleted file mode 100644
index 8f3e58b..0000000
--- a/src/org/torproject/descriptor/RelayDirectory.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a signed directory in the version 1 directory protocol.
- *
- * <p>Directory authorities in the (long outdated) version 1 of the
- * directory protocol served signed directory documents containing a list
- * of signed server descriptors ({@link ServerDescriptor}) along with
- * short summaries of the status of each server
- * ({@link RouterStatusEntry}).</p>
- *
- * <p>Clients in that version of the directory protocol would fetch this
- * signed directory to get up-to-date information on the state of the
- * network and be certain that the list was attested by a trusted
- * directory authority.</p>
- *
- * <p>Signed directories in the version 1 directory protocol have first
- * been superseded by network status documents in the version 2 directory
- * protocol ({@link RelayNetworkStatus}) and later by network status
- * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
- * directory protocol.</p>
- *
- * @since 1.0.0
- */
-public interface RelayDirectory extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this authority
- * as long-term identity key and to sign network statuses, or null if
- * this key is not included in the descriptor header.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return recommended Tor versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedSoftware();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key.
- *
- * @since 1.0.0
- */
- public String getDirectorySignature();
-
- /**
- * Return router status entries, one for each contained relay.
- *
- * @since 1.0.0
- */
- public List<RouterStatusEntry> getRouterStatusEntries();
-
- /**
- * Return a list of server descriptors contained in the signed
- * directory.
- *
- * @since 1.0.0
- */
- public List<ServerDescriptor> getServerDescriptors();
-
- /**
- * Return a (very likely empty) list of exceptions from parsing the
- * contained server descriptors.
- *
- * @since 1.0.0
- */
- public List<Exception> getServerDescriptorParseExceptions();
-
- /**
- * Return the directory nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the SHA-1 directory digest, encoded as 40 lower-case
- * hexadecimal characters, that the directory authority used to sign the
- * directory.
- *
- * @since 1.0.0
- */
- public String getDirectoryDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java b/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java
deleted file mode 100644
index 73f8438..0000000
--- a/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a relay extra-info descriptor.
- *
- * <p>Relay extra-info descriptors share many contents with sanitized
- * bridge extra-info descriptors ({@link BridgeExtraInfoDescriptor}),
- * which is why they share a common superinterface
- * ({@link ExtraInfoDescriptor}). The main purpose of having two
- * subinterfaces is being able to distinguish descriptor types more
- * easily.</p>
- *
- * @since 1.1.0
- */
-public interface RelayExtraInfoDescriptor extends ExtraInfoDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatus.java b/src/org/torproject/descriptor/RelayNetworkStatus.java
deleted file mode 100644
index db3ddac..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatus.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status document in the version 2 directory protocol.
- *
- * <p>Directory authorities in the (outdated) version 2 of the directory
- * protocol published signed network status documents. Each network
- * status listed, for every relay in the network
- * ({@link NetworkStatusEntry}): a hash of its identity key, a hash of its
- * most recent server descriptor, and a summary of what the authority
- * believed about its status.</p>
- *
- * <p>Clients would download the authorities' network status documents in
- * turn, and believe statements about routers iff they were attested to by
- * more than half of the authorities.</p>
- *
- * <p>Network status documents in the version 2 directory protocol
- * supersede signed directories in the version 1 directory protocol
- * ({@link RelayDirectory}) and have been superseded by network status
- * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
- * directory protocol.</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatus extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 2.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.0.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the descriptor does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirport();
-
- /**
- * Return a SHA-1 digest of the authority's public identity key,
- * encoded as 40 upper-case hexadecimal characters, which is also used
- * to sign network statuses.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this authority
- * as long-term identity key and to sign network statuses.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * authority does not recommend server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * authority does not recommend client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the set of flags that this directory assigns to relays, or
- * null if the status does not assign such flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getDirOptions();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key.
- *
- * @since 1.0.0
- */
- public String getDirectorySignature();
-
- /**
- * Return the SHA-1 status digest, encoded as 40 lower-case hexadecimal
- * characters, that the directory authority used to sign the network
- * status.
- *
- * @since 1.0.0
- */
- public String getStatusDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
deleted file mode 100644
index 15fdaca..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status consensus in the version 3 directory protocol.
- *
- * <p>Directory authorities in the version 3 of the directory protocol
- * periodically generate a view of the current descriptors and status for
- * known relays and send a signed summary of this view to the other
- * authorities ({@link RelayNetworkStatusVote}). The authorities compute
- * the result of this vote and sign a network status consensus containing
- * the result of the vote, which is this document.</p>
- *
- * <p>Clients use consensus documents to find out when their list of
- * relays is out-of-date by looking at the contained network status
- * entries ({@link NetworkStatusEntry}). If it is, they download any
- * missing server descriptors ({@link ServerDescriptor}).</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatusConsensus extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 3 or
- * higher.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the consensus flavor name, which denotes the variant of the
- * original, unflavored consensus, encoded as a string of alphanumeric
- * characters and dashes, or null if this descriptor is the unflavored
- * consensus.
- *
- * @since 1.0.0
- */
- public String getConsensusFlavor();
-
- /**
- * Return the consensus method number of this descriptor, which is the
- * highest consensus method supported by more than 2/3 of voting
- * authorities, or 0 if no consensus method is contained in the
- * descriptor.
- *
- * @since 1.0.0
- */
- public int getConsensusMethod();
-
- /**
- * Return the time in milliseconds since the epoch at which this
- * descriptor became valid.
- *
- * @since 1.0.0
- */
- public long getValidAfterMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which this
- * descriptor is the freshest that is available.
- *
- * @since 1.0.0
- */
- public long getFreshUntilMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which this
- * descriptor was valid.
- *
- * @since 1.0.0
- */
- public long getValidUntilMillis();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect votes from the other authorities when producing the
- * next consensus.
- *
- * @since 1.0.0
- */
- public long getVoteSeconds();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect signatures from the other authorities when producing
- * the next consensus.
- *
- * @since 1.0.0
- */
- public long getDistSeconds();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * consensus does not contain an opinion about server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * consensus does not contain an opinion about client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * Return a list of software packages and their versions together with a
- * URL and one or more digests in the format <code>PackageName Version
- * URL DIGESTS</code> that are known by at least three directory
- * authorities and agreed upon by the majority of directory authorities,
- * or null if the consensus does not contain package information.
- *
- * @since 1.3.0
- */
- public List<String> getPackageLines();
-
- /**
- * Return known relay flags in this descriptor that were contained in
- * enough votes for this consensus to be an authoritative opinion for
- * these relay flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getKnownFlags();
-
- /**
- * Return consensus parameters contained in this descriptor with map
- * keys being case-sensitive parameter identifiers and map values being
- * parameter values, or null if the consensus doesn't contain consensus
- * parameters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getConsensusParams();
-
- /**
- * Return directory source entries for each directory authority that
- * contributed to the consensus, with map keys being SHA-1 digests of
- * the authorities' identity keys in the version 3 directory protocol,
- * encoded as 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirSourceEntry> getDirSourceEntries();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return directory signatures of this consensus, with map keys being
- * SHA-1 digests of the authorities' identity keys in the version 3
- * directory protocol, encoded as 40 upper-case hexadecimal characters.
- *
- * @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by an authority using the same
- * identity key digest and different algorithms.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirectorySignature> getDirectorySignatures();
-
- /**
- * Return the list of signatures contained in this consensus.
- *
- * @since 1.3.0
- */
- public List<DirectorySignature> getSignatures();
-
- /**
- * Return optional weights to be applied to router bandwidths during
- * path selection with map keys being case-sensitive weight identifiers
- * and map values being weight values, or null if the consensus doesn't
- * contain such weights.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBandwidthWeights();
-
- /**
- * Return the SHA-1 digest of this consensus, encoded as 40 upper-case
- * hexadecimal characters that directory authorities use to sign the
- * consensus.
- *
- * @since 1.0.0
- */
- public String getConsensusDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
deleted file mode 100644
index 1f77db6..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status vote in the version 3 directory protocol.
- *
- * <p>Directory authorities in the version 3 of the directory protocol
- * periodically generate a view of the current descriptors and status for
- * known relays and send a signed summary of this view to the other
- * authorities, which is this document. The authorities compute the
- * result of this vote and sign a network status consensus containing the
- * result of the vote ({@link RelayNetworkStatusConsensus}).</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatusVote extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 3 or
- * higher.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the list of consensus method numbers supported by this
- * authority, or null if the descriptor doesn't say so, which would mean
- * that only method 1 is supported.
- *
- * @since 1.0.0
- */
- public List<Integer> getConsensusMethods();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch at which the
- * consensus is supposed to become valid.
- *
- * @since 1.0.0
- */
- public long getValidAfterMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which the
- * consensus is supposed to be the freshest that is available.
- *
- * @since 1.0.0
- */
- public long getFreshUntilMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which the
- * consensus is supposed to be valid.
- *
- * @since 1.0.0
- */
- public long getValidUntilMillis();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect votes from the other authorities when producing the
- * next consensus.
- *
- * @since 1.0.0
- */
- public long getVoteSeconds();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect signatures from the other authorities when producing
- * the next consensus.
- *
- * @since 1.0.0
- */
- public long getDistSeconds();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * authority does not recommend server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * authority does not recommend client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * Return a list of software packages and their versions together with a
- * URL and one or more digests in the format <code>PackageName Version
- * URL DIGESTS</code> that are known by this directory authority, or
- * null if this descriptor does not contain package information.
- *
- * @since 1.3.0
- */
- public List<String> getPackageLines();
-
- /**
- * Return known relay flags by this authority.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getKnownFlags();
-
- /**
- * Return the minimum uptime in seconds that this authority requires
- * for assigning the Stable flag, or -1 if the authority doesn't report
- * this value.
- *
- * @since 1.0.0
- */
- public long getStableUptime();
-
- /**
- * Return the minimum MTBF (mean time between failure) that this
- * authority requires for assigning the Stable flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getStableMtbf();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Fast flag, or -1 if the authority doesn't report this
- * value.
- *
- * @since 1.0.0
- */
- public long getFastBandwidth();
-
- /**
- * Return the minimum WFU (weighted fractional uptime) in percent that
- * this authority requires for assigning the Guard flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public double getGuardWfu();
-
- /**
- * Return the minimum weighted time in seconds that this authority
- * needs to know about a relay before assigning the Guard flag, or -1 if
- * the authority doesn't report this information.
- *
- * @since 1.0.0
- */
- public long getGuardTk();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getGuardBandwidthIncludingExits();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can not be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getGuardBandwidthExcludingExits();
-
- /**
- * Return 1 if the authority has measured enough MTBF info to use the
- * MTBF requirement instead of the uptime requirement for assigning the
- * Stable flag, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.0.0
- */
- public int getEnoughMtbfInfo();
-
- /**
- * Return 1 if the authority has enough measured bandwidths that it'll
- * ignore the advertised bandwidth claims of routers without measured
- * bandwidth, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getIgnoringAdvertisedBws();
-
- /**
- * Return consensus parameters contained in this descriptor with map
- * keys being case-sensitive parameter identifiers and map values being
- * parameter values, or null if the authority doesn't include consensus
- * parameters in its vote.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getConsensusParams();
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.2.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the descriptor does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirport();
-
- /**
- * Return the TCP port where this authority accepts TLS connections for
- * the main OR protocol, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getOrport();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters, or null if no contact information is included
- * in the descriptor.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the version of the directory key certificate used by this
- * authority, which must be 3 or higher.
- *
- * @since 1.0.0
- */
- public int getDirKeyCertificateVersion();
-
- /**
- * Return the SHA-1 digest for an obsolete authority identity key still
- * used by this authority to keep older clients working, or null if this
- * authority does not use such a key.
- *
- * @since 1.0.0
- */
- public String getLegacyDirKey();
-
- /**
- * Return the authority's identity key in PEM format.
- *
- * @since 1.2.0
- */
- public String getDirIdentityKey();
-
- /**
- * Return the time in milliseconds since the epoch when the authority's
- * signing key and corresponding key certificate were generated.
- *
- * @since 1.0.0
- */
- public long getDirKeyPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch after which the
- * authority's signing key is no longer valid.
- *
- * @since 1.0.0
- */
- public long getDirKeyExpiresMillis();
-
- /**
- * Return the authority's signing key in PEM format.
- *
- * @since 1.2.0
- */
- public String getDirSigningKey();
-
- /**
- * Return the SHA-1 digest of the authority's signing key, encoded as
- * 40 upper-case hexadecimal characters, or null if this digest cannot
- * be obtained from the directory signature.
- *
- * @deprecated Removed in order to be more explicit that authorities may
- * use different digest algorithms than "sha1"; see
- * {@link #getSignatures()} and
- * {@link DirectorySignature#getSigningKeyDigest()} for
- * alternatives.
- *
- * @since 1.0.0
- */
- public String getSigningKeyDigest();
-
- /**
- * Return the signature of the authority's identity key made using the
- * authority's signing key, or null if the vote does not contain such a
- * signature.
- *
- * @since 1.2.0
- */
- public String getDirKeyCrosscert();
-
- /**
- * Return the certificate signature from the initial item
- * "dir-key-certificate-version" until the final item
- * "dir-key-certification", signed with the authority identity key.
- *
- * @since 1.2.0
- */
- public String getDirKeyCertification();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return the directory signature of this vote, with the single map key
- * being the SHA-1 digest of the authority's identity key in the version
- * 3 directory protocol, encoded as 40 upper-case hexadecimal
- * characters.
- *
- * @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by the authority using the same
- * identity key digest and different algorithms.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirectorySignature> getDirectorySignatures();
-
- /**
- * Return a list of signatures contained in this vote, which is
- * typically a single signature made by the authority but which may also
- * be more than one signature made with different keys or algorithms.
- *
- * @since 1.3.0
- */
- public List<DirectorySignature> getSignatures();
-}
-
diff --git a/src/org/torproject/descriptor/RelayServerDescriptor.java b/src/org/torproject/descriptor/RelayServerDescriptor.java
deleted file mode 100644
index 6ef3140..0000000
--- a/src/org/torproject/descriptor/RelayServerDescriptor.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a relay server descriptor.
- *
- * <p>Relay server descriptors share many contents with sanitized bridge
- * server descriptors ({@link BridgeServerDescriptor}), which is why they
- * share a common superinterface ({@link ServerDescriptor}). The main
- * purpose of having two subinterfaces is being able to distinguish
- * descriptor types more easily.</p>
- *
- * @since 1.1.0
- */
-public interface RelayServerDescriptor extends ServerDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/RouterStatusEntry.java b/src/org/torproject/descriptor/RouterStatusEntry.java
deleted file mode 100644
index f9a56db..0000000
--- a/src/org/torproject/descriptor/RouterStatusEntry.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a router status entry contained in a signed directory in the
- * version 1 directory protocol.
- *
- * <p>Directory authorities in the (long outdated) version 1 of the
- * directory protocol included router status entries with short summaries
- * of the status of each server in the signed directories they produced
- * ({@link RelayDirectory}). These entries contained references to server
- * descriptors published by relays together with the authorities' opinion
- * on whether relays were verified and live.</p>
- *
- * @since 1.0.0
- */
-public interface RouterStatusEntry {
-
- /**
- * Return the relay nickname consisting of 1 to 19 alphanumeric
- * characters, or null if the relay is unverified.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the relay's identity key, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return whether the relay is verified.
- *
- * @since 1.0.0
- */
- public boolean isVerified();
-
- /**
- * Return whether the relay is live.
- *
- * @since 1.0.0
- */
- public boolean isLive();
-}
-
diff --git a/src/org/torproject/descriptor/ServerDescriptor.java b/src/org/torproject/descriptor/ServerDescriptor.java
deleted file mode 100644
index d1af421..0000000
--- a/src/org/torproject/descriptor/ServerDescriptor.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a relay or sanitized bridge server descriptor.
- *
- * <p>Relays publish server descriptors to the directory authorities to
- * register in the network. Server descriptors contain information about
- * the capabilities of a server, like their exit policy, that clients use
- * to select servers for their circuits (along with information provided
- * by directory authorities on reachability, stability, and capacity of
- * servers). Server descriptors also contain network addresses and
- * cryptographic material that clients use to build circuits.</p>
- *
- * <p>Prior to the introduction of microdescriptors
- * ({@link Microdescriptor}), the directory authorities included
- * cryptographic digests of server descriptors in network statuses
- * ({@link RelayNetworkStatusConsensus}) and clients downloaded all
- * referenced server descriptors. Nowadays, the directory authorities
- * derive microdescriptors from server descriptors and reference those
- * in network statuses, and clients only download microdescriptors instead
- * of server descriptors.</p>
- *
- * <p>Bridges publish server descriptors to the bridge directory
- * authority, also to announce themselves in the network. The bridge
- * directory authority compiles a list of available bridges
- * ({@link BridgeNetworkStatus}) for the bridge distribution service
- * BridgeDB. There are no microdescriptors for bridges, so that bridge
- * clients still rely on downloading bridge server descriptors directly
- * from the bridge they're connecting to.</p>
- *
- * <p>It's worth noting that all contents of server descriptors are
- * written and signed by relays and bridges without a third party
- * verifying their correctness. The (bridge) directory authorities may
- * decide to exclude dishonest servers from the network statuses they
- * produce, but that wouldn't be reflected in server descriptors.</p>
- *
- * @since 1.0.0
- */
-public interface ServerDescriptor extends Descriptor {
-
- /**
- * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
- * descriptors) or upper-case (bridge descriptors) hexadecimal
- * characters, that is used to reference this descriptor from a network
- * status descriptor.
- *
- * @since 1.0.0
- */
- public String getServerDescriptorDigest();
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that may be used to reference
- * this server descriptor from a network status descriptor.
- *
- * @since 1.1.0
- */
- public String getServerDescriptorDigestSha256();
-
- /**
- * Return the server's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the server's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this server accepts TLS connections for
- * the main OR protocol, or 0 if the server does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return the TCP port where this server accepts SOCKS connections,
- * which is deprecated and should always be 0.
- *
- * @since 1.0.0
- */
- public int getSocksPort();
-
- /**
- * Return the TCP port where this server accepts directory-related HTTP
- * connections, or 0 if the server does not accept such connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return IP addresses and TCP ports where this server accepts TLS
- * connections for the main OR protocol, or an empty list if the server
- * does not support additional addresses or ports; entries are given in
- * the order as they are listed in the descriptor; IPv4 addresses are
- * given in dotted-quad format, IPv6 addresses use the colon-separated
- * hexadecimal format surrounded by square brackets, and TCP ports are
- * separated from the IP address using a colon.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return the average bandwidth in bytes per second that the server is
- * willing to sustain over long periods.
- *
- * @since 1.0.0
- */
- public int getBandwidthRate();
-
- /**
- * Return the burst bandwidth in bytes per second that the server is
- * willing to sustain in very short intervals.
- *
- * @since 1.0.0
- */
- public int getBandwidthBurst();
-
- /**
- * Return the observed bandwidth in bytes per second as an estimate of
- * the capacity that the server can handle, or -1 if the descriptor
- * doesn't contain an observed bandwidth value (which is the case for
- * Tor 0.0.8 or older).
- *
- * @since 1.0.0
- */
- public int getBandwidthObserved();
-
- /**
- * Return a human-readable string describing the Tor software version
- * and the operating system of this server, which may contain non-ASCII
- * characters, typically written as {@code "Tor $version on $system"},
- * or null if this descriptor does not contain a platform line.
- *
- * @since 1.0.0
- */
- public String getPlatform();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * and the corresponding extra-info descriptor were generated.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return a SHA-1 digest of the server's public identity key, encoded
- * as 40 upper-case hexadecimal characters (without spaces after every 4
- * characters as opposed to the encoding in the descriptor), that is
- * typically used to uniquely identify the server, or null if this
- * descriptor does not contain a fingerprint line.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return whether the server was hibernating when this descriptor was
- * published and should not be used to build circuits.
- *
- * @since 1.0.0
- */
- public boolean isHibernating();
-
- /**
- * Return the number of seconds that the server process has been
- * running (which might even be negative in a few descriptors due to a
- * bug that was fixed in Tor 0.1.2.7-alpha), or null if the descriptor
- * does not contain an uptime line.
- *
- * @since 1.0.0
- */
- public Long getUptime();
-
- /**
- * Return the RSA-1024 public key in PEM format used to encrypt CREATE
- * cells for this server, or null if the descriptor doesn't contain an
- * onion key (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getOnionKey();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this server as
- * long-term identity key, or null if the descriptor doesn't contain a
- * signing key (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getSigningKey();
-
- /**
- * Return the server's exit policy consisting of one or more accept or
- * reject rules that the server follows when deciding whether to allow a
- * new stream to a given IP address and TCP port.
- *
- * @since 1.0.0
- */
- public List<String> getExitPolicyLines();
-
- /**
- * Return the RSA-1024 signature of the PKCS1-padded descriptor digest,
- * taken from the beginning of the router line through the newline after
- * the router-signature line, or null if the descriptor doesn't contain
- * a signature (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getRouterSignature();
-
- /**
- * Return the contact information for this server, which may contain
- * non-ASCII characters, or null if no contact information is included
- * in the descriptor.
- *
- * @since 1.0.0
- */
- public String getContact();
-
- /**
- * Return nicknames, $-prefixed identity fingerprints, or tuples of the
- * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
- * of servers contained in this server's family, or null if the
- * descriptor does not contain a family line.
- *
- * @since 1.0.0
- */
- public List<String> getFamilyEntries();
-
- /**
- * Return the server's history of read bytes, or null if the descriptor
- * does not contain a bandwidth history; current Tor versions include
- * bandwidth histories in their extra-info descriptors
- * ({@link ExtraInfoDescriptor#getReadHistory()}), not in their server
- * descriptors.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getReadHistory();
-
- /**
- * Return the server's history of written bytes, or null if the
- * descriptor does not contain a bandwidth history; current Tor versions
- * include bandwidth histories in their extra-info descriptors
- * ({@link ExtraInfoDescriptor#getWriteHistory()}), not in their server
- * descriptors.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getWriteHistory();
-
- /**
- * Return true if the server uses the enhanced DNS logic, or false if
- * doesn't use it or doesn't include an eventdns line in its
- * descriptor; current Tor versions should be presumed to have the evdns
- * backend.
- *
- * @since 1.0.0
- */
- public boolean getUsesEnhancedDnsLogic();
-
- /**
- * Return whether this server is a directory cache that provides
- * extra-info descriptors.
- *
- * @since 1.0.0
- */
- public boolean getCachesExtraInfo();
-
- /**
- * Return the SHA-1 digest of the server's extra-info descriptor,
- * encoded as 40 upper-case hexadecimal characters, or null if the
- * server did not upload a corresponding extra-info descriptor.
- *
- * @since 1.0.0
- */
- public String getExtraInfoDigest();
-
- /**
- * Return the SHA-256 digest of the server's extra-info descriptor,
- * encoded as 43 base64 characters without padding characters, or null
- * if the server either did not upload a corresponding extra-info
- * descriptor or did not refer to it using a SHA-256 digest.
- *
- * @since 1.1.0
- */
- public String getExtraInfoDigestSha256();
-
- /**
- * Return the list of hidden service descriptor version numbers that
- * this server stores and serves, or null if it doesn't store and serve
- * any hidden service descriptors.
- *
- * @since 1.0.0
- */
- public List<Integer> getHiddenServiceDirVersions();
-
- /**
- * Return the list of link protocol versions that this server
- * supports.
- *
- * @since 1.0.0
- */
- public List<Integer> getLinkProtocolVersions();
-
- /**
- * Return the list of circuit protocol versions that this server
- * supports.
- *
- * @since 1.0.0
- */
- public List<Integer> getCircuitProtocolVersions();
-
- /**
- * Return whether this server allows single-hop circuits to make exit
- * connections.
- *
- * @since 1.0.0
- */
- public boolean getAllowSingleHopExits();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv6 port summary, or null if the descriptor didn't contain an
- * IPv6 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6DefaultPolicy();
-
- /**
- * Return the port list of the IPv6 exit-policy summary, or null if the
- * descriptor didn't contain an IPv6 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6PortList();
-
- /**
- * Return the curve25519 public key, encoded as 43 base64 characters
- * without padding characters, that is used for the ntor circuit
- * extended handshake, or null if the descriptor didn't contain an
- * ntor-onion-key line. */
- public String getNtorOnionKey();
-
- /**
- * Return the Ed25519 certificate in PEM format, or null if the
- * descriptor doesn't contain one.
- *
- * @since 1.1.0
- */
- public String getIdentityEd25519();
-
- /**
- * Return the Ed25519 master key, encoded as 43 base64 characters
- * without padding characters, which was either parsed from the optional
- * {@code "master-key-ed25519"} line or derived from the (likewise
- * optional) Ed25519 certificate following the
- * {@code "identity-ed25519"} line, or null if the descriptor contains
- * neither Ed25519 master key nor Ed25519 certificate.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-
- /**
- * Return the Ed25519 signature of the SHA-256 digest of the entire
- * descriptor, encoded as 86 base64 characters without padding
- * characters, from the first character up to and including the first
- * space after the {@code "router-sig-ed25519"} string, prefixed with
- * the string {@code "Tor router descriptor signature v1"}.
- *
- * @since 1.1.0
- */
- public String getRouterSignatureEd25519();
-
- /**
- * Return an RSA-1024 signature in PEM format, generated using the
- * server's onion key, that proves that the party creating the
- * descriptor had control over the private key corresponding to the
- * onion key, or null if the descriptor does not contain such a
- * signature.
- *
- * @since 1.1.0
- */
- public String getOnionKeyCrosscert();
-
- /**
- * Return an Ed25519 signature in PEM format, generated using the
- * server's ntor onion key, that proves that the party creating the
- * descriptor had control over the private key corresponding to the ntor
- * onion key, or null if the descriptor does not contain such a
- * signature.
- *
- * @since 1.1.0
- */
- public String getNtorOnionKeyCrosscert();
-
- /**
- * Return the sign of the Ed25519 public key corresponding to the ntor
- * onion key as 0 or 1, or -1 if the descriptor does not contain this
- * information.
- *
- * @since 1.1.0
- */
- public int getNtorOnionKeyCrosscertSign();
-
- /**
- * Return whether the server accepts "tunneled" directory requests using
- * a BEGIN_DIR cell over the server's OR port.
- *
- * @since 1.3.0
- */
- public boolean getTunnelledDirServer();
-}
-
diff --git a/src/org/torproject/descriptor/TorperfResult.java b/src/org/torproject/descriptor/TorperfResult.java
deleted file mode 100644
index 188200b..0000000
--- a/src/org/torproject/descriptor/TorperfResult.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-
-/**
- * Contains performance measurement results from making simple HTTP
- * requests over the Tor network.
- *
- * <p>The performance measurement service Torperf publishes performance
- * data from making simple HTTP requests over the Tor network. Torperf
- * uses a trivial SOCKS client to download files of various sizes over the
- * Tor network and notes how long substeps take.</p>
- *
- * @since 1.0.0
- */
-public interface TorperfResult extends Descriptor {
-
- /**
- * Return all unrecognized keys together with their values, or null if
- * all keys were recognized.
- *
- * @since 1.2.0
- */
- public SortedMap<String, String> getUnrecognizedKeys();
-
- /**
- * Return the configured name of the data source.
- *
- * @since 1.0.0
- */
- public String getSource();
-
- /**
- * Return the configured file size in bytes.
- *
- * @since 1.0.0
- */
- public int getFileSize();
-
- /**
- * Return the time in milliseconds since the epoch when the connection
- * process started.
- *
- * @since 1.0.0
- */
- public long getStartMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the socket was
- * created.
- *
- * @since 1.0.0
- */
- public long getSocketMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the socket was
- * connected.
- *
- * @since 1.0.0
- */
- public long getConnectMillis();
-
- /**
- * Return the time in milliseconds since the epoch when SOCKS 5
- * authentication methods have been negotiated.
- *
- * @since 1.0.0
- */
- public long getNegotiateMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the SOCKS
- * request was sent.
- *
- * @since 1.0.0
- */
- public long getRequestMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the SOCKS
- * response was received.
- *
- * @since 1.0.0
- */
- public long getResponseMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the HTTP
- * request was written.
- *
- * @since 1.0.0
- */
- public long getDataRequestMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the first
- * response was received.
- *
- * @since 1.0.0
- */
- public long getDataResponseMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the payload was
- * complete.
- *
- * @since 1.0.0
- */
- public long getDataCompleteMillis();
-
- /**
- * Return the total number of bytes written.
- *
- * @since 1.0.0
- */
- public int getWriteBytes();
-
- /**
- * Return the total number of bytes read.
- *
- * @since 1.0.0
- */
- public int getReadBytes();
-
- /**
- * Return whether the request timed out (as opposed to failing), or
- * null if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public Boolean didTimeout();
-
- /**
- * Return the times in milliseconds since the epoch when {@code x%} of
- * expected bytes were read for {@code 0 <= x <= 100}, or null if the
- * torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public SortedMap<Integer, Long> getDataPercentiles();
-
- /**
- * Return the time in milliseconds since the epoch when the circuit was
- * launched, or -1 if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public long getLaunchMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the circuit was
- * used, or -1 if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public long getUsedAtMillis();
-
- /**
- * Return a list of fingerprints of the relays in the circuit, or null
- * if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public List<String> getPath();
-
- /**
- * Return a list of times in milliseconds since the epoch when circuit
- * hops were built, or null if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public List<Long> getBuildTimes();
-
- /**
- * Return the circuit build timeout that the Tor client used when
- * building this circuit, or -1 if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public long getTimeout();
-
- /**
- * Return the circuit build time quantile that the Tor client uses to
- * determine its circuit-build timeout, or -1 if the torperf line
- * didn't contain that information.
- *
- * @since 1.0.0
- */
- public double getQuantile();
-
- /**
- * Return the identifier of the circuit used for this measurement, or
- * -1 if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public int getCircId();
-
- /**
- * Return the identifier of the stream used for this measurement, or -1
- * if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public int getUsedBy();
-}
-
diff --git a/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
deleted file mode 100644
index 295e0a4..0000000
--- a/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.BandwidthHistory;
-
-public class BandwidthHistoryImpl implements BandwidthHistory {
-
- protected BandwidthHistoryImpl(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- boolean isValid = false;
- this.line = line;
- if (partsNoOpt.length == 5 || partsNoOpt.length == 6) {
- try {
- this.historyEndMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- if (partsNoOpt[3].startsWith("(") &&
- partsNoOpt[4].startsWith("s)")) {
- this.intervalLength = Long.parseLong(partsNoOpt[3].
- substring(1));
- if (this.intervalLength <= 0L) {
- throw new DescriptorParseException("Only positive interval "
- + "lengths are allowed in line '" + line + "'.");
- }
- String[] values = null;
- if (partsNoOpt.length == 5 &&
- partsNoOpt[4].equals("s)")) {
- /* There are no bandwidth values to parse. */
- isValid = true;
- } else if (partsNoOpt.length == 6) {
- /* There are bandwidth values to parse. */
- values = partsNoOpt[5].split(",", -1);
- } else if (partsNoOpt[4].length() > 2) {
- /* There are bandwidth values to parse, but there is no space
- * between "s)" and "0,0,0,0". Very old Tor versions around
- * Tor 0.0.8 wrote such history lines, and even though
- * dir-spec.txt implies a space here, the old format isn't
- * totally broken. Let's pretend there's a space. */
- values = partsNoOpt[4].substring(2).split(",", -1);
- }
- if (values != null) {
- this.bandwidthValues = new long[values.length];
- for (int i = values.length - 1; i >= 0; i--) {
- long bandwidthValue = Long.parseLong(values[i]);
- if (bandwidthValue < 0L) {
- throw new DescriptorParseException("Negative bandwidth "
- + "values are not allowed in line '" + line + "'.");
- }
- this.bandwidthValues[i] = bandwidthValue;
- }
- isValid = true;
- }
- }
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Invalid bandwidth-history line "
- + "'" + line + "'.");
- }
- }
-
- private String line;
- @Override
- public String getLine() {
- return this.line;
- }
-
- private long historyEndMillis;
- @Override
- public long getHistoryEndMillis() {
- return this.historyEndMillis;
- }
-
- private long intervalLength;
- @Override
- public long getIntervalLength() {
- return this.intervalLength;
- }
-
- private long[] bandwidthValues;
- @Override
- public SortedMap<Long, Long> getBandwidthValues() {
- SortedMap<Long, Long> result = new TreeMap<>();
- if (this.bandwidthValues != null) {
- long endMillis = this.historyEndMillis;
- for (int i = this.bandwidthValues.length - 1; i >= 0; i--) {
- result.put(endMillis, bandwidthValues[i]);
- endMillis -= this.intervalLength * 1000L;
- }
- }
- return result;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java
deleted file mode 100644
index 66426d8..0000000
--- a/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-
-/* Provide an iterator for a queue of objects and block when there are
- * currently no objects in the queue. Allow the producer to signal that
- * there won't be further objects and unblock any waiting consumers. */
-public class BlockingIteratorImpl<T> implements Iterator<T> {
-
- /* Queue containing produced elemnts waiting for consumers. */
- private Queue<T> queue = new LinkedList<>();
-
- /* Maximum number of elements in queue. */
- private int maxQueueSize = 100;
-
- /* Restrict object construction to the impl package. */
- protected BlockingIteratorImpl() {
- }
-
- /* Create instance with maximum queue size. */
- protected BlockingIteratorImpl(int maxQueueSize) {
- this.maxQueueSize = maxQueueSize;
- }
-
- /* Add an object to the queue if there's still room. */
- protected synchronized void add(T object) {
- if (this.outOfDescriptors) {
- throw new IllegalStateException("Internal error: Adding results to "
- + "descriptor queue not allowed after sending end-of-stream "
- + "object.");
- }
- while (this.queue.size() >= this.maxQueueSize) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- this.queue.offer(object);
- notifyAll();
- }
-
- /* Signalize that there won't be any further objects to be enqueued. */
- private boolean outOfDescriptors = false;
- protected synchronized void setOutOfDescriptors() {
- if (this.outOfDescriptors) {
- throw new IllegalStateException("Internal error: Sending "
- + "end-of-stream object only permitted once.");
- }
- this.outOfDescriptors = true;
- notifyAll();
- }
-
- /* Return whether there are more objects. Block if there are currently
- * no objects, but the producer hasn't signalized that there won't be
- * further objects. */
- @Override
- public synchronized boolean hasNext() {
- while (!this.outOfDescriptors && this.queue.isEmpty()) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- return this.queue.peek() != null;
- }
-
- /* Return the next object in the queue or throw an exception when there
- * are no further objects. Block if there are currently no objects, but
- * the producer hasn't signalized that there won't be further
- * objects. */
- @Override
- public synchronized T next() {
- while (!this.outOfDescriptors && this.queue.isEmpty()) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- if (this.queue.peek() == null) {
- throw new NoSuchElementException();
- }
- notifyAll();
- return this.queue.remove();
- }
-
- /* Don't support explicitly removing objects. They are removed
- * anyway. */
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
deleted file mode 100644
index 15d40d8..0000000
--- a/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.BridgeExtraInfoDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-
-public class BridgeExtraInfoDescriptorImpl
- extends ExtraInfoDescriptorImpl implements BridgeExtraInfoDescriptor {
-
- protected static List<ExtraInfoDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "extra-info ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ExtraInfoDescriptor parsedDescriptor =
- new BridgeExtraInfoDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgeExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
deleted file mode 100644
index bf3804d..0000000
--- a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedMap;
-import java.util.TimeZone;
-
-import org.torproject.descriptor.BridgeNetworkStatus;
-
-/* Contains a bridge network status. */
-public class BridgeNetworkStatusImpl extends NetworkStatusImpl
- implements BridgeNetworkStatus {
-
- protected BridgeNetworkStatusImpl(byte[] statusBytes,
- String fileName, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(statusBytes, failUnrecognizedDescriptorLines, false, false);
- this.setPublishedMillisFromFileName(fileName);
- }
-
- private void setPublishedMillisFromFileName(String fileName)
- throws DescriptorParseException {
- if (this.publishedMillis != 0L) {
- /* We already learned the publication timestamp from parsing the
- * "published" line. */
- return;
- }
- if (fileName.length() ==
- "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D".
- length()) {
- String publishedString = fileName.substring(0,
- "yyyyMMdd-HHmmss".length());
- try {
- SimpleDateFormat fileNameFormat = new SimpleDateFormat(
- "yyyyMMdd-HHmmss");
- fileNameFormat.setLenient(false);
- fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- this.publishedMillis = fileNameFormat.parse(publishedString).
- getTime();
- } catch (ParseException e) {
- }
- }
- if (this.publishedMillis == 0L) {
- throw new DescriptorParseException("Unrecognized bridge network "
- + "status file name '" + fileName + "'.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- /* Initialize flag-thresholds values here for the case that the status
- * doesn't contain those values. Initializing them in the constructor
- * or when declaring variables wouldn't work, because those parts are
- * evaluated later and would overwrite everything we parse here. */
- this.stableUptime = -1L;
- this.stableMtbf = -1L;
- this.fastBandwidth = -1L;
- this.guardWfu = -1.0;
- this.guardTk = -1L;
- this.guardBandwidthIncludingExits = -1L;
- this.guardBandwidthExcludingExits = -1L;
- this.enoughMtbfInfo = -1;
- this.ignoringAdvertisedBws = -1;
-
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in bridge network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFlagThresholdsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No flag thresholds in line '"
- + line + "'.");
- }
- SortedMap<String, String> flagThresholds =
- ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
- try {
- for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
- switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
- }
- }
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
-
- protected void parseDirSource(byte[] dirSourceBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory source expected in "
- + "bridge network status.");
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory footer expected in "
- + "bridge network status.");
- }
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory signature expected "
- + "in bridge network status.");
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long stableUptime;
- @Override
- public long getStableUptime() {
- return this.stableUptime;
- }
-
- private long stableMtbf;
- @Override
- public long getStableMtbf() {
- return this.stableMtbf;
- }
-
- private long fastBandwidth;
- @Override
- public long getFastBandwidth() {
- return this.fastBandwidth;
- }
-
- private double guardWfu;
- @Override
- public double getGuardWfu() {
- return this.guardWfu;
- }
-
- private long guardTk;
- @Override
- public long getGuardTk() {
- return this.guardTk;
- }
-
- private long guardBandwidthIncludingExits;
- @Override
- public long getGuardBandwidthIncludingExits() {
- return this.guardBandwidthIncludingExits;
- }
-
- private long guardBandwidthExcludingExits;
- @Override
- public long getGuardBandwidthExcludingExits() {
- return this.guardBandwidthExcludingExits;
- }
-
- private int enoughMtbfInfo;
- @Override
- public int getEnoughMtbfInfo() {
- return this.enoughMtbfInfo;
- }
-
- private int ignoringAdvertisedBws;
- @Override
- public int getIgnoringAdvertisedBws() {
- return this.ignoringAdvertisedBws;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
deleted file mode 100644
index 99578e8..0000000
--- a/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.BridgePoolAssignment;
-
-/* TODO Write a test class. */
-public class BridgePoolAssignmentImpl extends DescriptorImpl
- implements BridgePoolAssignment {
-
- protected static List<BridgePoolAssignment> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<BridgePoolAssignment> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "bridge-pool-assignment ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- BridgePoolAssignment parsedDescriptor =
- new BridgePoolAssignmentImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgePoolAssignmentImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
- new String[] { "bridge-pool-assignment" }));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- this.checkFirstKeyword("bridge-pool-assignment");
- this.clearParsedKeywords();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("bridge-pool-assignment ")) {
- this.parseBridgePoolAssignmentLine(line);
- } else {
- this.parseBridgeLine(line);
- }
- }
- }
-
- private void parseBridgePoolAssignmentLine(String line)
- throws DescriptorParseException {
- String[] parts = line.split("[ \t]+");
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in bridge pool assignment.");
- }
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseBridgeLine(String line)
- throws DescriptorParseException {
- String[] parts = line.split("[ \t]+");
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in bridge pool assignment.");
- }
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[0]);
- String poolAndDetails = line.substring(line.indexOf(" ") + 1);
- this.entries.put(fingerprint, poolAndDetails);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private SortedMap<String, String> entries = new TreeMap<>();
- @Override
- public SortedMap<String, String> getEntries() {
- return new TreeMap<>(this.entries);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
deleted file mode 100644
index eb2b933..0000000
--- a/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.BridgeServerDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ServerDescriptor;
-
-public class BridgeServerDescriptorImpl extends ServerDescriptorImpl
- implements BridgeServerDescriptor {
-
- protected static List<ServerDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "router ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ServerDescriptor parsedDescriptor =
- new BridgeServerDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgeServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
deleted file mode 100644
index 1a030ef..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.Stack;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.GZIPInputStream;
-
-import org.torproject.descriptor.DescriptorCollector;
-
-public class DescriptorCollectorImpl implements DescriptorCollector {
-
- @Override
- public void collectDescriptors(String collecTorBaseUrl,
- String[] remoteDirectories, long minLastModified,
- File localDirectory, boolean deleteExtraneousLocalFiles) {
- collecTorBaseUrl = collecTorBaseUrl.endsWith("/")
- ? collecTorBaseUrl.substring(0, collecTorBaseUrl.length() - 1)
- : collecTorBaseUrl;
- if (minLastModified < 0) {
- throw new IllegalArgumentException("A negative minimum "
- + "last-modified time is not permitted.");
- }
- if (localDirectory.exists() && !localDirectory.isDirectory()) {
- throw new IllegalArgumentException("Local directory already exists "
- + "and is not a directory.");
- }
- SortedMap<String, Long> localFiles =
- this.statLocalDirectory(localDirectory);
- SortedMap<String, String> fetchedDirectoryListings =
- this.fetchRemoteDirectories(collecTorBaseUrl, remoteDirectories);
- SortedSet<String> parsedDirectories = new TreeSet<>();
- SortedMap<String, Long> remoteFiles = new TreeMap<>();
- for (Map.Entry<String, String> e :
- fetchedDirectoryListings.entrySet()) {
- String remoteDirectory = e.getKey();
- String directoryListing = e.getValue();
- SortedMap<String, Long> parsedRemoteFiles =
- this.parseDirectoryListing(remoteDirectory, directoryListing);
- if (parsedRemoteFiles == null) {
- continue;
- }
- parsedDirectories.add(remoteDirectory);
- remoteFiles.putAll(parsedRemoteFiles);
- }
- this.fetchRemoteFiles(collecTorBaseUrl, remoteFiles, minLastModified,
- localDirectory, localFiles);
- if (deleteExtraneousLocalFiles) {
- this.deleteExtraneousLocalFiles(parsedDirectories, remoteFiles,
- localDirectory, localFiles);
- }
- }
-
- SortedMap<String, Long> statLocalDirectory(
- File localDirectory) {
- SortedMap<String, Long> localFiles = new TreeMap<>();
- if (!localDirectory.exists()) {
- return localFiles;
- }
- Stack<File> files = new Stack<>();
- files.add(localDirectory);
- while (!files.isEmpty()) {
- File file = files.pop();
- if (file.isDirectory()) {
- files.addAll(Arrays.asList(file.listFiles()));
- } else {
- String localPath = file.getPath().substring(
- localDirectory.getPath().length());
- localFiles.put(localPath, file.lastModified());
- }
- }
- return localFiles;
- }
-
- SortedMap<String, String> fetchRemoteDirectories(
- String collecTorBaseUrl, String[] remoteDirectories) {
- SortedMap<String, String> fetchedDirectoryListings = new TreeMap<>();
- for (String remoteDirectory : remoteDirectories) {
- String remoteDirectoryWithSlashAtBeginAndEnd =
- (remoteDirectory.startsWith("/") ? "" : "/") + remoteDirectory
- + (remoteDirectory.endsWith("/") ? "" : "/");
- String directoryUrl = collecTorBaseUrl
- + remoteDirectoryWithSlashAtBeginAndEnd;
- String directoryListing = this.fetchRemoteDirectory(directoryUrl);
- if (directoryListing.length() > 0) {
- fetchedDirectoryListings.put(
- remoteDirectoryWithSlashAtBeginAndEnd, directoryListing);
- }
- }
- return fetchedDirectoryListings;
- }
-
- String fetchRemoteDirectory(String url) {
- StringBuilder sb = new StringBuilder();
- HttpURLConnection huc = null;
- try {
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setRequestMethod("GET");
- huc.connect();
- int responseCode = huc.getResponseCode();
- if (responseCode == 200) {
- BufferedReader br = new BufferedReader(new InputStreamReader(
- huc.getInputStream()));
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line).append("\n");
- }
- br.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- if (huc != null) {
- huc.disconnect();
- }
- return "";
- }
- return sb.toString();
- }
-
- final Pattern DIRECTORY_LISTING_LINE_PATTERN =
- Pattern.compile(".* href=\"([^\"/]+)\"" /* filename */
- + ".*>(\\d{2}-\\w{3}-\\d{4} \\d{2}:\\d{2})\\s*<.*"); /* dateTime */
-
- SortedMap<String, Long> parseDirectoryListing(
- String remoteDirectory, String directoryListing) {
- SortedMap<String, Long> remoteFiles = new TreeMap<>();
- DateFormat dateTimeFormat = ParseHelper.getDateFormat(
- "dd-MMM-yyyy HH:mm");
- try {
- Scanner s = new Scanner(directoryListing);
- s.useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- Matcher matcher = DIRECTORY_LISTING_LINE_PATTERN.matcher(line);
- if (matcher.matches()) {
- String filename = matcher.group(1);
- long lastModifiedMillis = dateTimeFormat.parse(
- matcher.group(2)).getTime();
- remoteFiles.put(remoteDirectory + filename, lastModifiedMillis);
- }
- }
- s.close();
- } catch (ParseException e) {
- e.printStackTrace();
- return null;
- }
- return remoteFiles;
- }
-
- void fetchRemoteFiles(String collecTorBaseUrl,
- SortedMap<String, Long> remoteFiles, long minLastModified,
- File localDirectory, SortedMap<String, Long> localFiles) {
- for (Map.Entry<String, Long> e : remoteFiles.entrySet()) {
- String filename = e.getKey();
- long lastModifiedMillis = e.getValue();
- if (lastModifiedMillis < minLastModified ||
- (localFiles.containsKey(filename) &&
- localFiles.get(filename) >= lastModifiedMillis)) {
- continue;
- }
- String url = collecTorBaseUrl + filename;
- File destinationFile = new File(localDirectory.getPath()
- + filename);
- this.fetchRemoteFile(url, destinationFile, lastModifiedMillis);
- }
- }
-
- void fetchRemoteFile(String url, File destinationFile,
- long lastModifiedMillis) {
- HttpURLConnection huc = null;
- try {
- File destinationDirectory = destinationFile.getParentFile();
- destinationDirectory.mkdirs();
- File tempDestinationFile = new File(destinationDirectory, "."
- + destinationFile.getName());
- BufferedOutputStream bos = new BufferedOutputStream(
- new FileOutputStream(tempDestinationFile));
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setRequestMethod("GET");
- if (!url.endsWith(".xz")) {
- huc.addRequestProperty("Accept-Encoding", "gzip");
- }
- huc.connect();
- int responseCode = huc.getResponseCode();
- if (responseCode == 200) {
- InputStream is;
- if (huc.getContentEncoding() != null &&
- huc.getContentEncoding().equalsIgnoreCase("gzip")) {
- is = new GZIPInputStream(huc.getInputStream());
- } else {
- is = huc.getInputStream();
- }
- BufferedInputStream bis = new BufferedInputStream(is);
- int len;
- byte[] data = new byte[8192];
- while ((len = bis.read(data, 0, 8192)) >= 0) {
- bos.write(data, 0, len);
- }
- bis.close();
- bos.close();
- tempDestinationFile.renameTo(destinationFile);
- destinationFile.setLastModified(lastModifiedMillis);
- }
- } catch (IOException e) {
- e.printStackTrace();
- if (huc != null) {
- huc.disconnect();
- }
- }
- }
-
- void deleteExtraneousLocalFiles(
- SortedSet<String> parsedDirectories,
- SortedMap<String, Long> remoteFiles, File localDirectory,
- SortedMap<String, Long> localFiles) {
- for (String localPath : localFiles.keySet()) {
- for (String remoteDirectory : parsedDirectories) {
- if (localPath.startsWith(remoteDirectory)) {
- if (!remoteFiles.containsKey(localPath)) {
- new File(localDirectory.getPath() + localPath).delete();
- }
- }
- }
- }
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
deleted file mode 100644
index e726ce9..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DescriptorDownloader;
-
-public class DescriptorDownloaderImpl
- implements DescriptorDownloader {
-
- private boolean hasStartedDownloading = false;
-
- private SortedMap<String, DirectoryDownloader> directoryAuthorities =
- new TreeMap<>();
- @Override
- public void addDirectoryAuthority(String nickname, String ip,
- int dirPort) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkDirectoryParameters(nickname, ip, dirPort);
- DirectoryDownloader directoryAuthority = new DirectoryDownloader(
- nickname, ip, dirPort);
- this.directoryAuthorities.put(nickname, directoryAuthority);
- }
-
- private SortedMap<String, DirectoryDownloader> directoryMirrors =
- new TreeMap<>();
- @Override
- public void addDirectoryMirror(String nickname, String ip,
- int dirPort) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkDirectoryParameters(nickname, ip, dirPort);
- DirectoryDownloader directoryMirror = new DirectoryDownloader(
- nickname, ip, dirPort);
- this.directoryMirrors.put(nickname, directoryMirror);
- /* TODO Implement prioritizing mirrors for non-vote downloads. */
- throw new UnsupportedOperationException("Prioritizing directory "
- + "mirrors over directory authorities is not implemented yet. "
- + "Until it is, configuring directory mirrors is misleading and "
- + "therefore not supported.");
- }
-
- private void checkDirectoryParameters(String nickname, String ip,
- int dirPort) {
- if (nickname == null || nickname.length() < 1) {
- throw new IllegalArgumentException("'" + nickname + "' is not a "
- + "valid nickname.");
- }
- if (ip == null || ip.length() < 7 || ip.split("\\.").length != 4) {
- throw new IllegalArgumentException("'" + ip + "' is not a valid IP "
- + "address.");
- }
- if (dirPort < 1 || dirPort > 65535) {
- throw new IllegalArgumentException(String.valueOf(dirPort) + " is "
- + "not a valid DirPort.");
- }
- /* TODO Relax the requirement for directory nicknames to be unique.
- * In theory, we can identify them by ip+port. */
- if (this.directoryAuthorities.containsKey(nickname) ||
- this.directoryMirrors.containsKey(nickname)) {
- throw new IllegalArgumentException("Directory nicknames must be "
- + "unique.");
- }
- }
-
- private boolean downloadConsensus = false;
- @Override
- public void setIncludeCurrentConsensus() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.downloadConsensus = true;
- }
-
- private boolean downloadConsensusFromAllAuthorities = false;
- @Override
- public void setIncludeCurrentConsensusFromAllDirectoryAuthorities() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.downloadConsensusFromAllAuthorities = true;
- }
-
- private boolean includeCurrentReferencedVotes = false;
- @Override
- public void setIncludeCurrentReferencedVotes() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.includeCurrentReferencedVotes = true;
- }
-
- private Set<String> downloadVotes = new HashSet<>();
- @Override
- public void setIncludeCurrentVote(String fingerprint) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkVoteFingerprint(fingerprint);
- this.downloadVotes.add(fingerprint);
- }
-
- @Override
- public void setIncludeCurrentVotes(Set<String> fingerprints) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (fingerprints == null) {
- throw new IllegalArgumentException("Set of fingerprints must not "
- + "be null.");
- }
- for (String fingerprint : fingerprints) {
- this.checkVoteFingerprint(fingerprint);
- }
- for (String fingerprint : fingerprints) {
- this.setIncludeCurrentVote(fingerprint);
- }
- }
-
- private void checkVoteFingerprint(String fingerprint) {
- if (fingerprint == null || fingerprint.length() != 40) {
- throw new IllegalArgumentException("'" + fingerprint + "' is not a "
- + "valid fingerprint.");
- }
- }
-
- @Override
- public void setIncludeReferencedServerDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeServerDescriptor(String identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeServerDescriptors(Set<String> identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setIncludeReferencedExtraInfoDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeExtraInfoDescriptor(String identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeExtraInfoDescriptors(Set<String> identifiers) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- private long readTimeoutMillis = 60L * 1000L;
- @Override
- public void setReadTimeout(long readTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (readTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Read timeout value "
- + String.valueOf(readTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.readTimeoutMillis = readTimeoutMillis;
- }
-
- private long connectTimeoutMillis = 60L * 1000L;
- @Override
- public void setConnectTimeout(long connectTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (connectTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Connect timeout value "
- + String.valueOf(connectTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.connectTimeoutMillis = connectTimeoutMillis;
- }
-
- private long globalTimeoutMillis = 60L * 60L * 1000L;
- @Override
- public void setGlobalTimeout(long globalTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (globalTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Global timeout value "
- + String.valueOf(globalTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.globalTimeoutMillis = globalTimeoutMillis;
- }
-
- private boolean failUnrecognizedDescriptorLines = false;
- @Override
- public void setFailUnrecognizedDescriptorLines() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.failUnrecognizedDescriptorLines = true;
- }
-
- @Override
- public Iterator<DescriptorRequest> downloadDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Initiating downloads is only "
- + "permitted once.");
- }
- this.hasStartedDownloading = true;
- DownloadCoordinatorImpl downloadCoordinator =
- new DownloadCoordinatorImpl(this.directoryAuthorities,
- this.directoryMirrors, this.downloadConsensus,
- this.downloadConsensusFromAllAuthorities, this.downloadVotes,
- this.includeCurrentReferencedVotes, this.connectTimeoutMillis,
- this.readTimeoutMillis, this.globalTimeoutMillis,
- this.failUnrecognizedDescriptorLines);
- Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator.
- getDescriptorQueue();
- return descriptorQueue;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/org/torproject/descriptor/impl/DescriptorFileImpl.java
deleted file mode 100644
index 801c546..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorFileImpl.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-
-public class DescriptorFileImpl implements DescriptorFile {
-
- private File directory;
- protected void setDirectory(File directory) {
- this.directory = directory;
- }
- @Override
- public File getDirectory() {
- return this.directory;
- }
-
- private File tarball;
- protected void setTarball(File tarball) {
- this.tarball = tarball;
- }
- @Override
- public File getTarball() {
- return this.tarball;
- }
-
- private File file;
- protected void setFile(File file) {
- this.file = file;
- }
- @Override
- public File getFile() {
- return this.file;
- }
-
- private String fileName;
- protected void setFileName(String fileName) {
- this.fileName = fileName;
- }
- @Override
- public String getFileName() {
- return this.fileName;
- }
-
- private long lastModified;
- protected void setLastModified(long lastModified) {
- this.lastModified = lastModified;
- }
- @Override
- public long getLastModified() {
- return this.lastModified;
- }
-
- private List<Descriptor> descriptors;
- protected void setDescriptors(List<Descriptor> descriptors) {
- this.descriptors = descriptors;
- }
- @Override
- public List<Descriptor> getDescriptors() {
- return this.descriptors == null ? new ArrayList<Descriptor>() :
- new ArrayList<>(this.descriptors);
- }
-
- private Exception exception;
- protected void setException(Exception exception) {
- this.exception = exception;
- }
- @Override
- public Exception getException() {
- return this.exception;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
deleted file mode 100644
index 5625b3f..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-
-import org.torproject.descriptor.Descriptor;
-
-public abstract class DescriptorImpl implements Descriptor {
-
- protected static List<Descriptor> parseDescriptors(
- byte[] rawDescriptorBytes, String fileName,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<Descriptor> parsedDescriptors = new ArrayList<>();
- if (rawDescriptorBytes == null) {
- return parsedDescriptors;
- }
- byte[] first100Chars = new byte[Math.min(100,
- rawDescriptorBytes.length)];
- System.arraycopy(rawDescriptorBytes, 0, first100Chars, 0,
- first100Chars.length);
- String firstLines = new String(first100Chars);
- if (firstLines.startsWith("@type network-status-consensus-3 1.") ||
- firstLines.startsWith("@type network-status-microdesc-"
- + "consensus-3 1.") ||
- ((firstLines.startsWith("network-status-version 3") ||
- firstLines.contains("\nnetwork-status-version 3")) &&
- firstLines.contains("\nvote-status consensus\n"))) {
- parsedDescriptors.addAll(RelayNetworkStatusConsensusImpl.
- parseConsensuses(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type network-status-vote-3 1.")
- || ((firstLines.startsWith("network-status-version 3\n") ||
- firstLines.contains("\nnetwork-status-version 3\n")) &&
- firstLines.contains("\nvote-status vote\n"))) {
- parsedDescriptors.addAll(RelayNetworkStatusVoteImpl.
- parseVotes(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-network-status 1.")
- || firstLines.startsWith("r ")) {
- parsedDescriptors.add(new BridgeNetworkStatusImpl(
- rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith(
- "@type bridge-server-descriptor 1.")) {
- parsedDescriptors.addAll(BridgeServerDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type server-descriptor 1.") ||
- firstLines.startsWith("router ") ||
- firstLines.contains("\nrouter ")) {
- parsedDescriptors.addAll(RelayServerDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-extra-info 1.")) {
- parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type extra-info 1.") ||
- firstLines.startsWith("extra-info ") ||
- firstLines.contains("\nextra-info ")) {
- parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type microdescriptor 1.") ||
- firstLines.startsWith("onion-key\n") ||
- firstLines.contains("\nonion-key\n")) {
- parsedDescriptors.addAll(MicrodescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-pool-assignment 1.") ||
- firstLines.startsWith("bridge-pool-assignment ") ||
- firstLines.contains("\nbridge-pool-assignment ")) {
- parsedDescriptors.addAll(BridgePoolAssignmentImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type dir-key-certificate-3 1.") ||
- firstLines.startsWith("dir-key-certificate-version ") ||
- firstLines.contains("\ndir-key-certificate-version ")) {
- parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type tordnsel 1.") ||
- firstLines.startsWith("ExitNode ") ||
- firstLines.contains("\nExitNode ")) {
- parsedDescriptors.add(new ExitListImpl(rawDescriptorBytes, fileName,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type network-status-2 1.") ||
- firstLines.startsWith("network-status-version 2\n") ||
- firstLines.contains("\nnetwork-status-version 2\n")) {
- parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type directory 1.") ||
- firstLines.startsWith("signed-directory\n") ||
- firstLines.contains("\nsigned-directory\n")) {
- parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type torperf 1.")) {
- parsedDescriptors.addAll(TorperfResultImpl.parseTorperfResults(
- rawDescriptorBytes, failUnrecognizedDescriptorLines));
- } else {
- throw new DescriptorParseException("Could not detect descriptor "
- + "type in descriptor starting with '" + firstLines + "'.");
- }
- return parsedDescriptors;
- }
-
- protected static List<byte[]> splitRawDescriptorBytes(
- byte[] rawDescriptorBytes, String startToken) {
- List<byte[]> rawDescriptors = new ArrayList<>();
- String splitToken = "\n" + startToken;
- String ascii;
- try {
- ascii = new String(rawDescriptorBytes, "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- return rawDescriptors;
- }
- int endAllDescriptors = rawDescriptorBytes.length,
- startAnnotations = 0;
- boolean containsAnnotations = ascii.startsWith("@") ||
- ascii.contains("\n@");
- while (startAnnotations < endAllDescriptors) {
- int startDescriptor;
- if (ascii.indexOf(startToken, startAnnotations) == 0) {
- startDescriptor = startAnnotations;
- } else {
- startDescriptor = ascii.indexOf(splitToken, startAnnotations - 1);
- if (startDescriptor < 0) {
- break;
- } else {
- startDescriptor += 1;
- }
- }
- int endDescriptor = -1;
- if (containsAnnotations) {
- endDescriptor = ascii.indexOf("\n@", startDescriptor);
- }
- if (endDescriptor < 0) {
- endDescriptor = ascii.indexOf(splitToken, startDescriptor);
- }
- if (endDescriptor < 0) {
- endDescriptor = endAllDescriptors - 1;
- }
- endDescriptor += 1;
- byte[] rawDescriptor = new byte[endDescriptor - startAnnotations];
- System.arraycopy(rawDescriptorBytes, startAnnotations,
- rawDescriptor, 0, endDescriptor - startAnnotations);
- startAnnotations = endDescriptor;
- rawDescriptors.add(rawDescriptor);
- }
- return rawDescriptors;
- }
-
- protected byte[] rawDescriptorBytes;
- @Override
- public byte[] getRawDescriptorBytes() {
- return this.rawDescriptorBytes;
- }
-
- protected boolean failUnrecognizedDescriptorLines = false;
-
- protected List<String> unrecognizedLines;
- @Override
- public List<String> getUnrecognizedLines() {
- return this.unrecognizedLines == null ? new ArrayList<String>() :
- new ArrayList<>(this.unrecognizedLines);
- }
-
- protected DescriptorImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines, boolean blankLinesAllowed)
- throws DescriptorParseException {
- this.rawDescriptorBytes = rawDescriptorBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.cutOffAnnotations(rawDescriptorBytes);
- this.countKeywords(rawDescriptorBytes, blankLinesAllowed);
- }
-
- /* Parse annotation lines from the descriptor bytes. */
- private List<String> annotations = new ArrayList<>();
- private void cutOffAnnotations(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- String ascii = new String(rawDescriptorBytes);
- int start = 0;
- while ((start == 0 && ascii.startsWith("@")) ||
- (start > 0 && ascii.indexOf("\n@", start - 1) >= 0)) {
- int end = ascii.indexOf("\n", start);
- if (end < 0) {
- throw new DescriptorParseException("Annotation line does not "
- + "contain a newline.");
- }
- this.annotations.add(ascii.substring(start, end));
- start = end + 1;
- }
- if (start > 0) {
- int length = rawDescriptorBytes.length;
- byte[] rawDescriptor = new byte[length - start];
- System.arraycopy(rawDescriptorBytes, start, rawDescriptor, 0,
- length - start);
- this.rawDescriptorBytes = rawDescriptor;
- }
- }
- @Override
- public List<String> getAnnotations() {
- return new ArrayList<>(this.annotations);
- }
-
- /* Count parsed keywords for consistency checks by subclasses. */
- private String firstKeyword, lastKeyword;
- private Map<String, Integer> parsedKeywords = new HashMap<>();
- private void countKeywords(byte[] rawDescriptorBytes,
- boolean blankLinesAllowed) throws DescriptorParseException {
- if (rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- if (!blankLinesAllowed && (descriptorString.startsWith("\n") ||
- descriptorString.contains("\n\n"))) {
- throw new DescriptorParseException("Blank lines are not allowed.");
- }
- boolean skipCrypto = false;
- Scanner s = new Scanner(descriptorString).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("-----BEGIN")) {
- skipCrypto = true;
- } else if (line.startsWith("-----END")) {
- skipCrypto = false;
- } else if (!line.isEmpty() && !line.startsWith("@") &&
- !skipCrypto) {
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String keyword = lineNoOpt.split(" ", -1)[0];
- if (keyword.equals("")) {
- throw new DescriptorParseException("Illegal keyword in line '"
- + line + "'.");
- }
- if (this.firstKeyword == null) {
- this.firstKeyword = keyword;
- }
- lastKeyword = keyword;
- if (parsedKeywords.containsKey(keyword)) {
- parsedKeywords.put(keyword, parsedKeywords.get(keyword) + 1);
- } else {
- parsedKeywords.put(keyword, 1);
- }
- }
- }
- }
-
- protected void checkFirstKeyword(String keyword)
- throws DescriptorParseException {
- if (this.firstKeyword == null ||
- !this.firstKeyword.equals(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' must "
- + "be contained in the first line.");
- }
- }
-
- protected void checkLastKeyword(String keyword)
- throws DescriptorParseException {
- if (this.lastKeyword == null ||
- !this.lastKeyword.equals(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' must "
- + "be contained in the last line.");
- }
- }
-
- protected void checkExactlyOnceKeywords(Set<String> keywords)
- throws DescriptorParseException {
- for (String keyword : keywords) {
- int contained = 0;
- if (this.parsedKeywords.containsKey(keyword)) {
- contained = this.parsedKeywords.get(keyword);
- }
- if (contained != 1) {
- throw new DescriptorParseException("Keyword '" + keyword + "' is "
- + "contained " + contained + " times, but must be contained "
- + "exactly once.");
- }
- }
- }
-
- protected void checkAtLeastOnceKeywords(Set<String> keywords)
- throws DescriptorParseException {
- for (String keyword : keywords) {
- if (!this.parsedKeywords.containsKey(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' is "
- + "contained 0 times, but must be contained at least once.");
- }
- }
- }
-
- protected void checkAtMostOnceKeywords(Set<String> keywords)
- throws DescriptorParseException {
- for (String keyword : keywords) {
- if (this.parsedKeywords.containsKey(keyword) &&
- this.parsedKeywords.get(keyword) > 1) {
- throw new DescriptorParseException("Keyword '" + keyword + "' is "
- + "contained " + this.parsedKeywords.get(keyword) + " times, "
- + "but must be contained at most once.");
- }
- }
- }
-
- protected void checkKeywordsDependOn(Set<String> dependentKeywords,
- String dependingKeyword) throws DescriptorParseException {
- for (String dependentKeyword : dependentKeywords) {
- if (this.parsedKeywords.containsKey(dependentKeyword) &&
- !this.parsedKeywords.containsKey(dependingKeyword)) {
- throw new DescriptorParseException("Keyword '" + dependentKeyword
- + "' is contained, but keyword '" + dependingKeyword + "' is "
- + "not.");
- }
- }
- }
-
- protected int getKeywordCount(String keyword) {
- if (!this.parsedKeywords.containsKey(keyword)) {
- return 0;
- } else {
- return this.parsedKeywords.get(keyword);
- }
- }
-
- protected void clearParsedKeywords() {
- this.parsedKeywords = null;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorParseException.java b/src/org/torproject/descriptor/impl/DescriptorParseException.java
deleted file mode 100644
index 0f9add2..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorParseException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-/**
- * @deprecated Replaced by
- * org.torproject.descriptor.DescriptorParseException
- */
-@Deprecated public class DescriptorParseException extends Exception {
- private static final long serialVersionUID = 100L;
- protected DescriptorParseException(String message) {
- super(message);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/org/torproject/descriptor/impl/DescriptorParserImpl.java
deleted file mode 100644
index 6ac53f8..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorParserImpl.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorParser;
-
-public class DescriptorParserImpl implements DescriptorParser {
-
- private boolean failUnrecognizedDescriptorLines;
-
- @Override
- public void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines) {
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- }
-
- @Override
- public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
- String fileName) throws DescriptorParseException {
- return DescriptorImpl.parseDescriptors(rawDescriptorBytes, fileName,
- this.failUnrecognizedDescriptorLines);
- }
-}
diff --git a/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java
deleted file mode 100644
index 8da88e9..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.Stack;
-import java.util.TreeMap;
-
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
-import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorReader;
-
-public class DescriptorReaderImpl implements DescriptorReader {
-
- private boolean hasStartedReading = false;
-
- private List<File> directories = new ArrayList<>();
- @Override
- public void addDirectory(File directory) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.directories.add(directory);
- }
-
- private List<File> tarballs = new ArrayList<>();
- @Override
- public void addTarball(File tarball) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.tarballs.add(tarball);
- }
-
- private File historyFile;
- @Override
- public void setExcludeFiles(File historyFile) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.historyFile = historyFile;
- }
-
- private SortedMap<String, Long> excludedFiles;
- @Override
- public void setExcludedFiles(SortedMap<String, Long> excludedFiles) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.excludedFiles = excludedFiles;
- }
-
- @Override
- public SortedMap<String, Long> getExcludedFiles() {
- if (this.reader == null || !this.reader.hasFinishedReading) {
- throw new IllegalStateException("Operation is not permitted before "
- + "finishing to read.");
- }
- return new TreeMap<>(this.reader.excludedFilesAfter);
- }
-
- @Override
- public SortedMap<String, Long> getParsedFiles() {
- if (this.reader == null || !this.reader.hasFinishedReading) {
- throw new IllegalStateException("Operation is not permitted before "
- + "finishing to read.");
- }
- return new TreeMap<>(this.reader.parsedFilesAfter);
- }
-
- private boolean failUnrecognizedDescriptorLines = false;
- @Override
- public void setFailUnrecognizedDescriptorLines() {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.failUnrecognizedDescriptorLines = true;
- }
-
- private Integer maxDescriptorFilesInQueue = null;
- @Override
- public void setMaxDescriptorFilesInQueue(int max) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.maxDescriptorFilesInQueue = max;
- }
-
- private DescriptorReaderRunnable reader;
- @Override
- public Iterator<DescriptorFile> readDescriptors() {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Initiating reading is only "
- + "permitted once.");
- }
- this.hasStartedReading = true;
- BlockingIteratorImpl<DescriptorFile> descriptorQueue =
- this.maxDescriptorFilesInQueue == null
- ? new BlockingIteratorImpl<DescriptorFile>()
- : new BlockingIteratorImpl<DescriptorFile>(
- this.maxDescriptorFilesInQueue);
- this.reader = new DescriptorReaderRunnable(this.directories,
- this.tarballs, descriptorQueue, this.historyFile,
- this.excludedFiles, this.failUnrecognizedDescriptorLines);
- new Thread(this.reader).start();
- return descriptorQueue;
- }
-
- private static class DescriptorReaderRunnable implements Runnable {
- private List<File> directories;
- private List<File> tarballs;
- private BlockingIteratorImpl<DescriptorFile> descriptorQueue;
- private File historyFile;
- private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>(),
- excludedFilesAfter = new TreeMap<>(),
- parsedFilesAfter = new TreeMap<>();
- private DescriptorParser descriptorParser;
- private boolean hasFinishedReading = false;
- private DescriptorReaderRunnable(List<File> directories,
- List<File> tarballs,
- BlockingIteratorImpl<DescriptorFile> descriptorQueue,
- File historyFile, SortedMap<String, Long> excludedFiles,
- boolean failUnrecognizedDescriptorLines) {
- this.directories = directories;
- this.tarballs = tarballs;
- this.descriptorQueue = descriptorQueue;
- this.historyFile = historyFile;
- if (excludedFiles != null) {
- this.excludedFilesBefore = excludedFiles;
- }
- this.descriptorParser = new DescriptorParserImpl();
- this.descriptorParser.setFailUnrecognizedDescriptorLines(
- failUnrecognizedDescriptorLines);
- }
- public void run() {
- try {
- this.readOldHistory();
- this.readDescriptors();
- this.readTarballs();
- this.hasFinishedReading = true;
- } catch (Throwable t) {
- /* We're usually not writing to stdout or stderr, but we shouldn't
- * stay quiet about this potential bug. If we were to switch to a
- * logging API, this would qualify as ERROR. */
- System.err.println("Bug: uncaught exception or error while "
- + "reading descriptors:");
- t.printStackTrace();
- } finally {
- this.descriptorQueue.setOutOfDescriptors();
- }
- if (this.hasFinishedReading) {
- this.writeNewHistory();
- }
- }
- private void readOldHistory() {
- if (this.historyFile == null) {
- return;
- }
- try {
- BufferedReader br = new BufferedReader(new FileReader(
- this.historyFile));
- String line;
- while ((line = br.readLine()) != null) {
- if (!line.contains(" ")) {
- /* TODO Handle this problem? */
- continue;
- }
- long lastModifiedMillis = Long.parseLong(line.substring(0,
- line.indexOf(" ")));
- String absolutePath = line.substring(line.indexOf(" ") + 1);
- this.excludedFilesBefore.put(absolutePath, lastModifiedMillis);
- }
- br.close();
- } catch (IOException e) {
- /* TODO Handle this exception. */
- } catch (NumberFormatException e) {
- /* TODO Handle this exception. */
- }
- }
- private void writeNewHistory() {
- if (this.historyFile == null) {
- return;
- }
- try {
- if (this.historyFile.getParentFile() != null) {
- this.historyFile.getParentFile().mkdirs();
- }
- BufferedWriter bw = new BufferedWriter(new FileWriter(
- this.historyFile));
- SortedMap<String, Long> newHistory = new TreeMap<>();
- newHistory.putAll(this.excludedFilesAfter);
- newHistory.putAll(this.parsedFilesAfter);
- for (Map.Entry<String, Long> e : newHistory.entrySet()) {
- String absolutePath = e.getKey();
- long lastModifiedMillis = e.getValue();
- bw.write(String.valueOf(lastModifiedMillis) + " " + absolutePath
- + "\n");
- }
- bw.close();
- } catch (IOException e) {
- /* TODO Handle this exception. */
- }
- }
- private void readDescriptors() {
- for (File directory : this.directories) {
- if (!directory.exists() || !directory.isDirectory()) {
- continue;
- }
- Stack<File> files = new Stack<>();
- files.add(directory);
- boolean abortReading = false;
- while (!abortReading && !files.isEmpty()) {
- File file = files.pop();
- if (file.isDirectory()) {
- files.addAll(Arrays.asList(file.listFiles()));
- } else if (file.getName().endsWith(".tar") ||
- file.getName().endsWith(".tar.bz2") ||
- file.getName().endsWith(".tar.xz")) {
- this.tarballs.add(file);
- } else {
- String absolutePath = file.getAbsolutePath();
- long lastModifiedMillis = file.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
- this.excludedFilesAfter.put(absolutePath,
- lastModifiedMillis);
- continue;
- }
- this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
- DescriptorFileImpl descriptorFile = new DescriptorFileImpl();
- try {
- descriptorFile.setDirectory(directory);
- descriptorFile.setFile(file);
- descriptorFile.setFileName(file.getAbsolutePath());
- descriptorFile.setLastModified(lastModifiedMillis);
- descriptorFile.setDescriptors(this.readFile(file));
- } catch (DescriptorParseException e) {
- descriptorFile.setException(e);
- } catch (IOException e) {
- descriptorFile.setException(e);
- abortReading = true;
- }
- this.descriptorQueue.add(descriptorFile);
- }
- }
- }
- }
- private void readTarballs() {
- List<File> files = new ArrayList<>(this.tarballs);
- boolean abortReading = false;
- while (!abortReading && !files.isEmpty()) {
- File tarball = files.remove(0);
- if (!tarball.getName().endsWith(".tar") &&
- !tarball.getName().endsWith(".tar.bz2") &&
- !tarball.getName().endsWith(".tar.xz")) {
- continue;
- }
- String absolutePath = tarball.getAbsolutePath();
- long lastModifiedMillis = tarball.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
- this.excludedFilesAfter.put(absolutePath, lastModifiedMillis);
- continue;
- }
- this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
- try {
- FileInputStream in = new FileInputStream(tarball);
- if (in.available() > 0) {
- TarArchiveInputStream tais = null;
- if (tarball.getName().endsWith(".tar.bz2")) {
- tais = new TarArchiveInputStream(
- new BZip2CompressorInputStream(in));
- } else if (tarball.getName().endsWith(".tar.xz")) {
- tais = new TarArchiveInputStream(
- new XZCompressorInputStream(in));
- } else if (tarball.getName().endsWith(".tar")) {
- tais = new TarArchiveInputStream(in);
- }
- BufferedInputStream bis = new BufferedInputStream(tais);
- TarArchiveEntry tae = null;
- while ((tae = tais.getNextTarEntry()) != null) {
- if (tae.isDirectory()) {
- continue;
- }
- DescriptorFileImpl descriptorFile =
- new DescriptorFileImpl();
- descriptorFile.setTarball(tarball);
- descriptorFile.setFileName(tae.getName());
- descriptorFile.setLastModified(tae.getLastModifiedDate().
- getTime());
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- while ((len = bis.read(data, 0, 1024)) >= 0) {
- baos.write(data, 0, len);
- }
- byte[] rawDescriptorBytes = baos.toByteArray();
- if (rawDescriptorBytes.length < 1) {
- continue;
- }
- try {
- String fileName = tae.getName().substring(
- tae.getName().lastIndexOf("/") + 1);
- List<Descriptor> parsedDescriptors =
- this.descriptorParser.parseDescriptors(
- rawDescriptorBytes, fileName);
- descriptorFile.setDescriptors(parsedDescriptors);
- } catch (DescriptorParseException e) {
- descriptorFile.setException(e);
- }
- this.descriptorQueue.add(descriptorFile);
- }
- }
- } catch (IOException e) {
- abortReading = true;
- }
- }
- }
- private List<Descriptor> readFile(File file) throws IOException,
- DescriptorParseException {
- FileInputStream fis = new FileInputStream(file);
- BufferedInputStream bis = new BufferedInputStream(fis);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- while ((len = bis.read(data, 0, 1024)) >= 0) {
- baos.write(data, 0, len);
- }
- bis.close();
- byte[] rawDescriptorBytes = baos.toByteArray();
- return this.descriptorParser.parseDescriptors(rawDescriptorBytes,
- file.getName());
- }
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java
deleted file mode 100644
index 0238f24..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorRequest;
-
-public class DescriptorRequestImpl implements DescriptorRequest {
-
- private String requestedResource;
- protected void setRequestedResource(String requestedResource) {
- this.requestedResource = requestedResource;
- }
- protected String getRequestedResource() {
- return this.requestedResource;
- }
-
- private String descriptorType;
- protected void setDescriptorType(String descriptorType) {
- this.descriptorType = descriptorType;
- }
- protected String getDescriptorType() {
- return this.descriptorType;
- }
-
- private byte[] responseBytes;
- protected byte[] getResponseBytes() {
- return this.responseBytes;
- }
- protected void setResponseBytes(byte[] responseBytes) {
- this.responseBytes = responseBytes;
- }
-
- private String requestUrl;
- @Override
- public String getRequestUrl() {
- return this.requestUrl;
- }
-
- private String directoryNickname;
- protected void setDirectoryNickname(String directoryNickname) {
- this.directoryNickname = directoryNickname;
- }
- @Override
- public String getDirectoryNickname() {
- return this.directoryNickname;
- }
-
- private int responseCode;
- protected void setResponseCode(int responseCode) {
- this.responseCode = responseCode;
- }
- @Override
- public int getResponseCode() {
- return this.responseCode;
- }
-
- private long requestStart;
- protected void setRequestStart(long requestStart) {
- this.requestStart = requestStart;
- }
- @Override
- public long getRequestStart() {
- return this.requestStart;
- }
-
- private long requestEnd;
- protected void setRequestEnd(long requestEnd) {
- this.requestEnd = requestEnd;
- }
- @Override
- public long getRequestEnd() {
- return this.requestEnd;
- }
-
- private boolean connectTimeoutHasExpired;
- @Override
- public boolean connectTimeoutHasExpired() {
- return this.connectTimeoutHasExpired;
- }
-
- private boolean readTimeoutHasExpired;
- @Override
- public boolean readTimeoutHasExpired() {
- return this.readTimeoutHasExpired;
- }
-
- private boolean globalTimeoutHasExpired;
- @Override
- public boolean globalTimeoutHasExpired() {
- return this.globalTimeoutHasExpired;
- }
-
- private List<Descriptor> descriptors;
- protected void setDescriptors(List<Descriptor> descriptors) {
- this.descriptors = descriptors;
- }
- @Override
- public List<Descriptor> getDescriptors() {
- return this.descriptors;
- }
-
- private Exception exception;
- protected void setException(Exception exception) {
- this.exception = exception;
- }
- @Override
- public Exception getException() {
- return this.exception;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java
deleted file mode 100644
index fb2f5ad..0000000
--- a/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.DirSourceEntry;
-
-public class DirSourceEntryImpl implements DirSourceEntry {
-
- private byte[] dirSourceEntryBytes;
- @Override
- public byte[] getDirSourceEntryBytes() {
- return this.dirSourceEntryBytes;
- }
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected DirSourceEntryImpl(byte[] dirSourceEntryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.dirSourceEntryBytes = dirSourceEntryBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseDirSourceEntryBytes();
- this.checkAndClearKeywords();
- }
-
- private SortedSet<String> exactlyOnceKeywords, atMostOnceKeywords;
- private void initializeKeywords() {
- this.exactlyOnceKeywords = new TreeSet<>();
- this.exactlyOnceKeywords.add("dir-source");
- this.exactlyOnceKeywords.add("vote-digest");
- this.atMostOnceKeywords = new TreeSet<>();
- this.atMostOnceKeywords.add("contact");
- }
-
- private void parsedExactlyOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.exactlyOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in dir-source.");
- }
- this.exactlyOnceKeywords.remove(keyword);
- }
-
- private void parsedAtMostOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.atMostOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate " + keyword + "line "
- + "in dir-source.");
- }
- this.atMostOnceKeywords.remove(keyword);
- }
-
- private void checkAndClearKeywords() throws DescriptorParseException {
- if (!this.exactlyOnceKeywords.isEmpty()) {
- throw new DescriptorParseException("dir-source does not contain a '"
- + this.exactlyOnceKeywords.first() + "' line.");
- }
- this.exactlyOnceKeywords = null;
- this.atMostOnceKeywords = null;
- }
-
- private void parseDirSourceEntryBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.dirSourceEntryBytes)).
- useDelimiter("\n");
- boolean skipCrypto = false;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ");
- switch (parts[0]) {
- case "dir-source":
- this.parseDirSourceLine(line);
- break;
- case "contact":
- this.parseContactLine(line);
- break;
- case "vote-digest":
- this.parseVoteDigestLine(line);
- break;
- case "-----BEGIN":
- skipCrypto = true;
- break;
- case "-----END":
- skipCrypto = false;
- break;
- default:
- if (!skipCrypto) {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseDirSourceLine(String line)
- throws DescriptorParseException {
- this.parsedExactlyOnceKeyword("dir-source");
- String[] parts = line.split("[ \t]+");
- if (parts.length != 7) {
- throw new DescriptorParseException("Invalid line '" + line + "'.");
- }
- String nickname = parts[1];
- if (nickname.endsWith("-legacy")) {
- nickname = nickname.substring(0, nickname.length()
- - "-legacy".length());
- this.isLegacy = true;
- this.parsedExactlyOnceKeyword("vote-digest");
- }
- this.nickname = ParseHelper.parseNickname(line, nickname);
- this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
- if (parts[3].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.hostname = parts[3];
- this.ip = ParseHelper.parseIpv4Address(line, parts[4]);
- this.dirPort = ParseHelper.parsePort(line, parts[5]);
- this.orPort = ParseHelper.parsePort(line, parts[6]);
- }
-
- private void parseContactLine(String line)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("contact");
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseVoteDigestLine(String line)
- throws DescriptorParseException {
- this.parsedExactlyOnceKeyword("vote-digest");
- String[] parts = line.split("[ \t]+");
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "'.");
- }
- this.voteDigest = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private boolean isLegacy;
- @Override
- public boolean isLegacy() {
- return this.isLegacy;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String ip;
- @Override
- public String getIp() {
- return this.ip;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private String voteDigest;
- @Override
- public String getVoteDigest() {
- return this.voteDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/org/torproject/descriptor/impl/DirectoryDownloader.java
deleted file mode 100644
index a27ed76..0000000
--- a/src/org/torproject/descriptor/impl/DirectoryDownloader.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.zip.InflaterInputStream;
-
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorSourceFactory;
-
-/* Download descriptors from one directory authority or mirror. First,
- * ask the coordinator thread to create a request, run it, and deliver
- * the response. Repeat until the coordinator thread says there are no
- * further requests to make. */
-public class DirectoryDownloader implements Runnable {
-
- private String nickname;
- private String ipPort;
- private DescriptorParser descriptorParser;
- protected DirectoryDownloader(String nickname, String ip, int dirPort) {
- this.nickname = nickname;
- this.ipPort = ip + ":" + String.valueOf(dirPort);
- this.descriptorParser =
- DescriptorSourceFactory.createDescriptorParser();
- }
-
- private DownloadCoordinator downloadCoordinator;
- protected void setDownloadCoordinator(
- DownloadCoordinator downloadCoordinator) {
- this.downloadCoordinator = downloadCoordinator;
- }
-
- private long connectTimeout;
- protected void setConnectTimeout(long connectTimeout) {
- this.connectTimeout = connectTimeout;
- }
-
- private long readTimeout;
- protected void setReadTimeout(long readTimeout) {
- this.readTimeout = readTimeout;
- }
-
- protected void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines) {
- this.descriptorParser.setFailUnrecognizedDescriptorLines(
- failUnrecognizedDescriptorLines);
- }
-
- @Override
- public void run() {
- boolean keepRunning = true;
- do {
- DescriptorRequestImpl request =
- this.downloadCoordinator.createRequest(this.nickname);
- if (request != null) {
- String url = "http://" + this.ipPort
- + request.getRequestedResource();
- request.setRequestStart(System.currentTimeMillis());
- HttpURLConnection huc = null;
- try {
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setConnectTimeout((int) this.connectTimeout);
- huc.setReadTimeout((int) this.readTimeout);
- huc.setRequestMethod("GET");
- huc.connect();
- int responseCode = huc.getResponseCode();
- request.setResponseCode(responseCode);
- if (responseCode == 200) {
- BufferedInputStream in = new BufferedInputStream(
- new InflaterInputStream(huc.getInputStream()));
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[8192];
- while ((len = in.read(data, 0, 8192)) >= 0) {
- baos.write(data, 0, len);
- }
- in.close();
- byte[] responseBytes = baos.toByteArray();
- request.setResponseBytes(responseBytes);
- request.setRequestEnd(System.currentTimeMillis());
- request.setDescriptors(this.descriptorParser.parseDescriptors(
- responseBytes, null));
- }
- } catch (Exception e) {
- request.setException(e);
- if (huc != null) {
- huc.disconnect();
- }
- /* Stop downloading from this directory if there are any
- * problems, e.g., refused connections. */
- keepRunning = false;
- }
- this.downloadCoordinator.deliverResponse(request);
- } else {
- keepRunning = false;
- }
- } while (keepRunning);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
deleted file mode 100644
index b62fc8e..0000000
--- a/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectoryKeyCertificate;
-
-/* TODO Add test class. */
-
-public class DirectoryKeyCertificateImpl extends DescriptorImpl
- implements DirectoryKeyCertificate {
-
- protected static List<DirectoryKeyCertificate> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<DirectoryKeyCertificate> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DirectoryKeyCertificateImpl.splitRawDescriptorBytes(
- descriptorsBytes, "dir-key-certificate-version ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- DirectoryKeyCertificate parsedDescriptor =
- new DirectoryKeyCertificateImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected DirectoryKeyCertificateImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- this.calculateDigest();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "dir-key-certificate-version,fingerprint,dir-identity-key,"
- + "dir-key-published,dir-key-expires,dir-signing-key,"
- + "dir-key-certification").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "dir-address,dir-key-crosscert").split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("dir-key-certificate-version");
- this.checkLastKeyword("dir-key-certification");
- this.clearParsedKeywords();
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-key-published":
- this.parseDirKeyPublishedLine(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
- case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
- break;
- case "dir-signing-key":
- this.dirSigningKey = cryptoString;
- break;
- case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
- break;
- case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
- break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in directory key certificate.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in directory key certificate.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseDirKeyCertificateVersionLine(String line,
- String[] parts) throws DescriptorParseException {
- if (!line.equals("dir-key-certificate-version 3")) {
- throw new DescriptorParseException("Illegal directory key "
- + "certificate version number in line '" + line + "'.");
- }
- this.dirKeyCertificateVersion = 3;
- }
-
- private void parseDirAddressLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || parts[1].split(":").length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in directory key certificate.");
- }
- this.address = ParseHelper.parseIpv4Address(line,
- parts[1].split(":")[0]);
- this.port = ParseHelper.parsePort(line, parts[1].split(":")[1]);
- }
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in directory key certificate.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parseDirIdentityKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-identity-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyPublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirKeyExpiresLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCrosscertLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-crosscert")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCertificationLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-certification")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "dir-key-certificate-version ";
- String sigToken = "\ndir-key-certification\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.certificateDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.certificateDigest == null) {
- throw new DescriptorParseException("Could not calculate "
- + "certificate digest.");
- }
- }
-
- private int dirKeyCertificateVersion;
- @Override
- public int getDirKeyCertificateVersion() {
- return this.dirKeyCertificateVersion;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int port = -1;
- @Override
- public int getPort() {
- return this.port;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String dirIdentityKey;
- @Override
- public String getDirIdentityKey() {
- return this.dirIdentityKey;
- }
-
- private long dirKeyPublishedMillis;
- @Override
- public long getDirKeyPublishedMillis() {
- return this.dirKeyPublishedMillis;
- }
-
- private long dirKeyExpiresMillis;
- @Override
- public long getDirKeyExpiresMillis() {
- return this.dirKeyExpiresMillis;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String dirKeyCrosscert;
- @Override
- public String getDirKeyCrosscert() {
- return this.dirKeyCrosscert;
- }
-
- private String dirKeyCertification;
- @Override
- public String getDirKeyCertification() {
- return this.dirKeyCertification;
- }
-
- private String certificateDigest;
- @Override
- public String getCertificateDigest() {
- return this.certificateDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
deleted file mode 100644
index a955f62..0000000
--- a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-
-import org.torproject.descriptor.DirectorySignature;
-
-public class DirectorySignatureImpl implements DirectorySignature {
-
- private byte[] directorySignatureBytes;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected DirectorySignatureImpl(byte[] directorySignatureBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.directorySignatureBytes = directorySignatureBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.parseDirectorySignatureBytes();
- }
-
- private void parseDirectorySignatureBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.directorySignatureBytes)).
- useDelimiter("\n");
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ", -1);
- String keyword = parts[0];
- switch (keyword) {
- case "directory-signature":
- int algorithmOffset = 0;
- switch (parts.length) {
- case 4:
- this.algorithm = parts[1];
- algorithmOffset = 1;
- break;
- case 3:
- break;
- default:
- throw new DescriptorParseException("Illegal line '" + line
- + "'.");
- }
- this.identity = ParseHelper.parseHexString(line,
- parts[1 + algorithmOffset]);
- this.signingKeyDigest = ParseHelper.parseHexString(
- line, parts[2 + algorithmOffset]);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- this.signature = cryptoString;
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- static final String DEFAULT_ALGORITHM = "sha1";
-
- private String algorithm;
- @Override
- public String getAlgorithm() {
- return this.algorithm == null ? DEFAULT_ALGORITHM : this.algorithm;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private String signingKeyDigest;
- @Override
- public String getSigningKeyDigest() {
- return this.signingKeyDigest;
- }
-
- private String signature;
- @Override
- public String getSignature() {
- return this.signature;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/org/torproject/descriptor/impl/DownloadCoordinator.java
deleted file mode 100644
index 72cfeae..0000000
--- a/src/org/torproject/descriptor/impl/DownloadCoordinator.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-public interface DownloadCoordinator {
-
- public DescriptorRequestImpl createRequest(String nickname);
-
- public void deliverResponse(DescriptorRequestImpl request);
-}
diff --git a/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
deleted file mode 100644
index a8e3731..0000000
--- a/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* TODO This whole download logic is a mess and needs a cleanup. */
-public class DownloadCoordinatorImpl implements DownloadCoordinator {
-
- private BlockingIteratorImpl<DescriptorRequest> descriptorQueue =
- new BlockingIteratorImpl<>();
- protected Iterator<DescriptorRequest> getDescriptorQueue() {
- return this.descriptorQueue;
- }
-
- private SortedSet<String> runningDirectories;
- private SortedMap<String, DirectoryDownloader> directoryAuthorities;
- private SortedMap<String, DirectoryDownloader> directoryMirrors;
- private boolean downloadConsensusFromAllAuthorities;
- private boolean includeCurrentReferencedVotes;
- private long connectTimeoutMillis;
- private long readTimeoutMillis;
- private long globalTimeoutMillis;
- private boolean failUnrecognizedDescriptorLines;
-
- protected DownloadCoordinatorImpl(
- SortedMap<String, DirectoryDownloader> directoryAuthorities,
- SortedMap<String, DirectoryDownloader> directoryMirrors,
- boolean downloadConsensus,
- boolean downloadConsensusFromAllAuthorities,
- Set<String> downloadVotes, boolean includeCurrentReferencedVotes,
- long connectTimeoutMillis, long readTimeoutMillis,
- long globalTimeoutMillis, boolean failUnrecognizedDescriptorLines) {
- this.directoryAuthorities = directoryAuthorities;
- this.directoryMirrors = directoryMirrors;
- this.runningDirectories = new TreeSet<>();
- this.runningDirectories.addAll(directoryAuthorities.keySet());
- this.runningDirectories.addAll(directoryMirrors.keySet());
- this.missingConsensus = downloadConsensus;
- this.downloadConsensusFromAllAuthorities =
- downloadConsensusFromAllAuthorities;
- this.missingVotes = downloadVotes;
- this.includeCurrentReferencedVotes = includeCurrentReferencedVotes;
- this.connectTimeoutMillis = connectTimeoutMillis;
- this.readTimeoutMillis = readTimeoutMillis;
- this.globalTimeoutMillis = globalTimeoutMillis;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- if (this.directoryMirrors.isEmpty() &&
- this.directoryAuthorities.isEmpty()) {
- this.descriptorQueue.setOutOfDescriptors();
- /* TODO Should we say anything if we don't have any directories
- * configured? */
- } else {
- GlobalTimer globalTimer = new GlobalTimer(this.globalTimeoutMillis,
- this);
- this.globalTimerThread = new Thread(globalTimer);
- this.globalTimerThread.start();
- for (DirectoryDownloader directoryMirror :
- this.directoryMirrors.values()) {
- directoryMirror.setDownloadCoordinator(this);
- directoryMirror.setConnectTimeout(this.connectTimeoutMillis);
- directoryMirror.setReadTimeout(this.readTimeoutMillis);
- directoryMirror.setFailUnrecognizedDescriptorLines(
- this.failUnrecognizedDescriptorLines);
- new Thread(directoryMirror).start();
- }
- for (DirectoryDownloader directoryAuthority :
- this.directoryAuthorities.values()) {
- directoryAuthority.setDownloadCoordinator(this);
- directoryAuthority.setConnectTimeout(this.connectTimeoutMillis);
- directoryAuthority.setReadTimeout(this.readTimeoutMillis);
- directoryAuthority.setFailUnrecognizedDescriptorLines(
- this.failUnrecognizedDescriptorLines);
- new Thread(directoryAuthority).start();
- }
- }
- }
-
- /* Interrupt all downloads if the total download time exceeds a given
- * time. */
- private Thread globalTimerThread;
- private static class GlobalTimer implements Runnable {
- private long timeoutMillis;
- private DownloadCoordinatorImpl downloadCoordinator;
- private GlobalTimer(long timeoutMillis,
- DownloadCoordinatorImpl downloadCoordinator) {
- this.timeoutMillis = timeoutMillis;
- this.downloadCoordinator = downloadCoordinator;
- }
- public void run() {
- long started = System.currentTimeMillis(), sleep;
- while ((sleep = started + this.timeoutMillis
- - System.currentTimeMillis()) > 0L) {
- try {
- Thread.sleep(sleep);
- } catch (InterruptedException e) {
- return;
- }
- }
- this.downloadCoordinator.interruptAllDownloads();
- }
- }
-
- /* Are we missing the consensus, and should the next directory that
- * hasn't tried downloading it before attempt to download it? */
- private boolean missingConsensus = false;
-
- /* Which directories are currently attempting to download the
- * consensus? */
- private Set<String> requestingConsensuses = new HashSet<>();
-
- /* Which directories have attempted to download the consensus so far,
- * including those directories that are currently attempting it? */
- private Set<String> requestedConsensuses = new HashSet<>();
-
- /* Which votes are we currently missing? */
- private Set<String> missingVotes = new HashSet<>();
-
- /* Which vote (map value) is a given directory (map key) currently
- * attempting to download? */
- private Map<String, String> requestingVotes = new HashMap<>();
-
- /* Which votes (map value) has a given directory (map key) attempted or
- * is currently attempting to download? */
- private Map<String, Set<String>> requestedVotes = new HashMap<>();
-
- private boolean hasFinishedDownloading = false;
-
- /* Look up what request a directory should make next. If there is
- * nothing to do right now, but maybe later, block the caller. If
- * we're done downloading, return null to notify the caller. */
- @Override
- public synchronized DescriptorRequestImpl createRequest(
- String nickname) {
- while (!this.hasFinishedDownloading) {
- DescriptorRequestImpl request = new DescriptorRequestImpl();
- request.setDirectoryNickname(nickname);
- if ((this.missingConsensus ||
- (this.downloadConsensusFromAllAuthorities &&
- this.directoryAuthorities.containsKey(nickname))) &&
- !this.requestedConsensuses.contains(nickname)) {
- if (!this.downloadConsensusFromAllAuthorities) {
- this.missingConsensus = false;
- }
- this.requestingConsensuses.add(nickname);
- this.requestedConsensuses.add(nickname);
- request.setRequestedResource(
- "/tor/status-vote/current/consensus.z");
- request.setDescriptorType("consensus");
- return request;
- }
- if (!this.missingVotes.isEmpty() &&
- this.directoryAuthorities.containsKey(nickname)) {
- String requestingVote = null;
- for (String missingVote : this.missingVotes) {
- if (!this.requestedVotes.containsKey(nickname) ||
- !this.requestedVotes.get(nickname).contains(missingVote)) {
- requestingVote = missingVote;
- }
- }
- if (requestingVote != null) {
- this.requestingVotes.put(nickname, requestingVote);
- if (!this.requestedVotes.containsKey(nickname)) {
- this.requestedVotes.put(nickname, new HashSet<String>());
- }
- this.requestedVotes.get(nickname).add(requestingVote);
- this.missingVotes.remove(requestingVote);
- request.setRequestedResource("/tor/status-vote/current/"
- + requestingVote + ".z");
- request.setDescriptorType("vote");
- return request;
- }
- }
- /* TODO Add server descriptors and extra-info descriptors later. */
- try {
- this.wait();
- } catch (InterruptedException e) {
- /* TODO What shall we do? */
- }
- }
- return null;
- }
-
- /* Deliver a response which may either contain one or more descriptors
- * or a failure response code. Update the lists of missing descriptors,
- * decide if there are more descriptors to download, and wake up any
- * waiting downloader threads. */
- @Override
- public synchronized void deliverResponse(
- DescriptorRequestImpl response) {
- String nickname = response.getDirectoryNickname();
- if (response.getException() != null) {
- this.runningDirectories.remove(nickname);
- }
- switch (response.getDescriptorType()) {
- case "consensus":
- this.requestingConsensuses.remove(nickname);
- if (response.getResponseCode() == 200 &&
- response.getDescriptors() != null) {
- if (this.includeCurrentReferencedVotes) {
- /* TODO Only add votes if the consensus is not older than one
- * hour. Or does that make no sense? */
- for (Descriptor parsedDescriptor :
- response.getDescriptors()) {
- if (!(parsedDescriptor instanceof
- RelayNetworkStatusConsensus)) {
- continue;
- }
- RelayNetworkStatusConsensus parsedConsensus =
- (RelayNetworkStatusConsensus) parsedDescriptor;
- for (DirSourceEntry dirSource :
- parsedConsensus.getDirSourceEntries().values()) {
- String identity = dirSource.getIdentity();
- if (!this.missingVotes.contains(identity)) {
- boolean alreadyRequested = false;
- for (Set<String> requestedBefore :
- this.requestedVotes.values()) {
- if (requestedBefore.contains(identity)) {
- alreadyRequested = true;
- break;
- }
- }
- if (!alreadyRequested) {
- this.missingVotes.add(identity);
- }
- }
- }
- }
- /* TODO Later, add referenced server descriptors. */
- }
- } else {
- this.missingConsensus = true;
- }
- break;
- case "vote":
- String requestedVote = requestingVotes.remove(nickname);
- if (response.getResponseCode() != 200) {
- this.missingVotes.add(requestedVote);
- }
- }
- if (response.getRequestEnd() != 0L) {
- this.descriptorQueue.add(response);
- }
- boolean doneDownloading = true;
- if ((this.missingConsensus ||
- this.downloadConsensusFromAllAuthorities) &&
- (!this.requestedConsensuses.containsAll(
- this.runningDirectories) ||
- !this.requestingConsensuses.isEmpty())) {
- doneDownloading = false;
- }
- if (!this.requestingVotes.isEmpty()) {
- doneDownloading = false;
- } else if (!this.missingVotes.isEmpty()) {
- if (!this.requestedVotes.keySet().containsAll(
- this.runningDirectories)) {
- doneDownloading = false;
- } else {
- for (String missingVote : this.missingVotes) {
- for (String runningDirectory : this.runningDirectories) {
- Set<String> reqVotes = this.requestedVotes.get(
- runningDirectory);
- if (!reqVotes.contains(missingVote)) {
- doneDownloading = false;
- }
- }
- }
- }
- }
- if (doneDownloading) {
- this.hasFinishedDownloading = true;
- this.globalTimerThread.interrupt();
- this.descriptorQueue.setOutOfDescriptors();
- }
- /* Wake up all waiting downloader threads. Maybe they can now
- * download something, or they'll realize we're done downloading. */
- this.notifyAll();
- }
-
- private synchronized void interruptAllDownloads() {
- this.hasFinishedDownloading = true;
- this.notifyAll();
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/org/torproject/descriptor/impl/ExitListEntryImpl.java
deleted file mode 100644
index efbf31c..0000000
--- a/src/org/torproject/descriptor/impl/ExitListEntryImpl.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExitList;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
-
- private byte[] exitListEntryBytes;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- @Deprecated
- private ExitListEntryImpl(String fingerprint, long publishedMillis,
- long lastStatusMillis, String exitAddress, long scanMillis) {
- this.fingerprint = fingerprint;
- this.publishedMillis = publishedMillis;
- this.lastStatusMillis = lastStatusMillis;
- this.exitAddresses.put(exitAddress, scanMillis);
- }
-
- @Deprecated
- List<ExitListEntry> oldEntries() {
- List<ExitListEntry> result = new ArrayList<>();
- if (this.exitAddresses.size() > 1) {
- for (Map.Entry<String, Long> entry :
- this.exitAddresses.entrySet()) {
- result.add(new ExitListEntryImpl(this.fingerprint,
- this.publishedMillis, this.lastStatusMillis, entry.getKey(),
- entry.getValue()));
- }
- } else {
- result.add(this);
- }
- return result;
- }
-
- protected ExitListEntryImpl(byte[] exitListEntryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.exitListEntryBytes = exitListEntryBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseExitListEntryBytes();
- this.checkAndClearKeywords();
- }
-
- private SortedSet<String> keywordCountingSet;
- private void initializeKeywords() {
- this.keywordCountingSet = new TreeSet<>();
- this.keywordCountingSet.add("ExitNode");
- this.keywordCountingSet.add("Published");
- this.keywordCountingSet.add("LastStatus");
- this.keywordCountingSet.add("ExitAddress");
- }
-
- private void parsedExactlyOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.keywordCountingSet.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in exit list entry.");
- }
- this.keywordCountingSet.remove(keyword);
- }
-
- private void checkAndClearKeywords() throws DescriptorParseException {
- for (String missingKeyword : this.keywordCountingSet) {
- throw new DescriptorParseException("Missing '" + missingKeyword
- + "' line in exit list entry.");
- }
- this.keywordCountingSet = null;
- }
-
- private void parseExitListEntryBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.exitListEntryBytes)).
- useDelimiter(ExitList.EOL);
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ");
- String keyword = parts[0];
- switch (keyword) {
- case "ExitNode":
- this.parseExitNodeLine(line, parts);
- break;
- case "Published":
- this.parsePublishedLine(line, parts);
- break;
- case "LastStatus":
- this.parseLastStatusLine(line, parts);
- break;
- case "ExitAddress":
- this.parseExitAddressLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in exit list entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseExitNodeLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseLastStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.lastStatusMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseExitAddressLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 4) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.keywordCountingSet.remove(parts[0]);
- this.exitAddresses.put(ParseHelper.parseIpv4Address(line, parts[1]),
- ParseHelper.parseTimestampAtIndex(line, parts, 2, 3));
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long lastStatusMillis;
- @Override
- public long getLastStatusMillis() {
- return this.lastStatusMillis;
- }
-
- private String exitAddress;
- @Override
- public String getExitAddress() {
- if (null == exitAddress) {
- Map.Entry<String, Long> randomEntry =
- this.exitAddresses.entrySet().iterator().next();
- this.exitAddress = randomEntry.getKey();
- this.scanMillis = randomEntry.getValue();
- }
- return this.exitAddress;
- }
-
- private Map<String, Long> exitAddresses = new HashMap<>();
- @Override
- public Map<String, Long> getExitAddresses(){
- return new HashMap<>(this.exitAddresses);
- }
-
- private long scanMillis;
- @Override
- public long getScanMillis() {
- if (null == exitAddress) {
- getExitAddress();
- }
- return scanMillis;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExitListImpl.java b/src/org/torproject/descriptor/impl/ExitListImpl.java
deleted file mode 100644
index 10619ba..0000000
--- a/src/org/torproject/descriptor/impl/ExitListImpl.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.TimeZone;
-
-import org.torproject.descriptor.ExitList;
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListImpl extends DescriptorImpl implements ExitList {
-
- protected ExitListImpl(byte[] rawDescriptorBytes, String fileName,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
- this.splitAndParseExitListEntries(rawDescriptorBytes);
- this.setPublishedMillisFromFileName(fileName);
- }
-
- private void setPublishedMillisFromFileName(String fileName)
- throws DescriptorParseException {
- if (this.downloadedMillis == 0L &&
- fileName.length() == "2012-02-01-04-06-24".length()) {
- try {
- SimpleDateFormat fileNameFormat = new SimpleDateFormat(
- "yyyy-MM-dd-HH-mm-ss");
- fileNameFormat.setLenient(false);
- fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- this.downloadedMillis = fileNameFormat.parse(fileName).getTime();
- } catch (ParseException e) {
- /* Handle below. */
- }
- }
- if (this.downloadedMillis == 0L) {
- throw new DescriptorParseException("Unrecognized exit list file "
- + "name '" + fileName + "'.");
- }
- }
-
- private void splitAndParseExitListEntries(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- Scanner s = new Scanner(descriptorString).useDelimiter(EOL);
- StringBuilder sb = new StringBuilder();
- boolean firstEntry = true;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) { /* Skip annotation. */
- if (!s.hasNext()) {
- throw new DescriptorParseException("Descriptor is empty.");
- } else {
- line = s.next();
- }
- }
- String[] parts = line.split(" ");
- String keyword = parts[0];
- switch (keyword) {
- case "Downloaded":
- this.downloadedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- break;
- case "ExitNode":
- if (!firstEntry) {
- this.parseExitListEntry(sb.toString().getBytes());
- } else {
- firstEntry = false;
- }
- sb = new StringBuilder();
- sb.append(line).append(ExitList.EOL);
- break;
- case "Published":
- sb.append(line).append(ExitList.EOL);
- break;
- case "LastStatus":
- sb.append(line).append(ExitList.EOL);
- break;
- case "ExitAddress":
- sb.append(line).append(ExitList.EOL);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in exit list.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- /* Parse the last entry. */
- this.parseExitListEntry(sb.toString().getBytes());
- }
-
- protected void parseExitListEntry(byte[] exitListEntryBytes)
- throws DescriptorParseException {
- ExitListEntryImpl exitListEntry = new ExitListEntryImpl(
- exitListEntryBytes, this.failUnrecognizedDescriptorLines);
- this.exitListEntries.add(exitListEntry);
- this.oldExitListEntries.addAll(exitListEntry.oldEntries());
- List<String> unrecognizedExitListEntryLines = exitListEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedExitListEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedExitListEntryLines);
- }
- }
-
- private long downloadedMillis;
- @Override
- public long getDownloadedMillis() {
- return this.downloadedMillis;
- }
-
- private Set<ExitListEntry> oldExitListEntries = new HashSet<>();
- @Deprecated
- @Override
- public Set<ExitListEntry> getExitListEntries() {
- return new HashSet<>(this.oldExitListEntries);
- }
-
- private Set<ExitList.Entry> exitListEntries = new HashSet<>();
- @Override
- public Set<ExitList.Entry> getEntries() {
- return new HashSet<>(this.exitListEntries);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
deleted file mode 100644
index 3f72616..0000000
--- a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,1284 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-
-public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
- implements ExtraInfoDescriptor {
-
- protected ExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- this.calculateDigest();
- this.calculateDigestSha256();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "extra-info,published").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> dirreqStatsKeywords = new HashSet<>(Arrays.asList((
- "dirreq-stats-end,dirreq-v2-ips,dirreq-v3-ips,dirreq-v2-reqs,"
- + "dirreq-v3-reqs,dirreq-v2-share,dirreq-v3-share,dirreq-v2-resp,"
- + "dirreq-v3-resp,dirreq-v2-direct-dl,dirreq-v3-direct-dl,"
- + "dirreq-v2-tunneled-dl,dirreq-v3-tunneled-dl,").split(",")));
- Set<String> entryStatsKeywords = new HashSet<>(Arrays.asList(
- "entry-stats-end,entry-ips".split(",")));
- Set<String> cellStatsKeywords = new HashSet<>(Arrays.asList((
- "cell-stats-end,cell-processed-cells,cell-queued-cells,"
- + "cell-time-in-queue,cell-circuits-per-decile").split(",")));
- Set<String> connBiDirectStatsKeywords = new HashSet<>(
- Arrays.asList("conn-bi-direct".split(",")));
- Set<String> exitStatsKeywords = new HashSet<>(Arrays.asList((
- "exit-stats-end,exit-kibibytes-written,exit-kibibytes-read,"
- + "exit-streams-opened").split(",")));
- Set<String> bridgeStatsKeywords = new HashSet<>(Arrays.asList(
- "bridge-stats-end,bridge-stats-ips".split(",")));
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "identity-ed25519,master-key-ed25519,read-history,write-history,"
- + "dirreq-read-history,dirreq-write-history,geoip-db-digest,"
- + "router-sig-ed25519,router-signature,router-digest-sha256,"
- + "router-digest").split(",")));
- atMostOnceKeywords.addAll(dirreqStatsKeywords);
- atMostOnceKeywords.addAll(entryStatsKeywords);
- atMostOnceKeywords.addAll(cellStatsKeywords);
- atMostOnceKeywords.addAll(connBiDirectStatsKeywords);
- atMostOnceKeywords.addAll(exitStatsKeywords);
- atMostOnceKeywords.addAll(bridgeStatsKeywords);
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkKeywordsDependOn(dirreqStatsKeywords, "dirreq-stats-end");
- this.checkKeywordsDependOn(entryStatsKeywords, "entry-stats-end");
- this.checkKeywordsDependOn(cellStatsKeywords, "cell-stats-end");
- this.checkKeywordsDependOn(exitStatsKeywords, "exit-stats-end");
- this.checkKeywordsDependOn(bridgeStatsKeywords, "bridge-stats-end");
- this.checkFirstKeyword("extra-info");
- this.clearParsedKeywords();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- List<String> cryptoLines = null;
- while (s.hasNext()) {
- String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "extra-info":
- this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-db-digest":
- this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip6-db-digest":
- this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-start-time":
- this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-client-origins":
- this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-stats-end":
- this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-ips":
- this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-ips":
- this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-reqs":
- this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-reqs":
- this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-share":
- this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-share":
- this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-resp":
- this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-resp":
- this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-direct-dl":
- this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-direct-dl":
- this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-tunneled-dl":
- this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-tunneled-dl":
- this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-read-history":
- this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-write-history":
- this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-stats-end":
- this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-ips":
- this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-stats-end":
- this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-processed-cells":
- this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-queued-cells":
- this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-time-in-queue":
- this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-circuits-per-decile":
- this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt);
- break;
- case "conn-bi-direct":
- this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-stats-end":
- this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-written":
- this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-read":
- this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-streams-opened":
- this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-stats-end":
- this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ips":
- this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-versions":
- this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-transports":
- this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "transport":
- this.parseTransportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-stats-end":
- this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-rend-relayed-cells":
- this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
- partsNoOpt);
- break;
- case "hidserv-dir-onions-seen":
- this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
- case "router-signature":
- this.routerSignature = cryptoString;
- break;
- case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in extra-info "
- + "descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- cryptoLines = null;
- nextCrypto = "";
- }
- break;
- default:
- if (cryptoLines != null) {
- cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in extra-info descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseExtraInfoLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[2]);
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseGeoipDbDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoipDbDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseGeoip6DbDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoip6DbDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseGeoipStartTimeLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoipStartTimeMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseGeoipClientOriginsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.geoipClientOrigins =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.dirreqStatsEndMillis = parsedStatsEndData[0];
- this.dirreqStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private long[] parseStatsEndLine(String line, String partsNoOpt[],
- int partsNoOptExpectedLength) throws DescriptorParseException {
- if (partsNoOpt.length != partsNoOptExpectedLength ||
- partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
- !partsNoOpt[4].equals("s)")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- long[] result = new long[2];
- result[0] = ParseHelper.parseTimestampAtIndex(line, partsNoOpt, 1, 2);
- result[1] = ParseHelper.parseSeconds(line,
- partsNoOpt[3].substring(1));
- if (result[1] <= 0) {
- throw new DescriptorParseException("Interval length must be "
- + "positive in line '" + line + "'.");
- }
- return result;
- }
-
- private void parseDirreqV2IpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV3IpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV2ReqsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Reqs =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV3ReqsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Reqs =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV2ShareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Share = this.parseShareLine(line, partsNoOpt);
- }
-
- private void parseDirreqV3ShareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Share = this.parseShareLine(line, partsNoOpt);
- }
-
- private double parseShareLine(String line, String[] partsNoOpt)
- throws DescriptorParseException {
- double share = -1.0;
- if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
- partsNoOpt[1].endsWith("%")) {
- String shareString = partsNoOpt[1];
- shareString = shareString.substring(0, shareString.length() - 1);
- try {
- share = Double.parseDouble(shareString);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (share < 0.0) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- return share;
- }
-
- private void parseDirreqV2RespLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Resp =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3RespLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Resp =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV2DirectDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2DirectDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3DirectDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3DirectDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV2TunneledDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2TunneledDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3TunneledDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3TunneledDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line,partsNoOpt, 1, 0);
- }
-
- private void parseDirreqReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqReadHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseDirreqWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqWriteHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseEntryStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.entryStatsEndMillis = parsedStatsEndData[0];
- this.entryStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseEntryIpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.entryIps = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseCellStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.cellStatsEndMillis = parsedStatsEndData[0];
- this.cellStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseCellProcessedCellsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellProcessedCells = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
- if (this.cellProcessedCells.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellQueuedCellsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellQueuedCells = ParseHelper.parseCommaSeparatedDoubleValueList(
- line, partsNoOpt, 1);
- if (this.cellQueuedCells.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellTimeInQueueLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellTimeInQueue = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
- if (this.cellTimeInQueue.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellCircuitsPerDecileLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- int circuits = -1;
- if (partsNoOpt.length == 2) {
- try {
- circuits = Integer.parseInt(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (circuits < 0) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.cellCircuitsPerDecile = circuits;
- }
-
- private void parseConnBiDirectLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 6);
- this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
- this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
- Integer[] parsedConnBiDirectStats = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
- if (parsedConnBiDirectStats.length != 4) {
- throw new DescriptorParseException("Illegal line '" + line + "' in "
- + "extra-info descriptor.");
- }
- this.connBiDirectBelow = parsedConnBiDirectStats[0];
- this.connBiDirectRead = parsedConnBiDirectStats[1];
- this.connBiDirectWrite = parsedConnBiDirectStats[2];
- this.connBiDirectBoth = parsedConnBiDirectStats[3];
- }
-
- private void parseExitStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.exitStatsEndMillis = parsedStatsEndData[0];
- this.exitStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseExitKibibytesWrittenLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- this.exitKibibytesWritten = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitKibibytesWritten.keySet());
- this.verifyBytesOrStreams(line, this.exitKibibytesWritten.values());
- }
-
- private void parseExitKibibytesReadLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.exitKibibytesRead = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitKibibytesRead.keySet());
- this.verifyBytesOrStreams(line, this.exitKibibytesRead.values());
- }
-
- private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.exitStreamsOpened = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitStreamsOpened.keySet());
- this.verifyBytesOrStreams(line, this.exitStreamsOpened.values());
- }
-
- private SortedMap<String, Long> sortByPorts(
- SortedMap<String, Long> naturalOrder) {
- SortedMap<String, Long> byPortNumber =
- new TreeMap<String, Long>(new Comparator<String>() {
- public int compare(String arg0, String arg1) {
- int port0 = 0, port1 = 0;
- try {
- port1 = Integer.parseInt(arg1);
- } catch (NumberFormatException e) {
- return -1;
- }
- try {
- port0 = Integer.parseInt(arg0);
- } catch (NumberFormatException e) {
- return 1;
- }
- if (port0 < port1) {
- return -1;
- } else if (port0 > port1) {
- return 1;
- } else {
- return 0;
- }
- }});
- byPortNumber.putAll(naturalOrder);
- return byPortNumber;
- }
-
- private void verifyPorts(String line, Set<String> ports)
- throws DescriptorParseException {
- boolean valid = true;
- try {
- for (String port : ports) {
- if (!port.equals("other") && Integer.parseInt(port) <= 0) {
- valid = false;
- break;
- }
- }
- } catch (NumberFormatException e) {
- valid = false;
- }
- if (!valid) {
- throw new DescriptorParseException("Invalid port in line '" + line
- + "'.");
- }
- }
-
- private void verifyBytesOrStreams(String line,
- Collection<Long> bytesOrStreams) throws DescriptorParseException {
- boolean valid = true;
- for (long s : bytesOrStreams) {
- if (s < 0L) {
- valid = false;
- break;
- }
- }
- if (!valid) {
- throw new DescriptorParseException("Invalid value in line '" + line
- + "'.");
- }
- }
-
- private void parseBridgeStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.bridgeStatsEndMillis = parsedStatsEndData[0];
- this.bridgeStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseBridgeStatsIpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIps =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseBridgeIpVersionsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIpVersions =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseBridgeIpTransportsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIpTransports =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseTransportLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.transports.add(partsNoOpt[1]);
- }
-
- private void parseHidservStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.hidservStatsEndMillis = parsedStatsEndData[0];
- this.hidservStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseHidservRendRelayedCellsLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.hidservRendRelayedCells = Double.parseDouble(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hidservRendRelayedCellsParameters =
- ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
- partsNoOpt, 2);
- }
-
- private void parseHidservDirOnionsSeenLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.hidservDirOnionsSeen = Double.parseDouble(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hidservDirOnionsSeenParameters =
- ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
- partsNoOpt, 2);
- }
-
- private void parseRouterSignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("router-signature")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseRouterDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseIdentityEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseIdentityEd25519CryptoBlock(String cryptoString)
- throws DescriptorParseException {
- String masterKeyEd25519FromIdentityEd25519 =
- ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
- cryptoString);
- if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
- masterKeyEd25519FromIdentityEd25519)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
- }
-
- private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
- if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
- masterKeyEd25519FromMasterKeyEd25519Line)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
- }
-
- private void parseRouterSigEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.routerSignatureEd25519 = partsNoOpt[1];
- }
-
- private void parseRouterDigestSha256Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
- this.extraInfoDigestSha256 = partsNoOpt[1];
- }
-
- private void calculateDigest() throws DescriptorParseException {
- if (this.extraInfoDigest != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "extra-info ";
- String sigToken = "\nrouter-signature\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.extraInfoDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.extraInfoDigest == null) {
- throw new DescriptorParseException("Could not calculate extra-info "
- + "descriptor digest.");
- }
- }
-
- private void calculateDigestSha256() throws DescriptorParseException {
- if (this.extraInfoDigestSha256 != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest-sha256" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "extra-info ";
- String sigToken = "\n-----END SIGNATURE-----\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
- 0, sig - start);
- this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.extraInfoDigestSha256 == null) {
- throw new DescriptorParseException("Could not calculate extra-info "
- + "descriptor SHA-256 digest.");
- }
- }
-
- private String extraInfoDigest;
- @Override
- public String getExtraInfoDigest() {
- return this.extraInfoDigest;
- }
-
- private String extraInfoDigestSha256;
- @Override
- public String getExtraInfoDigestSha256() {
- return this.extraInfoDigestSha256;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private BandwidthHistory readHistory;
- @Override
- public BandwidthHistory getReadHistory() {
- return this.readHistory;
- }
-
- private BandwidthHistory writeHistory;
- @Override
- public BandwidthHistory getWriteHistory() {
- return this.writeHistory;
- }
-
- private String geoipDbDigest;
- @Override
- public String getGeoipDbDigest() {
- return this.geoipDbDigest;
- }
-
- private String geoip6DbDigest;
- @Override
- public String getGeoip6DbDigest() {
- return this.geoip6DbDigest;
- }
-
- private long dirreqStatsEndMillis = -1L;
- @Override
- public long getDirreqStatsEndMillis() {
- return this.dirreqStatsEndMillis;
- }
-
- private long dirreqStatsIntervalLength = -1L;
- @Override
- public long getDirreqStatsIntervalLength() {
- return this.dirreqStatsIntervalLength;
- }
-
- private String dirreqV2Ips;
- @Override
- public SortedMap<String, Integer> getDirreqV2Ips() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Ips);
- }
-
- private String dirreqV3Ips;
- @Override
- public SortedMap<String, Integer> getDirreqV3Ips() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Ips);
- }
-
- private String dirreqV2Reqs;
- @Override
- public SortedMap<String, Integer> getDirreqV2Reqs() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Reqs);
- }
-
- private String dirreqV3Reqs;
- @Override
- public SortedMap<String, Integer> getDirreqV3Reqs() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Reqs);
- }
-
- private double dirreqV2Share = -1.0;
- @Override
- public double getDirreqV2Share() {
- return this.dirreqV2Share;
- }
-
- private double dirreqV3Share = -1.0;
- @Override
- public double getDirreqV3Share() {
- return this.dirreqV3Share;
- }
-
- private String dirreqV2Resp;
- @Override
- public SortedMap<String, Integer> getDirreqV2Resp() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Resp);
- }
-
- private String dirreqV3Resp;
- @Override
- public SortedMap<String, Integer> getDirreqV3Resp() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Resp);
- }
-
- private String dirreqV2DirectDl;
- @Override
- public SortedMap<String, Integer> getDirreqV2DirectDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2DirectDl);
- }
-
- private String dirreqV3DirectDl;
- @Override
- public SortedMap<String, Integer> getDirreqV3DirectDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3DirectDl);
- }
-
- private String dirreqV2TunneledDl;
- @Override
- public SortedMap<String, Integer> getDirreqV2TunneledDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2TunneledDl);
- }
-
- private String dirreqV3TunneledDl;
- @Override
- public SortedMap<String, Integer> getDirreqV3TunneledDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3TunneledDl);
- }
-
- private BandwidthHistory dirreqReadHistory;
- @Override
- public BandwidthHistory getDirreqReadHistory() {
- return this.dirreqReadHistory;
- }
-
- private BandwidthHistory dirreqWriteHistory;
- @Override
- public BandwidthHistory getDirreqWriteHistory() {
- return this.dirreqWriteHistory;
- }
-
- private long entryStatsEndMillis = -1L;
- @Override
- public long getEntryStatsEndMillis() {
- return this.entryStatsEndMillis;
- }
-
- private long entryStatsIntervalLength = -1L;
- @Override
- public long getEntryStatsIntervalLength() {
- return this.entryStatsIntervalLength;
- }
-
- private String entryIps;
- @Override
- public SortedMap<String, Integer> getEntryIps() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.entryIps);
- }
-
- private long cellStatsEndMillis = -1L;
- @Override
- public long getCellStatsEndMillis() {
- return this.cellStatsEndMillis;
- }
-
- private long cellStatsIntervalLength = -1L;
- @Override
- public long getCellStatsIntervalLength() {
- return this.cellStatsIntervalLength;
- }
-
- private Integer[] cellProcessedCells;
- @Override
- public List<Integer> getCellProcessedCells() {
- return this.cellProcessedCells == null ? null :
- Arrays.asList(this.cellProcessedCells);
- }
-
- private Double[] cellQueuedCells;
- @Override
- public List<Double> getCellQueuedCells() {
- return this.cellQueuedCells == null ? null :
- Arrays.asList(this.cellQueuedCells);
- }
-
- private Integer[] cellTimeInQueue;
- @Override
- public List<Integer> getCellTimeInQueue() {
- return this.cellTimeInQueue == null ? null :
- Arrays.asList(this.cellTimeInQueue);
- }
-
- private int cellCircuitsPerDecile = -1;
- @Override
- public int getCellCircuitsPerDecile() {
- return this.cellCircuitsPerDecile;
- }
-
- private long connBiDirectStatsEndMillis = -1L;
- @Override
- public long getConnBiDirectStatsEndMillis() {
- return this.connBiDirectStatsEndMillis;
- }
-
- private long connBiDirectStatsIntervalLength = -1L;
- @Override
- public long getConnBiDirectStatsIntervalLength() {
- return this.connBiDirectStatsIntervalLength;
- }
-
- private int connBiDirectBelow = -1;
- @Override
- public int getConnBiDirectBelow() {
- return this.connBiDirectBelow;
- }
-
- private int connBiDirectRead = -1;
- @Override
- public int getConnBiDirectRead() {
- return this.connBiDirectRead;
- }
-
- private int connBiDirectWrite = -1;
- @Override
- public int getConnBiDirectWrite() {
- return this.connBiDirectWrite;
- }
-
- private int connBiDirectBoth = -1;
- @Override
- public int getConnBiDirectBoth() {
- return this.connBiDirectBoth;
- }
-
- private long exitStatsEndMillis = -1L;
- @Override
- public long getExitStatsEndMillis() {
- return this.exitStatsEndMillis;
- }
-
- private long exitStatsIntervalLength = -1L;
- @Override
- public long getExitStatsIntervalLength() {
- return this.exitStatsIntervalLength;
- }
-
- private SortedMap<String, Long> exitKibibytesWritten;
- @Override
- public SortedMap<String, Long> getExitKibibytesWritten() {
- return this.exitKibibytesWritten == null ? null :
- new TreeMap<>(this.exitKibibytesWritten);
- }
-
- private SortedMap<String, Long> exitKibibytesRead;
- @Override
- public SortedMap<String, Long> getExitKibibytesRead() {
- return this.exitKibibytesRead == null ? null :
- new TreeMap<>(this.exitKibibytesRead);
- }
-
- private SortedMap<String, Long> exitStreamsOpened;
- @Override
- public SortedMap<String, Long> getExitStreamsOpened() {
- return this.exitStreamsOpened == null ? null :
- new TreeMap<>(this.exitStreamsOpened);
- }
-
- private long geoipStartTimeMillis = -1L;
- @Override
- public long getGeoipStartTimeMillis() {
- return this.geoipStartTimeMillis;
- }
-
- private String geoipClientOrigins;
- @Override
- public SortedMap<String, Integer> getGeoipClientOrigins() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.geoipClientOrigins);
- }
-
- private long bridgeStatsEndMillis = -1L;
- @Override
- public long getBridgeStatsEndMillis() {
- return this.bridgeStatsEndMillis;
- }
-
- private long bridgeStatsIntervalLength = -1L;
- @Override
- public long getBridgeStatsIntervalLength() {
- return this.bridgeStatsIntervalLength;
- }
-
- private String bridgeIps;
- @Override
- public SortedMap<String, Integer> getBridgeIps() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIps);
- }
-
- private String bridgeIpVersions;
- @Override
- public SortedMap<String, Integer> getBridgeIpVersions() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIpVersions);
- }
-
- private String bridgeIpTransports;
- @Override
- public SortedMap<String, Integer> getBridgeIpTransports() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIpTransports);
- }
-
- private List<String> transports = new ArrayList<>();
- @Override
- public List<String> getTransports() {
- return new ArrayList<>(this.transports);
- }
-
- private long hidservStatsEndMillis = -1L;
- @Override
- public long getHidservStatsEndMillis() {
- return this.hidservStatsEndMillis;
- }
-
- private long hidservStatsIntervalLength = -1L;
- @Override
- public long getHidservStatsIntervalLength() {
- return this.hidservStatsIntervalLength;
- }
-
- private Double hidservRendRelayedCells;
- @Override
- public Double getHidservRendRelayedCells() {
- return this.hidservRendRelayedCells;
- }
-
- private Map<String, Double> hidservRendRelayedCellsParameters;
- @Override
- public Map<String, Double> getHidservRendRelayedCellsParameters() {
- return this.hidservRendRelayedCellsParameters == null ? null :
- new HashMap<>(this.hidservRendRelayedCellsParameters);
- }
-
- private Double hidservDirOnionsSeen;
- @Override
- public Double getHidservDirOnionsSeen() {
- return this.hidservDirOnionsSeen;
- }
-
- private Map<String, Double> hidservDirOnionsSeenParameters;
- @Override
- public Map<String, Double> getHidservDirOnionsSeenParameters() {
- return this.hidservDirOnionsSeenParameters == null ? null :
- new HashMap<>(this.hidservDirOnionsSeenParameters);
- }
-
- private String routerSignature;
- @Override
- public String getRouterSignature() {
- return this.routerSignature;
- }
-
- private String identityEd25519;
- @Override
- public String getIdentityEd25519() {
- return this.identityEd25519;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-
- private String routerSignatureEd25519;
- @Override
- public String getRouterSignatureEd25519() {
- return this.routerSignatureEd25519;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
deleted file mode 100644
index 4931c31..0000000
--- a/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/* Copyright 2014--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.Microdescriptor;
-
-/* Contains a microdescriptor. */
-public class MicrodescriptorImpl extends DescriptorImpl
- implements Microdescriptor {
-
- protected static List<Microdescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<Microdescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "onion-key\n");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- Microdescriptor parsedDescriptor =
- new MicrodescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected MicrodescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- this.calculateDigest();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
- "onion-key".split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "ntor-onion-key,family,p,p6,id").split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("onion-key");
- this.clearParsedKeywords();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) {
- continue;
- }
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "onion-key":
- this.parseOnionKeyLine(line, parts);
- nextCrypto = "onion-key";
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, parts);
- break;
- case "a":
- this.parseALine(line, parts);
- break;
- case "family":
- this.parseFamilyLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "p6":
- this.parseP6Line(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("onion-key")) {
- this.onionKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in microdescriptor.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- ParseHelper.parseKeyword(line, parts[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in microdescriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseOnionKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("onion-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.ntorOnionKey = parts[1].replaceAll("=", "");
- }
-
- private void parseALine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(parts[1]);
- }
-
- private void parseFamilyLine(String line, String[] parts)
- throws DescriptorParseException {
- String[] familyEntries = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- if (parts[i].startsWith("$")) {
- if (parts[i].contains("=") ^ parts[i].contains("~")) {
- String separator = parts[i].contains("=") ? "=" : "~";
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[i].substring(1, parts[i].indexOf(separator)));
- String nickname = ParseHelper.parseNickname(line,
- parts[i].substring(parts[i].indexOf(separator) + 1));
- familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
- } else {
- familyEntries[i - 1] = "$"
- + ParseHelper.parseTwentyByteHexString(line,
- parts[i].substring(1));
- }
- } else {
- familyEntries[i - 1] = ParseHelper.parseNickname(line, parts[i]);
- }
- }
- this.familyEntries = familyEntries;
- }
-
- private void parsePLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validatePOrP6Line(line, parts);
- this.defaultPolicy = parts[1];
- this.portList = parts[2];
- }
-
- private void parseP6Line(String line, String[] parts)
- throws DescriptorParseException {
- this.validatePOrP6Line(line, parts);
- this.ipv6DefaultPolicy = parts[1];
- this.ipv6PortList = parts[2];
- }
-
- private void validatePOrP6Line(String line, String[] parts)
- throws DescriptorParseException {
- boolean isValid = true;
- if (parts.length != 3) {
- isValid = false;
- } else {
- switch (parts[1]) {
- case "accept":
- case "reject":
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseIdLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else {
- switch (parts[1]) {
- case "ed25519":
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
- this.ed25519Identity = parts[2];
- break;
- case "rsa1024":
- ParseHelper.parseTwentyByteBase64String(line, parts[2]);
- this.rsa1024Identity = parts[2];
- break;
- default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "onion-key\n";
- int start = ascii.indexOf(startToken);
- int end = ascii.length();
- if (start >= 0 && end > start) {
- byte[] forDigest = new byte[end - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, end - start);
- this.microdescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.microdescriptorDigest == null) {
- throw new DescriptorParseException("Could not calculate "
- + "microdescriptor digest.");
- }
- }
-
- private String microdescriptorDigest;
- @Override
- public String getMicrodescriptorDigest() {
- return this.microdescriptorDigest;
- }
-
- private String onionKey;
- @Override
- public String getOnionKey() {
- return this.onionKey;
- }
-
- private String ntorOnionKey;
- @Override
- public String getNtorOnionKey() {
- return this.ntorOnionKey;
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private String[] familyEntries;
- @Override
- public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
- }
- private String defaultPolicy;
- @Override
- public String getDefaultPolicy() {
- return this.defaultPolicy;
- }
-
- private String portList;
- @Override
- public String getPortList() {
- return this.portList;
- }
-
- private String ipv6DefaultPolicy;
- @Override
- public String getIpv6DefaultPolicy() {
- return this.ipv6DefaultPolicy;
- }
-
- private String ipv6PortList;
- @Override
- public String getIpv6PortList() {
- return this.ipv6PortList;
- }
-
- private String rsa1024Identity;
- @Override
- public String getRsa1024Identity() {
- return this.rsa1024Identity;
- }
-
- private String ed25519Identity;
- @Override
- public String getEd25519Identity() {
- return this.ed25519Identity;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
deleted file mode 100644
index b73d211..0000000
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.NetworkStatusEntry;
-
-public class NetworkStatusEntryImpl implements NetworkStatusEntry {
-
- private byte[] statusEntryBytes;
- @Override
- public byte[] getStatusEntryBytes() {
- return this.statusEntryBytes;
- }
-
- private boolean microdescConsensus;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected NetworkStatusEntryImpl(byte[] statusEntryBytes,
- boolean microdescConsensus, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.statusEntryBytes = statusEntryBytes;
- this.microdescConsensus = microdescConsensus;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseStatusEntryBytes();
- this.clearAtMostOnceKeywords();
- }
-
- private SortedSet<String> atMostOnceKeywords;
- private void initializeKeywords() {
- this.atMostOnceKeywords = new TreeSet<>();
- this.atMostOnceKeywords.add("s");
- this.atMostOnceKeywords.add("v");
- this.atMostOnceKeywords.add("w");
- this.atMostOnceKeywords.add("p");
- }
-
- private void parsedAtMostOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.atMostOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in status entry.");
- }
- this.atMostOnceKeywords.remove(keyword);
- }
-
- private void parseStatusEntryBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.statusEntryBytes)).
- useDelimiter("\n");
- String line = null;
- if (!s.hasNext() || !(line = s.next()).startsWith("r ")) {
- throw new DescriptorParseException("Status entry must start with "
- + "an r line.");
- }
- String[] rLineParts = line.split("[ \t]+");
- this.parseRLine(line, rLineParts);
- while (s.hasNext()) {
- line = s.next();
- String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") :
- line.substring("opt ".length()).split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "a":
- this.parseALine(line, parts);
- break;
- case "s":
- this.parseSLine(line, parts);
- break;
- case "v":
- this.parseVLine(line, parts);
- break;
- case "w":
- this.parseWLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "m":
- this.parseMLine(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in status entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseRLine(String line, String[] parts)
- throws DescriptorParseException {
- if ((!this.microdescConsensus && parts.length != 9) ||
- (this.microdescConsensus && parts.length != 8)) {
- throw new DescriptorParseException("r line '" + line + "' has "
- + "fewer space-separated elements than expected.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- this.fingerprint = ParseHelper.parseTwentyByteBase64String(line,
- parts[2]);
- int descriptorOffset = 0;
- if (!this.microdescConsensus) {
- this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
- parts[3]);
- descriptorOffset = 1;
- }
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 3 + descriptorOffset, 4 + descriptorOffset);
- this.address = ParseHelper.parseIpv4Address(line,
- parts[5 + descriptorOffset]);
- this.orPort = ParseHelper.parsePort(line,
- parts[6 + descriptorOffset]);
- this.dirPort = ParseHelper.parsePort(line,
- parts[7 + descriptorOffset]);
- }
-
- private void parseALine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "status entry.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(parts[1]);
- }
-
- private static Map<String, Integer> flagIndexes = new HashMap<>();
- private static Map<Integer, String> flagStrings = new HashMap<>();
-
- private void parseSLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("s");
- BitSet flags = new BitSet(flagIndexes.size());
- for (int i = 1; i < parts.length; i++) {
- String flag = parts[i];
- if (!flagIndexes.containsKey(flag)) {
- flagStrings.put(flagIndexes.size(), flag);
- flagIndexes.put(flag, flagIndexes.size());
- }
- flags.set(flagIndexes.get(flag));
- }
- this.flags = flags;
- }
-
- private void parseVLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("v");
- String noOptLine = line;
- if (noOptLine.startsWith("opt ")) {
- noOptLine = noOptLine.substring(4);
- }
- if (noOptLine.length() < 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "status entry.");
- } else {
- this.version = noOptLine.substring(2);
- }
- }
-
- private void parseWLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("w");
- SortedMap<String, Integer> pairs =
- ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "=");
- if (pairs.isEmpty()) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- if (pairs.containsKey("Bandwidth")) {
- this.bandwidth = pairs.remove("Bandwidth");
- }
- if (pairs.containsKey("Measured")) {
- this.measured = pairs.remove("Measured");
- }
- if (pairs.containsKey("Unmeasured")) {
- this.unmeasured = pairs.remove("Unmeasured") == 1L;
- }
- if (!pairs.isEmpty()) {
- /* Ignore unknown key-value pair. */
- }
- }
-
- private void parsePLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("p");
- boolean isValid = true;
- if (parts.length != 3) {
- isValid = false;
- } else {
- switch (parts[1]) {
- case "accept":
- case "reject":
- this.defaultPolicy = parts[1];
- this.portList = parts[2];
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseMLine(String line, String[] parts)
- throws DescriptorParseException {
- if (this.microdescriptorDigests == null) {
- this.microdescriptorDigests = new HashSet<>();
- }
- if (parts.length == 2) {
- this.microdescriptorDigests.add(
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[1]));
- } else if (parts.length == 3 && parts[2].length() > 7) {
- /* 7 == "sha256=".length() */
- this.microdescriptorDigests.add(
- ParseHelper.parseThirtyTwoByteBase64String(line,
- parts[2].substring(7)));
- }
- }
-
- private void parseIdLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3 || !"ed25519".equals(parts[1])) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else if ("none".equals(parts[2])) {
- this.masterKeyEd25519 = "none";
- } else {
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
- this.masterKeyEd25519 = parts[2];
- }
- }
-
- private void clearAtMostOnceKeywords() {
- this.atMostOnceKeywords = null;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String descriptor;
- @Override
- public String getDescriptor() {
- return this.descriptor;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private Set<String> microdescriptorDigests;
- @Override
- public Set<String> getMicrodescriptorDigests() {
- return this.microdescriptorDigests == null ? null :
- new HashSet<>(this.microdescriptorDigests);
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private BitSet flags;
- @Override
- public SortedSet<String> getFlags() {
- SortedSet<String> result = new TreeSet<>();
- if (this.flags != null) {
- for (int i = this.flags.nextSetBit(0); i >= 0;
- i = this.flags.nextSetBit(i + 1)) {
- result.add(flagStrings.get(i));
- }
- }
- return result;
- }
-
- private String version;
- @Override
- public String getVersion() {
- return this.version;
- }
-
- private long bandwidth = -1L;
- @Override
- public long getBandwidth() {
- return this.bandwidth;
- }
-
- private long measured = -1L;
- @Override
- public long getMeasured() {
- return this.measured;
- }
-
- private boolean unmeasured = false;
- @Override
- public boolean getUnmeasured() {
- return this.unmeasured;
- }
-
- private String defaultPolicy;
- @Override
- public String getDefaultPolicy() {
- return this.defaultPolicy;
- }
-
- private String portList;
- @Override
- public String getPortList() {
- return this.portList;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
deleted file mode 100644
index 5fa22c7..0000000
--- a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.DirectorySignature;
-import org.torproject.descriptor.NetworkStatusEntry;
-
-/* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
- * consensuses, v2 statuses, and sanitized bridge network statuses and
- * delegate the specific parts to the subclasses. */
-public abstract class NetworkStatusImpl extends DescriptorImpl {
-
- protected NetworkStatusImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines,
- boolean containsDirSourceEntries, boolean blankLinesAllowed)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines,
- blankLinesAllowed);
- this.splitAndParseParts(this.rawDescriptorBytes,
- containsDirSourceEntries);
- }
-
- private void splitAndParseParts(byte[] rawDescriptorBytes,
- boolean containsDirSourceEntries) throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- int startIndex = 0;
- int firstDirSourceIndex = !containsDirSourceEntries ? -1 :
- this.findFirstIndexOfKeyword(descriptorString, "dir-source");
- int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r");
- int directoryFooterIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-footer");
- int firstDirectorySignatureIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-signature");
- int endIndex = descriptorString.length();
- if (firstDirectorySignatureIndex < 0) {
- firstDirectorySignatureIndex = endIndex;
- }
- if (directoryFooterIndex < 0) {
- directoryFooterIndex = firstDirectorySignatureIndex;
- }
- if (firstRIndex < 0) {
- firstRIndex = directoryFooterIndex;
- }
- if (firstDirSourceIndex < 0) {
- firstDirSourceIndex = firstRIndex;
- }
- if (firstDirSourceIndex > startIndex) {
- this.parseHeaderBytes(descriptorString, startIndex,
- firstDirSourceIndex);
- }
- if (firstRIndex > firstDirSourceIndex) {
- this.parseDirSourceBytes(descriptorString, firstDirSourceIndex,
- firstRIndex);
- }
- if (directoryFooterIndex > firstRIndex) {
- this.parseStatusEntryBytes(descriptorString, firstRIndex,
- directoryFooterIndex);
- }
- if (firstDirectorySignatureIndex > directoryFooterIndex) {
- this.parseDirectoryFooterBytes(descriptorString,
- directoryFooterIndex, firstDirectorySignatureIndex);
- }
- if (endIndex > firstDirectorySignatureIndex) {
- this.parseDirectorySignatureBytes(descriptorString,
- firstDirectorySignatureIndex, endIndex);
- }
- }
-
- private int findFirstIndexOfKeyword(String descriptorString,
- String keyword) {
- if (descriptorString.startsWith(keyword)) {
- return 0;
- } else if (descriptorString.contains("\n" + keyword + " ")) {
- return descriptorString.indexOf("\n" + keyword + " ") + 1;
- } else if (descriptorString.contains("\n" + keyword + "\n")) {
- return descriptorString.indexOf("\n" + keyword + "\n") + 1;
- } else {
- return -1;
- }
- }
-
- private void parseHeaderBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- byte[] headerBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- headerBytes, 0, end - start);
- this.parseHeader(headerBytes);
- }
-
- private void parseDirSourceBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- List<byte[]> splitDirSourceBytes =
- this.splitByKeyword(descriptorString, "dir-source", start, end);
- for (byte[] dirSourceBytes : splitDirSourceBytes) {
- this.parseDirSource(dirSourceBytes);
- }
- }
-
- private void parseStatusEntryBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- List<byte[]> splitStatusEntryBytes =
- this.splitByKeyword(descriptorString, "r", start, end);
- for (byte[] statusEntryBytes : splitStatusEntryBytes) {
- this.parseStatusEntry(statusEntryBytes);
- }
- }
-
- private void parseDirectoryFooterBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- byte[] directoryFooterBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- directoryFooterBytes, 0, end - start);
- this.parseFooter(directoryFooterBytes);
- }
-
- private void parseDirectorySignatureBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
- descriptorString, "directory-signature", start, end);
- for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
- this.parseDirectorySignature(directorySignatureBytes);
- }
- }
-
- private List<byte[]> splitByKeyword(String descriptorString,
- String keyword, int start, int end) {
- List<byte[]> splitParts = new ArrayList<>();
- int from = start;
- while (from < end) {
- int to = descriptorString.indexOf("\n" + keyword + " ", from);
- if (to < 0) {
- to = descriptorString.indexOf("\n" + keyword + "\n", from);
- }
- if (to < 0) {
- to = end;
- } else {
- to += 1;
- }
- byte[] part = new byte[to - from];
- System.arraycopy(this.rawDescriptorBytes, from, part, 0,
- to - from);
- from = to;
- splitParts.add(part);
- }
- return splitParts;
- }
-
- protected abstract void parseHeader(byte[] headerBytes)
- throws DescriptorParseException;
-
- protected void parseDirSource(byte[] dirSourceBytes)
- throws DescriptorParseException {
- DirSourceEntryImpl dirSourceEntry = new DirSourceEntryImpl(
- dirSourceBytes, this.failUnrecognizedDescriptorLines);
- this.dirSourceEntries.put(dirSourceEntry.getIdentity(),
- dirSourceEntry);
- List<String> unrecognizedDirSourceLines = dirSourceEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedDirSourceLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedDirSourceLines);
- }
- }
-
- protected String[] parseClientOrServerVersions(String line,
- String[] parts) throws DescriptorParseException {
- String[] result = null;
- switch (parts.length) {
- case 1:
- result = new String[0];
- break;
- case 2:
- result = parts[1].split(",", -1);
- for (String version : result) {
- if (version.length() < 1) {
- throw new DescriptorParseException("Illegal versions line '"
- + line + "'.");
- }
- }
- break;
- default:
- throw new DescriptorParseException("Illegal versions line '" + line
- + "'.");
- }
- return result;
- }
-
- protected void parseStatusEntry(byte[] statusEntryBytes)
- throws DescriptorParseException {
- NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
- statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
- this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected abstract void parseFooter(byte[] footerBytes)
- throws DescriptorParseException;
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- if (this.signatures == null) {
- this.signatures = new ArrayList<>();
- }
- DirectorySignatureImpl signature = new DirectorySignatureImpl(
- directorySignatureBytes, failUnrecognizedDescriptorLines);
- this.signatures.add(signature);
- List<String> unrecognizedStatusEntryLines = signature.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected SortedMap<String, DirSourceEntry> dirSourceEntries =
- new TreeMap<>();
- public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
- return new TreeMap<>(this.dirSourceEntries);
- }
-
- protected SortedMap<String, NetworkStatusEntry> statusEntries =
- new TreeMap<>();
- public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
- return new TreeMap<>(this.statusEntries);
- }
- public boolean containsStatusEntry(String fingerprint) {
- return this.statusEntries.containsKey(fingerprint);
- }
- public NetworkStatusEntry getStatusEntry(String fingerprint) {
- return this.statusEntries.get(fingerprint);
- }
-
- protected List<DirectorySignature> signatures;
- public List<DirectorySignature> getSignatures() {
- return this.signatures == null ? null
- : new ArrayList<>(this.signatures);
- }
- public SortedMap<String, DirectorySignature> getDirectorySignatures() {
- SortedMap<String, DirectorySignature> directorySignatures = null;
- if (this.signatures != null) {
- directorySignatures = new TreeMap<>();
- for (DirectorySignature signature : this.signatures) {
- directorySignatures.put(signature.getIdentity(), signature);
- }
- }
- return directorySignatures;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
deleted file mode 100644
index 82c0813..0000000
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.regex.Pattern;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-
-public class ParseHelper {
-
- private static Pattern keywordPattern =
- Pattern.compile("^[A-Za-z0-9-]+$");
- protected static String parseKeyword(String line, String keyword)
- throws DescriptorParseException {
- if (!keywordPattern.matcher(keyword).matches()) {
- throw new DescriptorParseException("Unrecognized character in "
- + "keyword '" + keyword + "' in line '" + line + "'.");
- }
- return keyword;
- }
-
- private static Pattern ipv4Pattern =
- Pattern.compile("^[0-9\\.]{7,15}$");
- protected static String parseIpv4Address(String line, String address)
- throws DescriptorParseException {
- boolean isValid = true;
- if (!ipv4Pattern.matcher(address).matches()) {
- isValid = false;
- } else {
- String[] parts = address.split("\\.", -1);
- if (parts.length != 4) {
- isValid = false;
- } else {
- for (int i = 0; i < 4; i++) {
- try {
- int octetValue = Integer.parseInt(parts[i]);
- if (octetValue < 0 || octetValue > 255) {
- isValid = false;
- }
- } catch (NumberFormatException e) {
- isValid = false;
- }
- }
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("'" + address + "' in line '"
- + line + "' is not a valid IPv4 address.");
- }
- return address;
- }
-
- protected static int parsePort(String line, String portString)
- throws DescriptorParseException {
- int port = -1;
- try {
- port = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("'" + portString + "' in line '"
- + line + "' is not a valid port number.");
- }
- if (port < 0 || port > 65535) {
- throw new DescriptorParseException("'" + portString + "' in line '"
- + line + "' is not a valid port number.");
- }
- return port;
- }
-
- protected static long parseSeconds(String line, String secondsString)
- throws DescriptorParseException {
- try {
- return Long.parseLong(secondsString);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("'" + secondsString + "' in "
- + "line '" + line + "' is not a valid time in seconds.");
- }
- }
-
- protected static String parseExitPattern(String line, String exitPattern)
- throws DescriptorParseException {
- if (!exitPattern.contains(":")) {
- throw new DescriptorParseException("'" + exitPattern + "' in line '"
- + line + "' must contain address and port.");
- }
- String[] parts = exitPattern.split(":");
- String addressPart = parts[0];
- /* TODO Extend to IPv6. */
- if (addressPart.equals("*")) {
- /* Nothing to check. */
- } else if (addressPart.contains("/")) {
- String[] addressParts = addressPart.split("/");
- String address = addressParts[0];
- String mask = addressParts[1];
- ParseHelper.parseIpv4Address(line, address);
- if (addressParts.length != 2) {
- throw new DescriptorParseException("'" + addressPart + "' in "
- + "line '" + line + "' is not a valid address part.");
- }
- if (mask.contains(".")) {
- ParseHelper.parseIpv4Address(line, mask);
- } else {
- int maskValue = -1;
- try {
- maskValue = Integer.parseInt(mask);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (maskValue < 0 || maskValue > 32) {
- throw new DescriptorParseException("'" + mask + "' in line '"
- + line + "' is not a valid IPv4 mask.");
- }
- }
- } else {
- ParseHelper.parseIpv4Address(line, addressPart);
- }
- String portPart = parts[1];
- if (portPart.equals("*")) {
- /* Nothing to check. */
- } else if (portPart.contains("-")) {
- String[] portParts = portPart.split("-");
- String fromPort = portParts[0];
- ParseHelper.parsePort(line, fromPort);
- String toPort = portParts[1];
- ParseHelper.parsePort(line, toPort);
- } else {
- ParseHelper.parsePort(line, portPart);
- }
- return exitPattern;
- }
-
- private static ThreadLocal<Map<String, DateFormat>> dateFormats =
- new ThreadLocal<Map<String, DateFormat>> () {
- public Map<String, DateFormat> get() {
- return super.get();
- }
- protected Map<String, DateFormat> initialValue() {
- return new HashMap<>();
- }
- public void remove() {
- super.remove();
- }
- public void set(Map<String, DateFormat> value) {
- super.set(value);
- }
- };
- static DateFormat getDateFormat(String format) {
- Map<String, DateFormat> threadDateFormats = dateFormats.get();
- if (!threadDateFormats.containsKey(format)) {
- DateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
- dateFormat.setLenient(false);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- threadDateFormats.put(format, dateFormat);
- }
- return threadDateFormats.get(format);
- }
-
- protected static long parseTimestampAtIndex(String line, String[] parts,
- int dateIndex, int timeIndex) throws DescriptorParseException {
- if (dateIndex >= parts.length || timeIndex >= parts.length) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a timestamp at the expected position.");
- }
- long result = -1L;
- try {
- DateFormat dateTimeFormat = getDateFormat("yyyy-MM-dd HH:mm:ss");
- result = dateTimeFormat.parse(
- parts[dateIndex] + " " + parts[timeIndex]).getTime();
- } catch (ParseException e) {
- /* Leave result at -1L. */
- }
- if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
- throw new DescriptorParseException("Illegal timestamp format in "
- + "line '" + line + "'.");
- }
- return result;
- }
-
- protected static long parseDateAtIndex(String line, String[] parts,
- int dateIndex) throws DescriptorParseException {
- if (dateIndex >= parts.length) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a date at the expected position.");
- }
- long result = -1L;
- try {
- DateFormat dateFormat = getDateFormat("yyyy-MM-dd");
- result = dateFormat.parse(parts[dateIndex]).getTime();
- } catch (ParseException e) {
- /* Leave result at -1L. */
- }
- if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
- throw new DescriptorParseException("Illegal date format in line '"
- + line + "'.");
- }
- return result;
- }
-
- protected static String parseTwentyByteHexString(String line,
- String hexString) throws DescriptorParseException {
- return parseHexString(line, hexString, 40);
- }
-
- protected static String parseHexString(String line, String hexString)
- throws DescriptorParseException {
- return parseHexString(line, hexString, -1);
- }
-
- private static Pattern hexPattern = Pattern.compile("^[0-9a-fA-F]*$");
- private static String parseHexString(String line, String hexString,
- int expectedLength) throws DescriptorParseException {
- if (!hexPattern.matcher(hexString).matches() ||
- hexString.length() % 2 != 0 ||
- (expectedLength >= 0 && hexString.length() != expectedLength)) {
- throw new DescriptorParseException("Illegal hex string in line '"
- + line + "'.");
- }
- return hexString.toUpperCase();
- }
-
- protected static SortedMap<String, String> parseKeyValueStringPairs(
- String line, String[] parts, int startIndex, String separatorString)
- throws DescriptorParseException {
- SortedMap<String, String> result = new TreeMap<>();
- for (int i = startIndex; i < parts.length; i++) {
- String pair = parts[i];
- String[] pairParts = pair.split(separatorString);
- if (pairParts.length != 2) {
- throw new DescriptorParseException("Illegal key-value pair in "
- + "line '" + line + "'.");
- }
- result.put(pairParts[0], pairParts[1]);
- }
- return result;
- }
-
- protected static SortedMap<String, Integer> parseKeyValueIntegerPairs(
- String line, String[] parts, int startIndex, String separatorString)
- throws DescriptorParseException {
- SortedMap<String, Integer> result = new TreeMap<>();
- SortedMap<String, String> keyValueStringPairs =
- ParseHelper.parseKeyValueStringPairs(line, parts, startIndex,
- separatorString);
- for (Map.Entry<String, String> e : keyValueStringPairs.entrySet()) {
- try {
- result.put(e.getKey(), Integer.parseInt(e.getValue()));
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
- return result;
- }
-
- private static Pattern nicknamePattern =
- Pattern.compile("^[0-9a-zA-Z]{1,19}$");
- protected static String parseNickname(String line, String nickname)
- throws DescriptorParseException {
- if (!nicknamePattern.matcher(nickname).matches()) {
- throw new DescriptorParseException("Illegal nickname in line '"
- + line + "'.");
- }
- return nickname;
- }
-
- protected static boolean parseBoolean(String b, String line)
- throws DescriptorParseException {
- switch (b) {
- case "1":
- return true;
- case "0":
- return false;
- default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private static Pattern twentyByteBase64Pattern =
- Pattern.compile("^[0-9a-zA-Z+/]{27}$");
- protected static String parseTwentyByteBase64String(String line,
- String base64String) throws DescriptorParseException {
- if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
- throw new DescriptorParseException("'" + base64String
- + "' in line '" + line + "' is not a valid base64-encoded "
- + "20-byte value.");
- }
- return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
- }
-
- private static Pattern thirtyTwoByteBase64Pattern =
- Pattern.compile("^[0-9a-zA-Z+/]{43}$");
- protected static String parseThirtyTwoByteBase64String(String line,
- String base64String) throws DescriptorParseException {
- if (!thirtyTwoByteBase64Pattern.matcher(base64String).matches()) {
- throw new DescriptorParseException("'" + base64String
- + "' in line '" + line + "' is not a valid base64-encoded "
- + "32-byte value.");
- }
- return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
- }
-
- private static Map<Integer, Pattern>
- commaSeparatedKeyValueListPatterns = new HashMap<>();
- protected static String parseCommaSeparatedKeyIntegerValueList(
- String line, String[] partsNoOpt, int index, int keyLength)
- throws DescriptorParseException {
- String result = "";
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a key-value list at index " + index + ".");
- } else if (partsNoOpt.length > index + 1 ) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected key-value list at "
- + "index " + index + ".");
- } else if (partsNoOpt.length > index) {
- if (!commaSeparatedKeyValueListPatterns.containsKey(keyLength)) {
- String keyPattern = "[0-9a-zA-Z?<>\\-_]"
- + (keyLength == 0 ? "+" : "{" + keyLength + "}");
- String valuePattern = "\\-?[0-9]{1,9}";
- String patternString = String.format("^%s=%s(,%s=%s)*$",
- keyPattern, valuePattern, keyPattern, valuePattern);
- commaSeparatedKeyValueListPatterns.put(keyLength,
- Pattern.compile(patternString));
- }
- Pattern pattern = commaSeparatedKeyValueListPatterns.get(
- keyLength);
- if (pattern.matcher(partsNoOpt[index]).matches()) {
- result = partsNoOpt[index];
- } else {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal key or value.");
- }
- }
- return result;
- }
-
- protected static SortedMap<String, Integer>
- convertCommaSeparatedKeyIntegerValueList(String validatedString) {
- SortedMap<String, Integer> result = null;
- if (validatedString != null) {
- result = new TreeMap<>();
- if (validatedString.contains("=")) {
- for (String listElement : validatedString.split(",", -1)) {
- String[] keyAndValue = listElement.split("=");
- result.put(keyAndValue[0], Integer.parseInt(keyAndValue[1]));
- }
- }
- }
- return result;
- }
-
- protected static SortedMap<String, Long>
- parseCommaSeparatedKeyLongValueList(String line,
- String[] partsNoOpt, int index, int keyLength)
- throws DescriptorParseException {
- SortedMap<String, Long> result = new TreeMap<>();
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a key-value list at index " + index + ".");
- } else if (partsNoOpt.length > index + 1 ) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected key-value list at "
- + "index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- for (String listElement : listElements) {
- String[] keyAndValue = listElement.split("=");
- String key = null;
- long value = -1;
- if (keyAndValue.length == 2 && (keyLength == 0 ||
- keyAndValue[0].length() == keyLength)) {
- try {
- value = Long.parseLong(keyAndValue[1]);
- key = keyAndValue[0];
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (key == null) {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal key or value in list element '"
- + listElement + "'.");
- }
- result.put(key, value);
- }
- }
- return result;
- }
-
- protected static Integer[] parseCommaSeparatedIntegerValueList(
- String line, String[] partsNoOpt, int index)
- throws DescriptorParseException {
- Integer[] result = null;
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a comma-separated value list at index " + index
- + ".");
- } else if (partsNoOpt.length > index + 1) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected comma-separated "
- + "value list at index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- result = new Integer[listElements.length];
- for (int i = 0; i < listElements.length; i++) {
- try {
- result[i] = Integer.parseInt(listElements[i]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal value in list element '"
- + listElements[i] + "'.");
- }
- }
- }
- return result;
- }
-
- protected static Double[] parseCommaSeparatedDoubleValueList(
- String line, String[] partsNoOpt, int index)
- throws DescriptorParseException {
- Double[] result = null;
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a comma-separated value list at index " + index
- + ".");
- } else if (partsNoOpt.length > index + 1) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected comma-separated "
- + "value list at index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- result = new Double[listElements.length];
- for (int i = 0; i < listElements.length; i++) {
- try {
- result[i] = Double.parseDouble(listElements[i]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal value in list element '"
- + listElements[i] + "'.");
- }
- }
- }
- return result;
- }
-
- protected static Map<String, Double>
- parseSpaceSeparatedStringKeyDoubleValueMap(String line,
- String[] partsNoOpt, int startIndex)
- throws DescriptorParseException {
- Map<String, Double> result = new LinkedHashMap<>();
- if (partsNoOpt.length < startIndex) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a key-value list starting at index " + startIndex
- + ".");
- }
- for (int i = startIndex; i < partsNoOpt.length; i++) {
- String listElement = partsNoOpt[i];
- String[] keyAndValue = listElement.split("=");
- String key = null;
- Double value = null;
- if (keyAndValue.length == 2) {
- try {
- value = Double.parseDouble(keyAndValue[1]);
- key = keyAndValue[0];
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (key == null) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "an illegal key or value in list element '" + listElement
- + "'.");
- }
- result.put(key, value);
- }
- return result;
- }
-
- protected static String
- parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
- String identityEd25519CryptoBlock) throws DescriptorParseException {
- String identityEd25519CryptoBlockNoNewlines =
- identityEd25519CryptoBlock.replaceAll("\n", "");
- String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
- endEd25519CertLine = "-----END ED25519 CERT-----";
- if (!identityEd25519CryptoBlockNoNewlines.startsWith(
- beginEd25519CertLine)) {
- throw new DescriptorParseException("Illegal start of "
- + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
- + "'.");
- }
- if (!identityEd25519CryptoBlockNoNewlines.endsWith(
- endEd25519CertLine)) {
- throw new DescriptorParseException("Illegal end of "
- + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
- + "'.");
- }
- String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
- substring(beginEd25519CertLine.length(),
- identityEd25519CryptoBlock.length()
- - endEd25519CertLine.length()).replaceAll("=", "");
- byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
- identityEd25519Base64);
- if (identityEd25519.length < 40) {
- throw new DescriptorParseException("Invalid length of "
- + "identity-ed25519 (in bytes): " + identityEd25519.length);
- } else if (identityEd25519[0] != 0x01) {
- throw new DescriptorParseException("Unknown version in "
- + "identity-ed25519: " + identityEd25519[0]);
- } else if (identityEd25519[1] != 0x04) {
- throw new DescriptorParseException("Unknown cert type in "
- + "identity-ed25519: " + identityEd25519[1]);
- } else if (identityEd25519[6] != 0x01) {
- throw new DescriptorParseException("Unknown certified key type in "
- + "identity-ed25519: " + identityEd25519[1]);
- } else if (identityEd25519[39] == 0x00) {
- throw new DescriptorParseException("No extensions in "
- + "identity-ed25519 (which would contain the encoded "
- + "master-key-ed25519): " + identityEd25519[39]);
- } else {
- int extensionStart = 40;
- for (int i = 0; i < (int) identityEd25519[39]; i++) {
- if (identityEd25519.length < extensionStart + 4) {
- throw new DescriptorParseException("Invalid extension with id "
- + i + " in identity-ed25519.");
- }
- int extensionLength = identityEd25519[extensionStart];
- extensionLength <<= 8;
- extensionLength += identityEd25519[extensionStart + 1];
- int extensionType = identityEd25519[extensionStart + 2];
- if (extensionLength == 32 && extensionType == 4) {
- if (identityEd25519.length < extensionStart + 4 + 32) {
- throw new DescriptorParseException("Invalid extension with "
- + "id " + i + " in identity-ed25519.");
- }
- byte[] masterKeyEd25519 = new byte[32];
- System.arraycopy(identityEd25519, extensionStart + 4,
- masterKeyEd25519, 0, masterKeyEd25519.length);
- String masterKeyEd25519Base64 = DatatypeConverter.
- printBase64Binary(masterKeyEd25519).replaceAll("=", "");
- String masterKeyEd25519Base64NoTrailingEqualSigns =
- masterKeyEd25519Base64.replaceAll("=", "");
- return masterKeyEd25519Base64NoTrailingEqualSigns;
- }
- extensionStart += 4 + extensionLength;
- }
- }
- throw new DescriptorParseException("Unable to locate "
- + "master-key-ed25519 in identity-ed25519.");
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java
deleted file mode 100644
index 1ff15cb..0000000
--- a/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayDirectory;
-import org.torproject.descriptor.RouterStatusEntry;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* TODO Write unit tests. */
-
-public class RelayDirectoryImpl extends DescriptorImpl
- implements RelayDirectory {
-
- protected static List<RelayDirectory> parseDirectories(
- byte[] directoriesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayDirectory> parsedDirectories = new ArrayList<>();
- List<byte[]> splitDirectoriesBytes =
- DescriptorImpl.splitRawDescriptorBytes(directoriesBytes,
- "signed-directory\n");
- for (byte[] directoryBytes : splitDirectoriesBytes) {
- RelayDirectory parsedDirectory =
- new RelayDirectoryImpl(directoryBytes,
- failUnrecognizedDescriptorLines);
- parsedDirectories.add(parsedDirectory);
- }
- return parsedDirectories;
- }
-
- protected RelayDirectoryImpl(byte[] directoryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(directoryBytes, failUnrecognizedDescriptorLines, true);
- this.splitAndParseParts(rawDescriptorBytes);
- this.calculateDigest();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "signed-directory,recommended-software,"
- + "directory-signature").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList(
- "dir-signing-key,running-routers,router-status".split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("signed-directory");
- this.clearParsedKeywords();
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "signed-directory\n";
- String sigToken = "\ndirectory-signature ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- sig = ascii.indexOf("\n", sig) + 1;
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.directoryDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.directoryDigest == null) {
- throw new DescriptorParseException("Could not calculate v1 "
- + "directory digest.");
- }
- }
-
- private void splitAndParseParts(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- int startIndex = 0;
- int firstRouterIndex = this.findFirstIndexOfKeyword(descriptorString,
- "router");
- int directorySignatureIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-signature");
- int endIndex = descriptorString.length();
- if (directorySignatureIndex < 0) {
- directorySignatureIndex = endIndex;
- }
- if (firstRouterIndex < 0) {
- firstRouterIndex = directorySignatureIndex;
- }
- if (firstRouterIndex > startIndex) {
- this.parseHeaderBytes(descriptorString, startIndex,
- firstRouterIndex);
- }
- if (directorySignatureIndex > firstRouterIndex) {
- this.parseServerDescriptorBytes(descriptorString, firstRouterIndex,
- directorySignatureIndex);
- }
- if (endIndex > directorySignatureIndex) {
- this.parseDirectorySignatureBytes(descriptorString,
- directorySignatureIndex, endIndex);
- }
- }
-
- private int findFirstIndexOfKeyword(String descriptorString,
- String keyword) {
- if (descriptorString.startsWith(keyword)) {
- return 0;
- } else if (descriptorString.contains("\n" + keyword + " ")) {
- return descriptorString.indexOf("\n" + keyword + " ") + 1;
- } else if (descriptorString.contains("\n" + keyword + "\n")) {
- return descriptorString.indexOf("\n" + keyword + "\n") + 1;
- } else {
- return -1;
- }
- }
-
- private void parseHeaderBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- byte[] headerBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- headerBytes, 0, end - start);
- this.parseHeader(headerBytes);
- }
-
- private void parseServerDescriptorBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitServerDescriptorBytes =
- this.splitByKeyword(descriptorString, "router", start, end);
- for (byte[] statusEntryBytes : splitServerDescriptorBytes) {
- this.parseServerDescriptor(statusEntryBytes);
- }
- }
-
- private void parseDirectorySignatureBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
- descriptorString, "directory-signature", start, end);
- for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
- this.parseDirectorySignature(directorySignatureBytes);
- }
- }
-
- private List<byte[]> splitByKeyword(String descriptorString,
- String keyword, int start, int end) {
- List<byte[]> splitParts = new ArrayList<>();
- int from = start;
- while (from < end) {
- int to = descriptorString.indexOf("\n" + keyword + " ", from);
- if (to < 0) {
- to = descriptorString.indexOf("\n" + keyword + "\n", from);
- }
- if (to < 0) {
- to = end;
- } else {
- to += 1;
- }
- int toNoNewline = to;
- while (toNoNewline > from &&
- descriptorString.charAt(toNoNewline - 1) == '\n') {
- toNoNewline--;
- }
- byte[] part = new byte[toNoNewline - from];
- System.arraycopy(this.rawDescriptorBytes, from, part, 0,
- toNoNewline - from);
- from = to;
- splitParts.add(part);
- }
- return splitParts;
- }
-
- private void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String publishedLine = null, nextCrypto = "",
- runningRoutersLine = null, routerStatusLine = null;
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.isEmpty() || line.startsWith("@")) {
- continue;
- }
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "signed-directory":
- this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- if (publishedLine != null) {
- throw new DescriptorParseException("Keyword 'published' is "
- + "contained more than once, but must be contained exactly "
- + "once.");
- } else {
- publishedLine = line;
- }
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "dir-signing-key";
- break;
- case "recommended-software":
- this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "running-routers":
- runningRoutersLine = line;
- break;
- case "router-status":
- routerStatusLine = line;
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key") &&
- this.dirSigningKey == null) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v1 directory.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in v1 directory.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- if (publishedLine == null) {
- throw new DescriptorParseException("Keyword 'published' is "
- + "contained 0 times, but must be contained exactly once.");
- } else {
- String publishedLineNoOpt = publishedLine.startsWith("opt ") ?
- publishedLine.substring("opt ".length()) : publishedLine;
- String[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+");
- this.parsePublishedLine(publishedLine, publishedLineNoOpt,
- publishedPartsNoOpt);
- }
- if (routerStatusLine != null) {
- String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") ?
- routerStatusLine.substring("opt ".length()) : routerStatusLine;
- String[] routerStatusPartsNoOpt =
- routerStatusLineNoOpt.split("[ \t]+");
- this.parseRouterStatusLine(routerStatusLine, routerStatusLineNoOpt,
- routerStatusPartsNoOpt);
- } else if (runningRoutersLine != null) {
- String runningRoutersLineNoOpt =
- runningRoutersLine.startsWith("opt ") ?
- runningRoutersLine.substring("opt ".length()) :
- runningRoutersLine;
- String[] runningRoutersPartsNoOpt =
- runningRoutersLineNoOpt.split("[ \t]+");
- this.parseRunningRoutersLine(runningRoutersLine,
- runningRoutersLineNoOpt, runningRoutersPartsNoOpt);
- } else {
- throw new DescriptorParseException("Either running-routers or "
- + "router-status line must be given.");
- }
- }
-
- protected void parseServerDescriptor(byte[] serverDescriptorBytes) {
- try {
- ServerDescriptorImpl serverDescriptor =
- new RelayServerDescriptorImpl(serverDescriptorBytes,
- this.failUnrecognizedDescriptorLines);
- this.serverDescriptors.add(serverDescriptor);
- } catch (DescriptorParseException e) {
- this.serverDescriptorParseExceptions.add(e);
- }
- }
-
- private void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseSignedDirectoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("signed-directory")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirSigningKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length > 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else if (partsNoOpt.length == 2) {
- /* Early directories didn't have a crypto object following the
- * "dir-signing-key" line, but had the key base64-encoded in the
- * same line. */
- StringBuilder sb = new StringBuilder();
- sb.append("-----BEGIN RSA PUBLIC KEY-----\n");
- String keyString = partsNoOpt[1];
- while (keyString.length() > 64) {
- sb.append(keyString.substring(0, 64)).append("\n");
- keyString = keyString.substring(64);
- }
- if (keyString.length() > 0) {
- sb.append(keyString).append("\n");
- }
- sb.append("-----END RSA PUBLIC KEY-----\n");
- this.dirSigningKey = sb.toString();
- }
- }
-
- private void parseRecommendedSoftwareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- List<String> result = new ArrayList<>();
- if (partsNoOpt.length > 2) {
- throw new DescriptorParseException("Illegal versions line '" + line
- + "'.");
- } else if (partsNoOpt.length == 2) {
- String[] versions = partsNoOpt[1].split(",", -1);
- for (int i = 0; i < versions.length; i++) {
- String version = versions[i];
- if (version.length() < 1) {
- throw new DescriptorParseException("Illegal versions line '"
- + line + "'.");
- }
- result.add(version);
- }
- }
- this.recommendedSoftware = result;
- }
-
- private void parseRunningRoutersLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- for (int i = 1; i < partsNoOpt.length; i++) {
- String part = partsNoOpt[i];
- String debugLine = "running-routers [...] " + part + " [...]";
- boolean isLive = true;
- if (part.startsWith("!")) {
- isLive = false;
- part = part.substring(1);
- }
- boolean isVerified;
- String fingerprint = null, nickname = null;
- if (part.startsWith("$")) {
- isVerified = false;
- fingerprint = ParseHelper.parseTwentyByteHexString(debugLine,
- part.substring(1));
- } else {
- isVerified = true;
- nickname = ParseHelper.parseNickname(debugLine, part);
- }
- this.statusEntries.add(new RouterStatusEntryImpl(fingerprint,
- nickname, isLive, isVerified));
- }
- }
-
- private void parseRouterStatusLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- for (int i = 1; i < partsNoOpt.length; i++) {
- String part = partsNoOpt[i];
- String debugLine = "router-status [...] " + part + " [...]";
- RouterStatusEntry entry = null;
- if (part.contains("=")) {
- String[] partParts = part.split("=");
- if (partParts.length == 2) {
- boolean isVerified = true, isLive;
- String nickname;
- if (partParts[0].startsWith("!")) {
- isLive = false;
- nickname = ParseHelper.parseNickname(debugLine,
- partParts[0].substring(1));
- } else {
- isLive = true;
- nickname = ParseHelper.parseNickname(debugLine, partParts[0]);
- }
- String fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, partParts[1].substring(1));
- entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
- isVerified);
- }
- } else {
- boolean isVerified = false, isLive;
- String nickname = null, fingerprint;
- if (part.startsWith("!")) {
- isLive = false;
- fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, part.substring(2));
- } else {
- isLive = true;
- fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, part.substring(1));;
- }
- entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
- isVerified);
- }
- if (entry == null) {
- throw new DescriptorParseException("Illegal router-status entry '"
- + part + "' in v1 directory.");
- }
- this.statusEntries.add(entry);
- }
- }
-
- private void parseDirectorySignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private List<String> recommendedSoftware;
- @Override
- public List<String> getRecommendedSoftware() {
- return this.recommendedSoftware == null ? null :
- new ArrayList<>(this.recommendedSoftware);
- }
-
- private String directorySignature;
- @Override
- public String getDirectorySignature() {
- return this.directorySignature;
- }
-
- private List<RouterStatusEntry> statusEntries = new ArrayList<>();
- @Override
- public List<RouterStatusEntry> getRouterStatusEntries() {
- return new ArrayList<>(this.statusEntries);
- }
-
- private List<ServerDescriptor> serverDescriptors = new ArrayList<>();
- @Override
- public List<ServerDescriptor> getServerDescriptors() {
- return new ArrayList<>(this.serverDescriptors);
- }
-
- private List<Exception> serverDescriptorParseExceptions =
- new ArrayList<>();
- @Override
- public List<Exception> getServerDescriptorParseExceptions() {
- return new ArrayList<>(this.serverDescriptorParseExceptions);
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String directoryDigest;
- @Override
- public String getDirectoryDigest() {
- return this.directoryDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
deleted file mode 100644
index 73d4dfa..0000000
--- a/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.RelayExtraInfoDescriptor;
-
-public class RelayExtraInfoDescriptorImpl
- extends ExtraInfoDescriptorImpl implements RelayExtraInfoDescriptor {
-
- protected static List<ExtraInfoDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "extra-info ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ExtraInfoDescriptor parsedDescriptor =
- new RelayExtraInfoDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected RelayExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
deleted file mode 100644
index fe045c1..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* Contains a network status consensus or microdesc consensus. */
-public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
- implements RelayNetworkStatusConsensus {
-
- protected static List<RelayNetworkStatusConsensus> parseConsensuses(
- byte[] consensusesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatusConsensus> parsedConsensuses =
- new ArrayList<>();
- List<byte[]> splitConsensusBytes =
- DescriptorImpl.splitRawDescriptorBytes(consensusesBytes,
- "network-status-version 3");
- for (byte[] consensusBytes : splitConsensusBytes) {
- RelayNetworkStatusConsensus parsedConsensus =
- new RelayNetworkStatusConsensusImpl(consensusBytes,
- failUnrecognizedDescriptorLines);
- parsedConsensuses.add(parsedConsensus);
- }
- return parsedConsensuses;
- }
-
- protected RelayNetworkStatusConsensusImpl(byte[] consensusBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(consensusBytes, failUnrecognizedDescriptorLines, true, false);
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "vote-status,consensus-method,valid-after,fresh-until,"
- + "valid-until,voting-delay,known-flags").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "client-versions,server-versions,params,directory-footer,"
- + "bandwidth-weights").split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("network-status-version");
- this.clearParsedKeywords();
- this.calculateDigest();
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "network-status-version ";
- String sigToken = "\ndirectory-signature ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.consensusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.consensusDigest == null) {
- throw new DescriptorParseException("Could not calculate consensus "
- + "digest.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-method":
- this.parseConsensusMethodLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private boolean microdescConsensus = false;
- protected void parseStatusEntry(byte[] statusEntryBytes)
- throws DescriptorParseException {
- NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
- statusEntryBytes, this.microdescConsensus,
- this.failUnrecognizedDescriptorLines);
- this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "directory-footer":
- break;
- case "bandwidth-weights":
- this.parseBandwidthWeightsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.startsWith("network-status-version 3")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 3;
- if (parts.length == 3) {
- this.consensusFlavor = parts[2];
- if (this.consensusFlavor.equals("microdesc")) {
- this.microdescConsensus = true;
- }
- } else if (parts.length != 2) {
- throw new DescriptorParseException("Illegal network status version "
- + "line '" + line + "'.");
- }
- }
-
- private void parseVoteStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || !parts[1].equals("consensus")) {
- throw new DescriptorParseException("Line '" + line + "' indicates "
- + "that this is not a consensus.");
- }
- }
-
- private void parseConsensusMethodLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in consensus.");
- }
- try {
- this.consensusMethod = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- if (this.consensusMethod < 1) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- }
-
- private void parseValidAfterLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFreshUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseVotingDelayLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- try {
- this.voteSeconds = Long.parseLong(parts[1]);
- this.distSeconds = Long.parseLong(parts[2]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePackageLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 5) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- if (this.packageLines == null) {
- this.packageLines = new ArrayList<>();
- }
- this.packageLines.add(line.substring("package ".length()));
- }
-
- private void parseKnownFlagsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No known flags in line '" + line
- + "'.");
- }
- String[] knownFlags = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- knownFlags[i - 1] = parts[i];
- }
- this.knownFlags = knownFlags;
- }
-
- private void parseParamsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private void parseBandwidthWeightsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private String consensusDigest;
- @Override
- public String getConsensusDigest() {
- return this.consensusDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private String consensusFlavor;
- @Override
- public String getConsensusFlavor() {
- return this.consensusFlavor;
- }
-
- private int consensusMethod;
- @Override
- public int getConsensusMethod() {
- return this.consensusMethod;
- }
-
- private long validAfterMillis;
- @Override
- public long getValidAfterMillis() {
- return this.validAfterMillis;
- }
-
- private long freshUntilMillis;
- @Override
- public long getFreshUntilMillis() {
- return this.freshUntilMillis;
- }
-
- private long validUntilMillis;
- @Override
- public long getValidUntilMillis() {
- return this.validUntilMillis;
- }
-
- private long voteSeconds;
- @Override
- public long getVoteSeconds() {
- return this.voteSeconds;
- }
-
- private long distSeconds;
- @Override
- public long getDistSeconds() {
- return this.distSeconds;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private List<String> packageLines;
- @Override
- public List<String> getPackageLines() {
- return this.packageLines == null ? null
- : new ArrayList<>(this.packageLines);
- }
-
- private String[] knownFlags;
- @Override
- public SortedSet<String> getKnownFlags() {
- return new TreeSet<>(Arrays.asList(this.knownFlags));
- }
-
- private SortedMap<String, Integer> consensusParams;
- @Override
- public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
- }
-
- private SortedMap<String, Integer> bandwidthWeights;
- @Override
- public SortedMap<String, Integer> getBandwidthWeights() {
- return this.bandwidthWeights == null ? null :
- new TreeMap<>(this.bandwidthWeights);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
deleted file mode 100644
index a5469db..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatus;
-
-/* TODO Write unit tests. */
-
-public class RelayNetworkStatusImpl extends NetworkStatusImpl
- implements RelayNetworkStatus {
-
- protected static List<RelayNetworkStatus> parseStatuses(
- byte[] statusesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatus> parsedStatuses = new ArrayList<>();
- List<byte[]> splitStatusBytes =
- DescriptorImpl.splitRawDescriptorBytes(statusesBytes,
- "network-status-version 2");
- for (byte[] statusBytes : splitStatusBytes) {
- RelayNetworkStatus parsedStatus = new RelayNetworkStatusImpl(
- statusBytes, failUnrecognizedDescriptorLines);
- parsedStatuses.add(parsedStatus);
- }
- return parsedStatuses;
- }
-
- protected RelayNetworkStatusImpl(byte[] statusBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(statusBytes, failUnrecognizedDescriptorLines, false, true);
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "network-status-version,dir-source,fingerprint,contact,"
- + "dir-signing-key,published").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList(
- "dir-options,client-versions,server-versions".split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("network-status-version");
- this.clearParsedKeywords();
- this.calculateDigest();
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "network-status-version ";
- String sigToken = "\ndirectory-signature ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- sig = ascii.indexOf("\n", sig) + 1;
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.statusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.statusDigest == null) {
- throw new DescriptorParseException("Could not calculate status "
- + "digest.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.isEmpty()) {
- continue;
- }
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "dir-options":
- this.parseDirOptionsLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key")) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory footer expected in "
- + "v2 network status.");
- }
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, parts);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("network-status-version 2")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 2;
- }
-
- private void parseDirSourceLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 4) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in v2 network status.");
- }
- if (parts[1].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.address = ParseHelper.parseIpv4Address(line, parts[2]);
- this.dirPort = ParseHelper.parsePort(line, parts[3]);
- }
-
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in v2 network status.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parseContactLine(String line, String[] parts)
- throws DescriptorParseException {
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseDirOptionsLine(String line, String[] parts)
- throws DescriptorParseException {
- String[] dirOptions = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- dirOptions[i - 1] = parts[i];
- }
- this.dirOptions = dirOptions;
- }
-
- private void parseDirectorySignatureLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- }
-
- private String statusDigest;
- @Override
- public String getStatusDigest() {
- return this.statusDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int dirPort;
- @Override
- public int getDirport() {
- return this.dirPort;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String[] dirOptions;
- @Override
- public SortedSet<String> getDirOptions() {
- return new TreeSet<>(Arrays.asList(this.dirOptions));
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String directorySignature;
- @Override
- public String getDirectorySignature() {
- return this.directorySignature;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
deleted file mode 100644
index 384ad1f..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ /dev/null
@@ -1,761 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.RelayNetworkStatusVote;
-
-/* Contains a network status vote. */
-public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
- implements RelayNetworkStatusVote {
-
- protected static List<RelayNetworkStatusVote> parseVotes(
- byte[] votesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatusVote> parsedVotes = new ArrayList<>();
- List<byte[]> splitVotesBytes =
- DescriptorImpl.splitRawDescriptorBytes(votesBytes,
- "network-status-version 3");
- for (byte[] voteBytes : splitVotesBytes) {
- RelayNetworkStatusVote parsedVote =
- new RelayNetworkStatusVoteImpl(voteBytes,
- failUnrecognizedDescriptorLines);
- parsedVotes.add(parsedVote);
- }
- return parsedVotes;
- }
-
- protected RelayNetworkStatusVoteImpl(byte[] voteBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(voteBytes, failUnrecognizedDescriptorLines, false, false);
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList((
- "vote-status,published,valid-after,fresh-until,"
- + "valid-until,voting-delay,known-flags,dir-source,"
- + "dir-key-certificate-version,fingerprint,dir-key-published,"
- + "dir-key-expires,dir-identity-key,dir-signing-key,"
- + "dir-key-certification").split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "consensus-methods,client-versions,server-versions,"
- + "flag-thresholds,params,contact,"
- + "legacy-key,dir-key-crosscert,dir-address,directory-footer").
- split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- Set<String> atLeastOnceKeywords = new HashSet<>(Arrays.asList(
- "directory-signature"));
- this.checkAtLeastOnceKeywords(atLeastOnceKeywords);
- this.checkFirstKeyword("network-status-version");
- this.clearParsedKeywords();
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- /* Initialize flag-thresholds values here for the case that the vote
- * doesn't contain those values. Initializing them in the constructor
- * or when declaring variables wouldn't work, because those parts are
- * evaluated later and would overwrite everything we parse here. */
- this.stableUptime = -1L;
- this.stableMtbf = -1L;
- this.fastBandwidth = -1L;
- this.guardWfu = -1.0;
- this.guardTk = -1L;
- this.guardBandwidthIncludingExits = -1L;
- this.guardBandwidthExcludingExits = -1L;
- this.enoughMtbfInfo = -1;
- this.ignoringAdvertisedBws = -1;
-
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-methods":
- this.parseConsensusMethodsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "legacy-dir-key":
- this.parseLegacyDirKeyLine(line, parts);
- break;
- case "dir-key-published":
- this.parseDirKeyPublished(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
- case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
- break;
- case "dir-signing-key":
- this.dirSigningKey = cryptoString;
- break;
- case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
- break;
- case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
- break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in vote.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in vote.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("network-status-version 3")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 3;
- }
-
- private void parseVoteStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || !parts[1].equals("vote")) {
- throw new DescriptorParseException("Line '" + line + "' indicates "
- + "that this is not a vote.");
- }
- }
-
- private void parseConsensusMethodsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- Integer[] consensusMethods = new Integer[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- int consensusMethod = -1;
- try {
- consensusMethod = Integer.parseInt(parts[i]);
- } catch (NumberFormatException e) {
- /* We'll notice below that consensusMethod is still -1. */
- }
- if (consensusMethod < 1) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- consensusMethods[i - 1] = consensusMethod;
- }
- this.consensusMethods = consensusMethods;
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidAfterLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFreshUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseVotingDelayLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- try {
- this.voteSeconds = Long.parseLong(parts[1]);
- this.distSeconds = Long.parseLong(parts[2]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePackageLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 5) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- if (this.packageLines == null) {
- this.packageLines = new ArrayList<>();
- }
- this.packageLines.add(line.substring("package ".length()));
- }
-
- private void parseKnownFlagsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No known flags in line '" + line
- + "'.");
- }
- String[] knownFlags = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- knownFlags[i - 1] = parts[i];
- }
- this.knownFlags = knownFlags;
- }
-
- private void parseFlagThresholdsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No flag thresholds in line '"
- + line + "'.");
- }
- SortedMap<String, String> flagThresholds =
- ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
- try {
- for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
- switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
- default:
- // empty
- }
- }
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
-
- private void parseParamsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private void parseDirSourceLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 7) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
- if (parts[3].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.hostname = parts[3];
- this.address = ParseHelper.parseIpv4Address(line, parts[4]);
- this.dirPort = ParseHelper.parsePort(line, parts[5]);
- this.orPort = ParseHelper.parsePort(line, parts[6]);
- }
-
- private void parseContactLine(String line, String[] parts)
- throws DescriptorParseException {
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseDirKeyCertificateVersionLine(String line,
- String[] parts) throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- try {
- this.dirKeyCertificateVersion = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal dir key certificate "
- + "version in line '" + line + "'.");
- }
- if (this.dirKeyCertificateVersion < 1) {
- throw new DescriptorParseException("Illegal dir key certificate "
- + "version in line '" + line + "'.");
- }
- }
-
- private void parseDirAddressLine(String line, String[] parts) {
- /* Nothing new to learn here. Also, this line hasn't been observed
- * "in the wild" yet. Maybe it's just an urban legend. */
- }
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- /* Nothing new to learn here. We already know the fingerprint from
- * the dir-source line. But we should at least check that there's a
- * valid fingerprint in this line. */
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- ParseHelper.parseTwentyByteHexString(line, parts[1]);
- }
-
- private void parseLegacyDirKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line, parts[1]);
- }
-
- private void parseDirKeyPublished(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirKeyExpiresLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirIdentityKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-identity-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCrosscertLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-crosscert")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCertificationLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-certification")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (!line.equals("directory-footer")) {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in vote.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int dirPort;
- @Override
- public int getDirport() {
- return this.dirPort;
- }
-
- private int orPort;
- @Override
- public int getOrport() {
- return this.orPort;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private int dirKeyCertificateVersion;
- @Override
- public int getDirKeyCertificateVersion() {
- return this.dirKeyCertificateVersion;
- }
-
- private String legacyDirKey;
- @Override
- public String getLegacyDirKey() {
- return this.legacyDirKey;
- }
-
- private long dirKeyPublishedMillis;
- @Override
- public long getDirKeyPublishedMillis() {
- return this.dirKeyPublishedMillis;
- }
-
- private long dirKeyExpiresMillis;
- @Override
- public long getDirKeyExpiresMillis() {
- return this.dirKeyExpiresMillis;
- }
-
- private String dirIdentityKey;
- @Override
- public String getDirIdentityKey() {
- return this.dirIdentityKey;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String dirKeyCrosscert;
- @Override
- public String getDirKeyCrosscert() {
- return this.dirKeyCrosscert;
- }
-
- private String dirKeyCertification;
- @Override
- public String getDirKeyCertification() {
- return this.dirKeyCertification;
- }
-
- @Override
- public String getSigningKeyDigest() {
- String signingKeyDigest = null;
- if (this.signatures != null && !this.signatures.isEmpty()) {
- for (DirectorySignature signature : this.signatures) {
- if (DirectorySignatureImpl.DEFAULT_ALGORITHM.equals(
- signature.getAlgorithm())) {
- signingKeyDigest = signature.getSigningKeyDigest();
- break;
- }
- }
- }
- return signingKeyDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private Integer[] consensusMethods;
- @Override
- public List<Integer> getConsensusMethods() {
- return this.consensusMethods == null ? null :
- Arrays.asList(this.consensusMethods);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long validAfterMillis;
- @Override
- public long getValidAfterMillis() {
- return this.validAfterMillis;
- }
-
- private long freshUntilMillis;
- @Override
- public long getFreshUntilMillis() {
- return this.freshUntilMillis;
- }
-
- private long validUntilMillis;
- @Override
- public long getValidUntilMillis() {
- return this.validUntilMillis;
- }
-
- private long voteSeconds;
- @Override
- public long getVoteSeconds() {
- return this.voteSeconds;
- }
-
- private long distSeconds;
- @Override
- public long getDistSeconds() {
- return this.distSeconds;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private List<String> packageLines;
- @Override
- public List<String> getPackageLines() {
- return this.packageLines == null ? null
- : new ArrayList<>(this.packageLines);
- }
-
- private String[] knownFlags;
- @Override
- public SortedSet<String> getKnownFlags() {
- return new TreeSet<>(Arrays.asList(this.knownFlags));
- }
-
- private long stableUptime;
- @Override
- public long getStableUptime() {
- return this.stableUptime;
- }
-
- private long stableMtbf;
- @Override
- public long getStableMtbf() {
- return this.stableMtbf;
- }
-
- private long fastBandwidth;
- @Override
- public long getFastBandwidth() {
- return this.fastBandwidth;
- }
-
- private double guardWfu;
- @Override
- public double getGuardWfu() {
- return this.guardWfu;
- }
-
- private long guardTk;
- @Override
- public long getGuardTk() {
- return this.guardTk;
- }
-
- private long guardBandwidthIncludingExits;
- @Override
- public long getGuardBandwidthIncludingExits() {
- return this.guardBandwidthIncludingExits;
- }
-
- private long guardBandwidthExcludingExits;
- @Override
- public long getGuardBandwidthExcludingExits() {
- return this.guardBandwidthExcludingExits;
- }
-
- private int enoughMtbfInfo;
- @Override
- public int getEnoughMtbfInfo() {
- return this.enoughMtbfInfo;
- }
-
- private int ignoringAdvertisedBws;
- @Override
- public int getIgnoringAdvertisedBws() {
- return this.ignoringAdvertisedBws;
- }
-
- private SortedMap<String, Integer> consensusParams;
- @Override
- public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
deleted file mode 100644
index 4957072..0000000
--- a/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayServerDescriptor;
-import org.torproject.descriptor.ServerDescriptor;
-
-public class RelayServerDescriptorImpl extends ServerDescriptorImpl
- implements RelayServerDescriptor {
-
- protected static List<ServerDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "router ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ServerDescriptor parsedDescriptor =
- new RelayServerDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected RelayServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
deleted file mode 100644
index a359c50..0000000
--- a/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.RouterStatusEntry;
-
-public class RouterStatusEntryImpl implements RouterStatusEntry {
-
- protected RouterStatusEntryImpl(String fingerprint, String nickname,
- boolean isLive, boolean isVerified) {
- this.fingerprint = fingerprint;
- this.nickname = nickname;
- this.isLive = isLive;
- this.isVerified = isVerified;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private boolean isLive;
- @Override
- public boolean isLive() {
- return this.isLive;
- }
-
- private boolean isVerified;
- @Override
- public boolean isVerified() {
- return this.isVerified;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
deleted file mode 100644
index 1805dca..0000000
--- a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ /dev/null
@@ -1,985 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* Contains a server descriptor. */
-public abstract class ServerDescriptorImpl extends DescriptorImpl
- implements ServerDescriptor {
-
- protected ServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- this.calculateDigest();
- this.calculateDigestSha256();
- Set<String> exactlyOnceKeywords = new HashSet<>(Arrays.asList(
- "router,bandwidth,published".split(",")));
- this.checkExactlyOnceKeywords(exactlyOnceKeywords);
- Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
- "identity-ed25519,master-key-ed25519,platform,fingerprint,"
- + "hibernating,uptime,contact,family,read-history,write-history,"
- + "eventdns,caches-extra-info,extra-info-digest,"
- + "hidden-service-dir,protocols,allow-single-hop-exits,onion-key,"
- + "signing-key,ipv6-policy,ntor-onion-key,onion-key-crosscert,"
- + "ntor-onion-key-crosscert,tunnelled-dir-server,"
- + "router-sig-ed25519,router-signature,router-digest-sha256,"
- + "router-digest").split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("router");
- if (this.getKeywordCount("accept") == 0 &&
- this.getKeywordCount("reject") == 0) {
- throw new DescriptorParseException("Either keyword 'accept' or "
- + "'reject' must be contained at least once.");
- }
- this.clearParsedKeywords();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- List<String> cryptoLines = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) {
- continue;
- }
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "router":
- this.parseRouterLine(line, lineNoOpt, partsNoOpt);
- break;
- case "or-address":
- this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bandwidth":
- this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
- break;
- case "platform":
- this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hibernating":
- this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
- break;
- case "uptime":
- this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key":
- this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key";
- break;
- case "signing-key":
- this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "signing-key";
- break;
- case "accept":
- this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
- break;
- case "reject":
- this.parseRejectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "contact":
- this.parseContactLine(line, lineNoOpt, partsNoOpt);
- break;
- case "family":
- this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "eventdns":
- this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "caches-extra-info":
- this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "extra-info-digest":
- this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidden-service-dir":
- this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
- break;
- case "protocols":
- this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "allow-single-hop-exits":
- this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dircacheport":
- this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "ipv6-policy":
- this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key-crosscert":
- this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key-crosscert";
- break;
- case "ntor-onion-key-crosscert":
- this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "ntor-onion-key-crosscert";
- break;
- case "tunnelled-dir-server":
- this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
- case "onion-key":
- this.onionKey = cryptoString;
- break;
- case "signing-key":
- this.signingKey = cryptoString;
- break;
- case "router-signature":
- this.routerSignature = cryptoString;
- break;
- case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
- break;
- case "onion-key-crosscert":
- this.onionKeyCrosscert = cryptoString;
- break;
- case "ntor-onion-key-crosscert":
- this.ntorOnionKeyCrosscert = cryptoString;
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in server descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- }
- cryptoLines = null;
- nextCrypto = "";
- break;
- default:
- if (cryptoLines != null) {
- cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in server descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseRouterLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 6) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in server descriptor.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- this.address = ParseHelper.parseIpv4Address(line, partsNoOpt[2]);
- this.orPort = ParseHelper.parsePort(line, partsNoOpt[3]);
- this.socksPort = ParseHelper.parsePort(line, partsNoOpt[4]);
- this.dirPort = ParseHelper.parsePort(line, partsNoOpt[5]);
- }
-
- private void parseOrAddressLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(partsNoOpt[1]);
- }
-
- private void parseBandwidthLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 3 || partsNoOpt.length > 4) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- boolean isValid = false;
- try {
- this.bandwidthRate = Integer.parseInt(partsNoOpt[1]);
- this.bandwidthBurst = Integer.parseInt(partsNoOpt[2]);
- if (partsNoOpt.length == 4) {
- this.bandwidthObserved = Integer.parseInt(partsNoOpt[3]);
- }
- if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0 &&
- this.bandwidthObserved >= 0) {
- isValid = true;
- }
- if (partsNoOpt.length < 4) {
- /* Tor versions 0.0.8 and older only wrote bandwidth lines with
- * rate and burst values, but no observed value. */
- this.bandwidthObserved = -1;
- }
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parsePlatformLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (lineNoOpt.length() > "platform ".length()) {
- this.platform = lineNoOpt.substring("platform ".length());
- } else {
- this.platform = "";
- }
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseFingerprintLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (lineNoOpt.length() != "fingerprint".length() + 5 * 10) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- lineNoOpt.substring("fingerprint ".length()).replaceAll(" ", ""));
- }
-
- private void parseHibernatingLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hibernating = ParseHelper.parseBoolean(partsNoOpt[1], line);
- }
-
- private void parseUptimeLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- boolean isValid = false;
- try {
- this.uptime = Long.parseLong(partsNoOpt[1]);
- isValid = true;
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal value in line '" + line
- + "'.");
- }
- }
-
- private void parseOnionKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("onion-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseSigningKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseAcceptLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
- }
-
- private void parseRejectLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
- }
-
- private void parseExitPolicyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseExitPattern(line, partsNoOpt[1]);
- this.exitPolicyLines.add(lineNoOpt);
- }
-
- private void parseRouterSignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("router-signature")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseContactLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (lineNoOpt.length() > "contact ".length()) {
- this.contact = lineNoOpt.substring("contact ".length());
- } else {
- this.contact = "";
- }
- }
-
- private void parseFamilyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- String[] familyEntries = new String[partsNoOpt.length - 1];
- for (int i = 1; i < partsNoOpt.length; i++) {
- if (partsNoOpt[i].startsWith("$")) {
- if (partsNoOpt[i].contains("=") ^ partsNoOpt[i].contains("~")) {
- String separator = partsNoOpt[i].contains("=") ? "=" : "~";
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[i].substring(1, partsNoOpt[i].indexOf(
- separator)));
- String nickname = ParseHelper.parseNickname(line,
- partsNoOpt[i].substring(partsNoOpt[i].indexOf(
- separator) + 1));
- familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
- } else {
- familyEntries[i - 1] = "$"
- + ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[i].substring(1));
- }
- } else {
- familyEntries[i - 1] = ParseHelper.parseNickname(line,
- partsNoOpt[i]);
- }
- }
- this.familyEntries = familyEntries;
- }
-
- private void parseReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseEventdnsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1], line);
- }
-
- private void parseCachesExtraInfoLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("caches-extra-info")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.cachesExtraInfo = true;
- }
-
- private void parseExtraInfoDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- if (partsNoOpt.length >= 3) {
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[2]);
- this.extraInfoDigestSha256 = partsNoOpt[2];
- }
- }
-
- private void parseHiddenServiceDirLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length == 1) {
- this.hiddenServiceDirVersions = new Integer[] { 2 };
- } else {
- try {
- Integer[] result = new Integer[partsNoOpt.length - 1];
- for (int i = 1; i < partsNoOpt.length; i++) {
- result[i - 1] = Integer.parseInt(partsNoOpt[i]);
- }
- this.hiddenServiceDirVersions = result;
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
- }
-
- private void parseProtocolsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- int linkIndex = -1, circuitIndex = -1;
- for (int i = 1; i < partsNoOpt.length; i++) {
- switch (partsNoOpt[i]) {
- case "Link":
- linkIndex = i;
- break;
- case "Circuit":
- circuitIndex = i;
- break;
- default:
- // empty
- }
- }
- if (linkIndex < 0 || circuitIndex < 0 || circuitIndex < linkIndex) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- Integer[] linkProtocolVersions =
- new Integer[circuitIndex - linkIndex - 1];
- for (int i = linkIndex + 1, j = 0; i < circuitIndex; i++, j++) {
- linkProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
- }
- Integer[] circuitProtocolVersions =
- new Integer[partsNoOpt.length - circuitIndex - 1];
- for (int i = circuitIndex + 1, j = 0; i < partsNoOpt.length;
- i++, j++) {
- circuitProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
- }
- this.linkProtocolVersions = linkProtocolVersions;
- this.circuitProtocolVersions = circuitProtocolVersions;
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseAllowSingleHopExitsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("allow-single-hop-exits")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.allowSingleHopExits = true;
- }
-
- private void parseDircacheportLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- /* The dircacheport line was only contained in server descriptors
- * published by Tor 0.0.8 and before. It's only specified in old
- * tor-spec.txt versions. */
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- if (this.dirPort != 0) {
- throw new DescriptorParseException("At most one of dircacheport "
- + "and the directory port in the router line may be non-zero.");
- }
- this.dirPort = ParseHelper.parsePort(line, partsNoOpt[1]);
- }
-
- private void parseRouterDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.serverDescriptorDigest = ParseHelper.parseTwentyByteHexString(
- line, partsNoOpt[1]);
- }
-
- private void parseIpv6PolicyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- boolean isValid = true;
- if (partsNoOpt.length != 3) {
- isValid = false;
- } else {
- switch (partsNoOpt[1]) {
- case "accept":
- case "reject":
- this.ipv6DefaultPolicy = partsNoOpt[1];
- this.ipv6PortList = partsNoOpt[2];
- String[] ports = partsNoOpt[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
- }
-
- private void parseIdentityEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseOnionKeyCrosscert(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyCrosscert(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.ntorOnionKeyCrosscertSign = Integer.parseInt(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseTunnelledDirServerLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("tunnelled-dir-server")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.tunnelledDirServer = true;
- }
-
- private void parseIdentityEd25519CryptoBlock(String cryptoString)
- throws DescriptorParseException {
- String masterKeyEd25519FromIdentityEd25519 =
- ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
- cryptoString);
- if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
- masterKeyEd25519FromIdentityEd25519)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
- }
-
- private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
- if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
- masterKeyEd25519FromMasterKeyEd25519Line)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
- }
-
- private void parseRouterSigEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.routerSignatureEd25519 = partsNoOpt[1];
- }
-
- private void parseRouterDigestSha256Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
- this.serverDescriptorDigestSha256 = partsNoOpt[1];
- }
-
- private void calculateDigest() throws DescriptorParseException {
- if (this.serverDescriptorDigest != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "router ";
- String sigToken = "\nrouter-signature\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.serverDescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.serverDescriptorDigest == null) {
- throw new DescriptorParseException("Could not calculate server "
- + "descriptor digest.");
- }
- }
-
- private void calculateDigestSha256() throws DescriptorParseException {
- if (this.serverDescriptorDigestSha256 != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest-sha256" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "router ";
- String sigToken = "\n-----END SIGNATURE-----\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
- 0, sig - start);
- this.serverDescriptorDigestSha256 =
- DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.serverDescriptorDigestSha256 == null) {
- throw new DescriptorParseException("Could not calculate server "
- + "descriptor SHA-256 digest.");
- }
- }
-
- private String serverDescriptorDigest;
- @Override
- public String getServerDescriptorDigest() {
- return this.serverDescriptorDigest;
- }
-
- private String serverDescriptorDigestSha256;
- @Override
- public String getServerDescriptorDigestSha256() {
- return this.serverDescriptorDigestSha256;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private int socksPort;
- @Override
- public int getSocksPort() {
- return this.socksPort;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private int bandwidthRate;
- @Override
- public int getBandwidthRate() {
- return this.bandwidthRate;
- }
-
- private int bandwidthBurst;
- @Override
- public int getBandwidthBurst() {
- return this.bandwidthBurst;
- }
-
- private int bandwidthObserved;
- @Override
- public int getBandwidthObserved() {
- return this.bandwidthObserved;
- }
-
- private String platform;
- @Override
- public String getPlatform() {
- return this.platform;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private boolean hibernating;
- @Override
- public boolean isHibernating() {
- return this.hibernating;
- }
-
- private Long uptime;
- @Override
- public Long getUptime() {
- return this.uptime;
- }
-
- private String onionKey;
- @Override
- public String getOnionKey() {
- return this.onionKey;
- }
-
- private String signingKey;
- @Override
- public String getSigningKey() {
- return this.signingKey;
- }
-
- private List<String> exitPolicyLines = new ArrayList<>();
- @Override
- public List<String> getExitPolicyLines() {
- return new ArrayList<>(this.exitPolicyLines);
- }
-
- private String routerSignature;
- @Override
- public String getRouterSignature() {
- return this.routerSignature;
- }
-
- private String contact;
- @Override
- public String getContact() {
- return this.contact;
- }
-
- private String[] familyEntries;
- @Override
- public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
- }
-
- private BandwidthHistory readHistory;
- @Override
- public BandwidthHistory getReadHistory() {
- return this.readHistory;
- }
-
- private BandwidthHistory writeHistory;
- @Override
- public BandwidthHistory getWriteHistory() {
- return this.writeHistory;
- }
-
- private boolean usesEnhancedDnsLogic;
- @Override
- public boolean getUsesEnhancedDnsLogic() {
- return this.usesEnhancedDnsLogic;
- }
-
- private boolean cachesExtraInfo;
- @Override
- public boolean getCachesExtraInfo() {
- return this.cachesExtraInfo;
- }
-
- private String extraInfoDigest;
- @Override
- public String getExtraInfoDigest() {
- return this.extraInfoDigest;
- }
-
- private String extraInfoDigestSha256;
- @Override
- public String getExtraInfoDigestSha256() {
- return this.extraInfoDigestSha256;
- }
-
- private Integer[] hiddenServiceDirVersions;
- @Override
- public List<Integer> getHiddenServiceDirVersions() {
- return this.hiddenServiceDirVersions == null ? null :
- Arrays.asList(this.hiddenServiceDirVersions);
- }
-
- private Integer[] linkProtocolVersions;
- @Override
- public List<Integer> getLinkProtocolVersions() {
- return this.linkProtocolVersions == null ? null :
- Arrays.asList(this.linkProtocolVersions);
- }
-
- private Integer[] circuitProtocolVersions;
- @Override
- public List<Integer> getCircuitProtocolVersions() {
- return this.circuitProtocolVersions == null ? null :
- Arrays.asList(this.circuitProtocolVersions);
- }
-
- private boolean allowSingleHopExits;
- @Override
- public boolean getAllowSingleHopExits() {
- return this.allowSingleHopExits;
- }
-
- private String ipv6DefaultPolicy;
- @Override
- public String getIpv6DefaultPolicy() {
- return this.ipv6DefaultPolicy;
- }
-
- private String ipv6PortList;
- @Override
- public String getIpv6PortList() {
- return this.ipv6PortList;
- }
-
- private String ntorOnionKey;
- @Override
- public String getNtorOnionKey() {
- return this.ntorOnionKey;
- }
-
- private String identityEd25519;
- @Override
- public String getIdentityEd25519() {
- return this.identityEd25519;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-
- private String routerSignatureEd25519;
- @Override
- public String getRouterSignatureEd25519() {
- return this.routerSignatureEd25519;
- }
-
- private String onionKeyCrosscert;
- @Override
- public String getOnionKeyCrosscert() {
- return this.onionKeyCrosscert;
- }
-
- private String ntorOnionKeyCrosscert;
- @Override
- public String getNtorOnionKeyCrosscert() {
- return this.ntorOnionKeyCrosscert;
- }
-
- private int ntorOnionKeyCrosscertSign = -1;
- @Override
- public int getNtorOnionKeyCrosscertSign() {
- return ntorOnionKeyCrosscertSign;
- }
-
- private boolean tunnelledDirServer;
- @Override
- public boolean getTunnelledDirServer() {
- return this.tunnelledDirServer;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/TorperfResultImpl.java b/src/org/torproject/descriptor/impl/TorperfResultImpl.java
deleted file mode 100644
index 0800de0..0000000
--- a/src/org/torproject/descriptor/impl/TorperfResultImpl.java
+++ /dev/null
@@ -1,546 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.TorperfResult;
-
-public class TorperfResultImpl extends DescriptorImpl
- implements TorperfResult {
-
- protected static List<Descriptor> parseTorperfResults(
- byte[] rawDescriptorBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- if (rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- List<Descriptor> parsedDescriptors = new ArrayList<>();
- String descriptorString = new String(rawDescriptorBytes);
- Scanner s = new Scanner(descriptorString).useDelimiter("\r?\n");
- String typeAnnotation = "";
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@type torperf ")) {
- String[] parts = line.split(" ");
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "'.");
- }
- String version = parts[2];
- if (!version.startsWith("1.")) {
- throw new DescriptorParseException("Unsupported version in "
- + " line '" + line + "'.");
- }
- typeAnnotation = line + "\n";
- } else {
- parsedDescriptors.add(new TorperfResultImpl(
- (typeAnnotation + line).getBytes(),
- failUnrecognizedDescriptorLines));
- typeAnnotation = "";
- }
- }
- return parsedDescriptors;
- }
-
- protected TorperfResultImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseTorperfResultLine(new String(rawDescriptorBytes));
- }
-
- private void parseTorperfResultLine(String inputLine)
- throws DescriptorParseException {
- String line = inputLine;
- while (line.startsWith("@") && line.contains("\n")) {
- line = line.split("\n")[1];
- }
- if (line.isEmpty()) {
- throw new DescriptorParseException("Blank lines are not allowed.");
- }
- String[] parts = line.split(" ");
- for (int i = 0; i < parts.length; i++) {
- String keyValue = parts[i];
- String[] keyValueParts = keyValue.split("=");
- if (keyValueParts.length != 2) {
- throw new DescriptorParseException("Illegal key-value pair in "
- + "line '" + line + "'.");
- }
- String key = keyValueParts[0];
- this.markKeyAsParsed(key, line);
- String value = keyValueParts[1];
- switch (key) {
- case "SOURCE":
- this.parseSource(value, keyValue, line);
- break;
- case "FILESIZE":
- this.parseFileSize(value, keyValue, line);
- break;
- case "START":
- this.parseStart(value, keyValue, line);
- break;
- case "SOCKET":
- this.parseSocket(value, keyValue, line);
- break;
- case "CONNECT":
- this.parseConnect(value, keyValue, line);
- break;
- case "NEGOTIATE":
- this.parseNegotiate(value, keyValue, line);
- break;
- case "REQUEST":
- this.parseRequest(value, keyValue, line);
- break;
- case "RESPONSE":
- this.parseResponse(value, keyValue, line);
- break;
- case "DATAREQUEST":
- this.parseDataRequest(value, keyValue, line);
- break;
- case "DATARESPONSE":
- this.parseDataResponse(value, keyValue, line);
- break;
- case "DATACOMPLETE":
- this.parseDataComplete(value, keyValue, line);
- break;
- case "WRITEBYTES":
- this.parseWriteBytes(value, keyValue, line);
- break;
- case "READBYTES":
- this.parseReadBytes(value, keyValue, line);
- break;
- case "DIDTIMEOUT":
- this.parseDidTimeout(value, keyValue, line);
- break;
- case "LAUNCH":
- this.parseLaunch(value, keyValue, line);
- break;
- case "USED_AT":
- this.parseUsedAt(value, keyValue, line);
- break;
- case "PATH":
- this.parsePath(value, keyValue, line);
- break;
- case "BUILDTIMES":
- this.parseBuildTimes(value, keyValue, line);
- break;
- case "TIMEOUT":
- this.parseTimeout(value, keyValue, line);
- break;
- case "QUANTILE":
- this.parseQuantile(value, keyValue, line);
- break;
- case "CIRC_ID":
- this.parseCircId(value, keyValue, line);
- break;
- case "USED_BY":
- this.parseUsedBy(value, keyValue, line);
- break;
- default:
- if (key.startsWith("DATAPERC")) {
- this.parseDataPercentile(value, keyValue, line);
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized key '" + key
- + "' in line '" + line + "'.");
- } else {
- if (this.unrecognizedKeys == null) {
- this.unrecognizedKeys = new TreeMap<>();
- }
- this.unrecognizedKeys.put(key, value);
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- if (!this.unrecognizedLines.contains(line)) {
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- this.checkAllRequiredKeysParsed(line);
- }
-
- private Set<String> parsedKeys = new HashSet<>();
- private Set<String> requiredKeys = new HashSet<>(Arrays.asList(
- ("SOURCE,FILESIZE,START,SOCKET,CONNECT,NEGOTIATE,REQUEST,RESPONSE,"
- + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES").
- split(",")));
- private void markKeyAsParsed(String key, String line)
- throws DescriptorParseException {
- if (this.parsedKeys.contains(key)) {
- throw new DescriptorParseException("Key '" + key + "' is contained "
- + "at least twice in line '" + line + "', but must be "
- + "contained at most once.");
- }
- this.parsedKeys.add(key);
- this.requiredKeys.remove(key);
- }
- private void checkAllRequiredKeysParsed(String line)
- throws DescriptorParseException {
- for (String key : this.requiredKeys) {
- throw new DescriptorParseException("Key '" + key + "' is contained "
- + "contained 0 times in line '" + line + "', but must be "
- + "contained exactly once.");
- }
- }
-
- private void parseSource(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.source = value;
- }
-
- private void parseFileSize(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- this.fileSize = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private void parseStart(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.startMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseSocket(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.socketMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseConnect(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.connectMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseNegotiate(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.negotiateMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseRequest(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.requestMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseResponse(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.responseMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataRequest(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataRequestMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataResponse(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataResponseMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataComplete(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataCompleteMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseWriteBytes(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.writeBytes = parseInt(value, keyValue, line);
- }
-
- private void parseReadBytes(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.readBytes = parseInt(value, keyValue, line);
- }
-
- private void parseDidTimeout(String value, String keyValue, String line)
- throws DescriptorParseException {
- if (value.equals("1")) {
- this.didTimeout = true;
- } else if (value.equals("0")) {
- this.didTimeout = false;
- } else {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private void parseDataPercentile(String value, String keyValue,
- String line) throws DescriptorParseException {
- String key = keyValue.substring(0, keyValue.indexOf("="));
- String percentileString = key.substring("DATAPERC".length());
- int percentile = -1;
- try {
- percentile = Integer.parseInt(percentileString);
- } catch (NumberFormatException e) {
- /* Treat key as unrecognized below. */
- percentile = -1;
- }
- if (percentile < 0 || percentile > 100) {
- if (this.unrecognizedKeys == null) {
- this.unrecognizedKeys = new TreeMap<>();
- }
- this.unrecognizedKeys.put(key, value);
- } else {
- long timestamp = this.parseTimestamp(value, keyValue, line);
- if (this.dataPercentiles == null) {
- this.dataPercentiles = new TreeMap<>();
- }
- this.dataPercentiles.put(percentile, timestamp);
- }
- }
-
- private void parseLaunch(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.launchMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseUsedAt(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.usedAtMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parsePath(String value, String keyValue, String line)
- throws DescriptorParseException {
- String[] valueParts = value.split(",");
- String[] result = new String[valueParts.length];
- for (int i = 0; i < valueParts.length; i++) {
- if (valueParts[i].length() != 41) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- result[i] = ParseHelper.parseTwentyByteHexString(line,
- valueParts[i].substring(1));
- }
- this.path = result;
- }
-
- private void parseBuildTimes(String value, String keyValue, String line)
- throws DescriptorParseException {
- String[] valueParts = value.split(",");
- Long[] result = new Long[valueParts.length];
- for (int i = 0; i < valueParts.length; i++) {
- result[i] = this.parseTimestamp(valueParts[i], keyValue, line);
- }
- this.buildTimes = result;
- }
-
- private void parseTimeout(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.timeout = this.parseInt(value, keyValue, line);
- }
-
- private void parseQuantile(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.quantile = this.parseDouble(value, keyValue, line);
- }
-
- private void parseCircId(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.circId = this.parseInt(value, keyValue, line);
- }
-
- private void parseUsedBy(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.usedBy = this.parseInt(value, keyValue, line);
- }
-
- private long parseTimestamp(String value, String keyValue, String line)
- throws DescriptorParseException {
- long timestamp = -1L;
- if (value.contains(".") && value.split("\\.").length == 2) {
- String zeroPaddedValue = (value + "000");
- String threeDecimalPlaces = zeroPaddedValue.substring(0,
- zeroPaddedValue.indexOf(".") + 4);
- String millisString = threeDecimalPlaces.replaceAll("\\.", "");
- try {
- timestamp = Long.parseLong(millisString);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (timestamp < 0L) {
- throw new DescriptorParseException("Illegal timestamp '" + value
- + "' in '" + keyValue + "' in line '" + line + "'.");
- }
- return timestamp;
- }
-
- private int parseInt(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private double parseDouble(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- return Double.parseDouble(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private SortedMap<String, String> unrecognizedKeys;
- @Override
- public SortedMap<String, String> getUnrecognizedKeys() {
- return this.unrecognizedKeys == null ? null
- : new TreeMap<>(this.unrecognizedKeys);
- }
-
- private String source;
- @Override
- public String getSource() {
- return this.source;
- }
-
- private int fileSize;
- @Override
- public int getFileSize() {
- return this.fileSize;
- }
-
- private long startMillis;
- @Override
- public long getStartMillis() {
- return this.startMillis;
- }
-
- private long socketMillis;
- @Override
- public long getSocketMillis() {
- return this.socketMillis;
- }
-
- private long connectMillis;
- @Override
- public long getConnectMillis() {
- return this.connectMillis;
- }
-
- private long negotiateMillis;
- @Override
- public long getNegotiateMillis() {
- return this.negotiateMillis;
- }
-
- private long requestMillis;
- @Override
- public long getRequestMillis() {
- return this.requestMillis;
- }
-
- private long responseMillis;
- @Override
- public long getResponseMillis() {
- return this.responseMillis;
- }
-
- private long dataRequestMillis;
- @Override
- public long getDataRequestMillis() {
- return this.dataRequestMillis;
- }
-
- private long dataResponseMillis;
- @Override
- public long getDataResponseMillis() {
- return this.dataResponseMillis;
- }
-
- private long dataCompleteMillis;
- @Override
- public long getDataCompleteMillis() {
- return this.dataCompleteMillis;
- }
-
- private int writeBytes;
- @Override
- public int getWriteBytes() {
- return this.writeBytes;
- }
-
- private int readBytes;
- @Override
- public int getReadBytes() {
- return this.readBytes;
- }
-
- private boolean didTimeout;
- @Override
- public Boolean didTimeout() {
- return this.didTimeout;
- }
-
- private SortedMap<Integer, Long> dataPercentiles;
- @Override
- public SortedMap<Integer, Long> getDataPercentiles() {
- return this.dataPercentiles == null ? null
- : new TreeMap<>(this.dataPercentiles);
- }
-
- private long launchMillis = -1L;
- @Override
- public long getLaunchMillis() {
- return this.launchMillis;
- }
-
- private long usedAtMillis = -1L;
- @Override
- public long getUsedAtMillis() {
- return this.usedAtMillis;
- }
-
- private String[] path;
- @Override
- public List<String> getPath() {
- return this.path == null ? null : Arrays.asList(this.path);
- }
-
- private Long[] buildTimes;
- @Override
- public List<Long> getBuildTimes() {
- return this.buildTimes == null ? null :
- Arrays.asList(this.buildTimes);
- }
-
- private long timeout = -1L;
- @Override
- public long getTimeout() {
- return this.timeout;
- }
-
- private double quantile = -1.0;
- @Override
- public double getQuantile() {
- return this.quantile;
- }
-
- private int circId = -1;
- @Override
- public int getCircId() {
- return this.circId;
- }
-
- private int usedBy = -1;
- @Override
- public int getUsedBy() {
- return this.usedBy;
- }
-}
-
diff --git a/src/org/torproject/descriptor/package-info.java b/src/org/torproject/descriptor/package-info.java
deleted file mode 100644
index 5b34554..0000000
--- a/src/org/torproject/descriptor/package-info.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2016 The Tor Project
- * See LICENSE for licensing information */
-
-/**
- * Interfaces and essential classes for obtaining and processing Tor
- * descriptors.
- *
- * <p>This package contains all relevant interfaces and
- * classes that an application would need to use this library.
- * Applications are strongly discouraged from accessing types from the
- * implementation package ({@code org.torproject.descriptor.impl})
- * directly, because those may change without prior notice.</p>
- *
- * <p>Interfaces and classes in this package can be grouped into
- * general-purpose types to obtain and process any type of descriptor and
- * descriptors produced by different components of the Tor network:</p>
- *
- * <ol>
- * <li>General-purpose types comprise
- * {@link org.torproject.descriptor.DescriptorSourceFactory} which is the
- * main entry point into using this library. This factory is used to
- * create the descriptor sources for obtaining remote descriptor data
- * ({@link org.torproject.descriptor.DescriptorDownloader} and
- * {@link org.torproject.descriptor.DescriptorCollector}) and descriptor
- * sources for processing local descriptor data
- * ({@link org.torproject.descriptor.DescriptorReader} and
- * {@link org.torproject.descriptor.DescriptorParser}). General-purpose
- * types also include descriptor containers
- * ({@link org.torproject.descriptor.DescriptorRequest} and
- * {@link org.torproject.descriptor.DescriptorFile}) and the
- * superinterface for all provided descriptors
- * ({@link org.torproject.descriptor.Descriptor}).</li>
- *
- * <li>The first group of descriptors is published by relays and servers
- * in the Tor network. These interfaces include server descriptors
- * ({@link org.torproject.descriptor.ServerDescriptor} with subinterfaces
- * {@link org.torproject.descriptor.RelayServerDescriptor} and
- * {@link org.torproject.descriptor.BridgeServerDescriptor}), extra-info
- * descriptors ({@link org.torproject.descriptor.ExtraInfoDescriptor} with
- * subinterfaces
- * {@link org.torproject.descriptor.RelayExtraInfoDescriptor} and
- * {@link org.torproject.descriptor.BridgeExtraInfoDescriptor}),
- * microdescriptors which are derived from server descriptors by the
- * directory authorities
- * ({@link org.torproject.descriptor.Microdescriptor}), and helper types
- * for parts of the aforementioned descriptors
- * ({@link org.torproject.descriptor.BandwidthHistory}).</li>
- *
- * <li>The second group of descriptors is generated by authoritative
- * directory servers that form an opinion about relays and bridges in the
- * Tor network. These include descriptors specified in version 3 of the
- * directory protocol
- * ({@link org.torproject.descriptor.RelayNetworkStatusConsensus},
- * {@link org.torproject.descriptor.RelayNetworkStatusVote},
- * {@link org.torproject.descriptor.DirectoryKeyCertificate}, and helper
- * types for descriptor parts
- * {@link org.torproject.descriptor.DirSourceEntry},
- * {@link org.torproject.descriptor.NetworkStatusEntry}, and
- * {@link org.torproject.descriptor.DirectorySignature}), descriptors from
- * earlier directory protocol version 2
- * ({@link org.torproject.descriptor.RelayNetworkStatus}) and version 1
- * ({@link org.torproject.descriptor.RelayDirectory} and
- * {@link org.torproject.descriptor.RouterStatusEntry}), as well as
- * descriptors published by the bridge authority and sanitized by the
- * CollecTor service
- * ({@link org.torproject.descriptor.BridgeNetworkStatus}).</li>
- *
- * <li>The third group of descriptors is created by auxiliary services
- * connected to the Tor network rather than by the Tor software. This
- * group comprises descriptors by the bridge distribution service BridgeDB
- * ({@link org.torproject.descriptor.BridgePoolAssignment}), the exit list
- * service TorDNSEL ({@link org.torproject.descriptor.ExitList}), and the
- * performance measurement service Torperf
- * ({@link org.torproject.descriptor.TorperfResult}).</li>
- * </ol>
- *
- * @since 1.0.0
- */
-package org.torproject.descriptor;
-
diff --git a/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java b/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java
new file mode 100644
index 0000000..a52020a
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java
@@ -0,0 +1,278 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.benchmark;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorReader;
+import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.Microdescriptor;
+import org.torproject.descriptor.NetworkStatusEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+import org.torproject.descriptor.ServerDescriptor;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+
+public class MeasurePerformance {
+
+ /* Check if all necessary files are available and then measure
+ * performance of some more or less common use cases. */
+ public static void main(String[] args) {
+ if (!filesAvailable()) {
+ return;
+ }
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[0]));
+ pause();
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[1]));
+ pause();
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[2]));
+ pause();
+ measureCountriesV3Requests(new File(resDir, resPaths[3]));
+ pause();
+ measureCountriesV3Requests(new File(resDir, resPaths[4]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[5]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[6]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[7]));
+ measureFractionRelaysExit80Microdescriptors(
+ new File(resDir, resPaths[8]));
+ measureFractionRelaysExit80Microdescriptors(
+ new File(resDir, resPaths[9]));
+ }
+
+ private static File resDir = new File("res");
+ private static String[] resPaths = new String[] {
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11.tar.xz",
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11.tar",
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11",
+ "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar.xz",
+ "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11.tar.xz",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11.tar",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11",
+ "archive/relay-descriptors/microdescs/microdescs-2015-11.tar.xz",
+ "archive/relay-descriptors/microdescs/microdescs-2015-11.tar"
+ };
+
+ private static boolean filesAvailable() {
+ if (!resDir.exists() || !resDir.isDirectory()) {
+ return false;
+ }
+ for (String resPath : resPaths) {
+ if (!(new File(resDir, resPath).exists())) {
+ System.err.println("Missing resource: " + resDir + "/" + resPath);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void pause() {
+ try {
+ Thread.sleep(15L * 1000L);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void measureAverageAdvertisedBandwidth(
+ File tarballFileOrDirectory) {
+ System.out.println("Starting measureAverageAdvertisedBandwidth");
+ long startedMillis = System.currentTimeMillis();
+ long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFileOrDirectory);
+ descriptorReader.addDirectory(tarballFileOrDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ServerDescriptor)) {
+ continue;
+ }
+ ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+ sumAdvertisedBandwidth += (long) Math.min(Math.min(
+ serverDescriptor.getBandwidthRate(),
+ serverDescriptor.getBandwidthBurst()),
+ serverDescriptor.getBandwidthObserved());
+ countedServerDescriptors++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureAverageAdvertisedBandwidth");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed server descriptors: %d%n",
+ countedServerDescriptors);
+ System.out.printf("Average advertised bandwidth: %d%n",
+ sumAdvertisedBandwidth / countedServerDescriptors);
+ System.out.printf("Time per server descriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedServerDescriptors));
+ }
+
+ private static void measureCountriesV3Requests(File tarballFile) {
+ System.out.println("Starting measureCountriesV3Requests");
+ long startedMillis = System.currentTimeMillis();
+ Set<String> countries = new HashSet<>();
+ long countedExtraInfoDescriptors = 0;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFile);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ExtraInfoDescriptor)) {
+ continue;
+ }
+ ExtraInfoDescriptor extraInfoDescriptor =
+ (ExtraInfoDescriptor) descriptor;
+ SortedMap<String, Integer> dirreqV3Reqs =
+ extraInfoDescriptor.getDirreqV3Reqs();
+ if (dirreqV3Reqs != null) {
+ countries.addAll(dirreqV3Reqs.keySet());
+ }
+ countedExtraInfoDescriptors++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureCountriesV3Requests");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed extra-info descriptors: %d%n",
+ countedExtraInfoDescriptors);
+ System.out.printf("Number of countries: %d%n",
+ countries.size());
+ System.out.printf("Time per extra-info descriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedExtraInfoDescriptors));
+ }
+
+ private static void measureAverageRelaysExit(
+ File tarballFileOrDirectory) {
+ System.out.println("Starting measureAverageRelaysExit");
+ long startedMillis = System.currentTimeMillis();
+ long totalRelaysWithExitFlag = 0L, totalRelays = 0L,
+ countedConsensuses = 0L;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFileOrDirectory);
+ descriptorReader.addDirectory(tarballFileOrDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
+ continue;
+ }
+ RelayNetworkStatusConsensus consensus =
+ (RelayNetworkStatusConsensus) descriptor;
+ for (NetworkStatusEntry entry :
+ consensus.getStatusEntries().values()) {
+ if (entry.getFlags().contains("Exit")) {
+ totalRelaysWithExitFlag++;
+ }
+ totalRelays++;
+ }
+ countedConsensuses++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureAverageRelaysExit");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed consensuses: %d%n", countedConsensuses);
+ System.out.printf("Total number of status entries: %d%n",
+ totalRelays);
+ System.out.printf("Total number of status entries with Exit flag: "
+ + "%d%n", totalRelaysWithExitFlag);
+ System.out.printf("Average number of relays with Exit Flag: %.2f%n",
+ (double) totalRelaysWithExitFlag / (double) totalRelays);
+ System.out.printf("Time per consensus: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedConsensuses));
+ }
+
+ private static void measureFractionRelaysExit80Microdescriptors(
+ File tarballFile) {
+ System.out.println("Starting "
+ + "measureFractionRelaysExit80Microdescriptors");
+ long startedMillis = System.currentTimeMillis();
+ long totalRelaysWithExitFlag = 0L, countedMicrodescriptors = 0L;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFile);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof Microdescriptor)) {
+ continue;
+ }
+ countedMicrodescriptors++;
+ Microdescriptor microdescriptor =
+ (Microdescriptor) descriptor;
+ String defaultPolicy = microdescriptor.getDefaultPolicy();
+ if (defaultPolicy == null) {
+ continue;
+ }
+ boolean accept = "accept".equals(
+ microdescriptor.getDefaultPolicy());
+ for (String ports : microdescriptor.getPortList().split(",")) {
+ if (ports.contains("-")) {
+ String[] parts = ports.split("-");
+ int from = Integer.parseInt(parts[0]);
+ int to = Integer.parseInt(parts[1]);
+ if (from <= 80 && to >= 80) {
+ if (accept) {
+ totalRelaysWithExitFlag++;
+ }
+ } else if (to > 80) {
+ if (!accept) {
+ totalRelaysWithExitFlag++;
+ }
+ break;
+ }
+ } else if ("80".equals(ports)) {
+ if (accept) {
+ totalRelaysWithExitFlag++;
+ }
+ break;
+ }
+ }
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending "
+ + "measureFractionRelaysExit80Microdescriptors");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed microdescriptors: %d%n",
+ countedMicrodescriptors);
+ System.out.printf("Total number of microdescriptors that exit to 80: "
+ + "%d%n", totalRelaysWithExitFlag);
+ System.out.printf("Average number of relays that exit to 80: %.2f%n",
+ (double) totalRelaysWithExitFlag
+ / (double) countedMicrodescriptors);
+ System.out.printf("Time per microdescriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedMicrodescriptors));
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
new file mode 100644
index 0000000..0847e13
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
@@ -0,0 +1,151 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.BridgeNetworkStatus;
+import org.torproject.descriptor.DescriptorParseException;
+
+/* Test parsing of bridge network statuses. Some of the parsing code is
+ * already tested in the consensus/vote-parsing tests. */
+public class BridgeNetworkStatusTest {
+
+ /* Helper class to build a bridge network status based on default data
+ * and modifications requested by test methods. */
+ private static class StatusBuilder {
+ private String fileName = "20151121-173936-"
+ + "4A0CCD2DDC7995083D73F5D667100C8A5831F16D";
+ private static BridgeNetworkStatus
+ createWithFileName(String fileName)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.fileName = fileName;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private String publishedLine = "published 2015-11-21 17:39:36";
+ private static BridgeNetworkStatus
+ createWithPublishedLine(String line)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.publishedLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private String flagThresholdsLine = "flag-thresholds "
+ + "stable-uptime=3105080 stable-mtbf=2450615 fast-speed=55000 "
+ + "guard-wfu=98.000% guard-tk=691200 guard-bw-inc-exits=337000 "
+ + "guard-bw-exc-exits=339000 enough-mtbf=1 "
+ + "ignoring-advertised-bws=0";
+ private static BridgeNetworkStatus
+ createWithFlagThresholdsLine(String line)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.flagThresholdsLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private List<String> statusEntries = new ArrayList<>();
+ private String unrecognizedHeaderLine = null;
+ protected static BridgeNetworkStatus
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.unrecognizedHeaderLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static BridgeNetworkStatus
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.unrecognizedStatusEntryLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ failUnrecognizedDescriptorLines);
+ }
+
+ private StatusBuilder() {
+ this.statusEntries.add("r Unnamed ABk0wg4j6BLCdZKleVtmNrfzJGI "
+ + "bh7gVU1Cz6+JG+7j4qGsF4prDi8 2015-11-21 15:46:25 "
+ + "10.153.163.200 443 0\ns Fast Running Stable Valid\n"
+ + "w Bandwidth=264\np reject 1-65535");
+ }
+ private byte[] buildStatus() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendStatusEntries(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.flagThresholdsLine != null) {
+ sb.append(this.flagThresholdsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ }
+
+ @Test()
+ public void testSampleStatus() throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ BridgeNetworkStatus status =
+ new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName, true);
+ assertEquals(1448127576000L, status.getPublishedMillis());
+ assertEquals(3105080L, status.getStableUptime());
+ assertEquals(2450615L, status.getStableMtbf());
+ assertEquals(55000L, status.getFastBandwidth());
+ assertEquals(98.0, status.getGuardWfu(), 0.001);
+ assertEquals(691200L, status.getGuardTk());
+ assertEquals(337000L, status.getGuardBandwidthIncludingExits());
+ assertEquals(339000L, status.getGuardBandwidthExcludingExits());
+ assertEquals(1, status.getEnoughMtbfInfo());
+ assertEquals(0, status.getIgnoringAdvertisedBws());
+ assertEquals(264, status.getStatusEntries().get(
+ "001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
+ assertTrue(status.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test()
+ public void testPublishedNoLine() throws DescriptorParseException {
+ BridgeNetworkStatus status =
+ StatusBuilder.createWithPublishedLine(null);
+ assertEquals(1448127576000L, status.getPublishedMillis());
+ }
+
+ @Test()
+ public void testFlagThresholdsNoLine() throws DescriptorParseException {
+ BridgeNetworkStatus status =
+ StatusBuilder.createWithFlagThresholdsLine(null);
+ assertEquals(-1L, status.getStableUptime());
+ assertEquals(-1L, status.getStableMtbf());
+ assertEquals(-1L, status.getFastBandwidth());
+ assertEquals(-1.0, status.getGuardWfu(), 0.001);
+ assertEquals(-1L, status.getGuardTk());
+ assertEquals(-1L, status.getGuardBandwidthIncludingExits());
+ assertEquals(-1L, status.getGuardBandwidthExcludingExits());
+ assertEquals(-1, status.getEnoughMtbfInfo());
+ assertEquals(-1, status.getIgnoringAdvertisedBws());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
new file mode 100644
index 0000000..29a2d47
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
@@ -0,0 +1,321 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* Helper class to build a consensus based on default data and
+ * modifications requested by test methods. */
+public class ConsensusBuilder {
+ String networkStatusVersionLine = "network-status-version 3";
+ protected static RelayNetworkStatusConsensus
+ createWithNetworkStatusVersionLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.networkStatusVersionLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String voteStatusLine = "vote-status consensus";
+ protected static RelayNetworkStatusConsensus
+ createWithVoteStatusLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.voteStatusLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String consensusMethodLine = "consensus-method 11";
+ protected static RelayNetworkStatusConsensus
+ createWithConsensusMethodLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.consensusMethodLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String validAfterLine = "valid-after 2011-11-30 09:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithValidAfterLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.validAfterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithFreshUntilLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.freshUntilLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String validUntilLine = "valid-until 2011-11-30 12:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithValidUntilLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.validUntilLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String votingDelayLine = "voting-delay 300 300";
+ protected static RelayNetworkStatusConsensus
+ createWithVotingDelayLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.votingDelayLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ String clientVersionsLine = "client-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ protected static RelayNetworkStatusConsensus
+ createWithClientVersionsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.clientVersionsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ String serverVersionsLine = "server-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ protected static RelayNetworkStatusConsensus
+ createWithServerVersionsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.serverVersionsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String packageLines = null;
+ protected static RelayNetworkStatusConsensus
+ createWithPackageLines(String lines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.packageLines = lines;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String knownFlagsLine = "known-flags Authority BadExit Exit "
+ + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
+ protected static RelayNetworkStatusConsensus
+ createWithKnownFlagsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.knownFlagsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String paramsLine = "params "
+ + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
+ + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
+ + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
+ + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
+ protected static RelayNetworkStatusConsensus
+ createWithParamsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.paramsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ List<String> dirSources = new ArrayList<>();
+ List<String> statusEntries = new ArrayList<>();
+ private String directoryFooterLine = "directory-footer";
+ protected void setDirectoryFooterLine(String line) {
+ this.directoryFooterLine = line;
+ }
+ protected static RelayNetworkStatusConsensus
+ createWithDirectoryFooterLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.directoryFooterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String bandwidthWeightsLine = "bandwidth-weights Wbd=285 "
+ + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=1021 Wee=10000 "
+ + "Weg=1021 Wem=10000 Wgb=10000 Wgd=8694 Wgg=10000 Wgm=10000 "
+ + "Wmb=10000 Wmd=285 Wme=0 Wmg=0 Wmm=10000";
+ protected void setBandwidthWeightsLine(String line) {
+ this.bandwidthWeightsLine = line;
+ }
+ protected static RelayNetworkStatusConsensus
+ createWithBandwidthWeightsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.bandwidthWeightsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private List<String> directorySignatures = new ArrayList<>();
+ protected void addDirectorySignature(String directorySignatureString) {
+ this.directorySignatures.add(directorySignatureString);
+ }
+ private String unrecognizedHeaderLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedHeaderLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirSourceLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedDirSourceLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedDirSourceLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedStatusEntryLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedFooterLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedFooterLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedFooterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirectorySignatureLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedDirectorySignatureLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedDirectorySignatureLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+
+ protected ConsensusBuilder() {
+ this.dirSources.add("dir-source tor26 "
+ + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 "
+ + "86.59.21.38 80 443\ncontact Peter Palfrader\nvote-digest "
+ + "0333880AA67ED7E07C11108656D0C8D6DD1C7E5D");
+ this.dirSources.add("dir-source ides "
+ + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 216.224.124.114 "
+ + "216.224.124.114 9030 9090\ncontact Mike Perry "
+ + "<mikeperryTAfsckedTODorg>\nvote-digest "
+ + "1A8827ECD53184F7A771EFA9B3D30DC473FE8670");
+ this.statusEntries.add("r ANONIONROUTER "
+ + "AHhuQ8zFQJdT8l42Axxc6m6kNwI yEMZ5B/JQixNZgC1+2rLe0pR9rU "
+ + "2011-11-30 02:52:58 93.128.66.111 24051 24052\ns Exit Fast "
+ + "Named Running V2Dir Valid\nv Tor 0.2.2.34\nw "
+ + "Bandwidth=1100\np reject 25,119,135-139,6881-6999");
+ this.statusEntries.add("r Magellan AHlabo2RwnD8I7MPOIpJVVPgGJQ "
+ + "rB/7uzI4mU38bZ9cSXEy+Z/4Cuk 2011-11-30 05:37:35 "
+ + "188.177.149.216 9001 9030\ns Fast Named Running V2Dir "
+ + "Valid\nv Tor 0.2.2.34\nw Bandwidth=367\np reject 1-65535");
+ this.directorySignatures.add("directory-signature "
+ + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ + "3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "NYRcTWAMRiYYiGW0hIbzeZKU6sefg98AwwXrQUCudO8wfA1cfgttTDoscB9I"
+ + "TbOY\nr+c30jV/qQCMamTAEDGgJTw8KghI32vytupKallI1EjCOF8UvL1UnA"
+ + "LgpaR7sZ3W\n7WQZVVrWDtnYaULOEKfwnGnRC7WwE+YRSysbzwwCVs0=\n"
+ + "-----END SIGNATURE-----");
+ this.directorySignatures.add("directory-signature "
+ + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 "
+ + "D5C30C15BB3F1DA27669C2D88439939E8F418FCF\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "DzFPj3vyYrCv0W3r8qDPJPlmeLnadY+drjWkdOqO66Ih/hAWBb9KcBJAX1sX"
+ + "aDA7\n/iSaDhduBXuJdcu8lbmMP8d6uYBdRjHXqWDXySUZAkSfPB4JJPNGvf"
+ + "oQA/qeby7E\n5374pPPL6WwCLJHkKtk21S9oHDmFBdlZq7JWQelWlVM=\n"
+ + "-----END SIGNATURE-----");
+ }
+ protected byte[] buildConsensus() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendDirSources(sb);
+ this.appendStatusEntries(sb);
+ this.appendFooter(sb);
+ this.appendDirectorySignatures(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.networkStatusVersionLine != null) {
+ sb.append(this.networkStatusVersionLine).append("\n");
+ }
+ if (this.voteStatusLine != null) {
+ sb.append(this.voteStatusLine).append("\n");
+ }
+ if (this.consensusMethodLine != null) {
+ sb.append(this.consensusMethodLine).append("\n");
+ }
+ if (this.validAfterLine != null) {
+ sb.append(this.validAfterLine).append("\n");
+ }
+ if (this.freshUntilLine != null) {
+ sb.append(this.freshUntilLine).append("\n");
+ }
+ if (this.validUntilLine != null) {
+ sb.append(this.validUntilLine).append("\n");
+ }
+ if (this.votingDelayLine != null) {
+ sb.append(this.votingDelayLine).append("\n");
+ }
+ if (this.clientVersionsLine != null) {
+ sb.append(this.clientVersionsLine).append("\n");
+ }
+ if (this.serverVersionsLine != null) {
+ sb.append(this.serverVersionsLine).append("\n");
+ }
+ if (this.packageLines != null) {
+ sb.append(this.packageLines).append("\n");
+ }
+ if (this.knownFlagsLine != null) {
+ sb.append(this.knownFlagsLine).append("\n");
+ }
+ if (this.paramsLine != null) {
+ sb.append(this.paramsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendDirSources(StringBuilder sb) {
+ for (String dirSource : this.dirSources) {
+ sb.append(dirSource).append("\n");
+ }
+ if (this.unrecognizedDirSourceLine != null) {
+ sb.append(this.unrecognizedDirSourceLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ private void appendFooter(StringBuilder sb) {
+ if (this.directoryFooterLine != null) {
+ sb.append(this.directoryFooterLine).append("\n");
+ }
+ if (this.bandwidthWeightsLine != null) {
+ sb.append(this.bandwidthWeightsLine).append("\n");
+ }
+ if (this.unrecognizedFooterLine != null) {
+ sb.append(this.unrecognizedFooterLine).append("\n");
+ }
+ }
+ private void appendDirectorySignatures(StringBuilder sb) {
+ for (String directorySignature : this.directorySignatures) {
+ sb.append(directorySignature).append("\n");
+ }
+ if (this.unrecognizedDirectorySignatureLine != null) {
+ sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
+ }
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
new file mode 100644
index 0000000..fde8e57
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
@@ -0,0 +1,134 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.SortedMap;
+
+import org.junit.Test;
+
+public class DescriptorCollectorImplTest {
+
+ private static final String REMOTE_DIRECTORY_CONSENSUSES =
+ "/recent/relay-descriptors/consensuses/";
+
+ @Test()
+ public void testOneFile() {
+ String remoteFilename = "2015-05-24-12-00-00-consensus";
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"" + remoteFilename + "\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">24-May-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertSame(1, remoteFiles.size());
+ assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
+ remoteFiles.firstKey());
+ assertEquals((Long) 1432469280000L,
+ remoteFiles.get(remoteFiles.firstKey()));
+ }
+
+ @Test()
+ public void testSameFileTwoTimestampsLastWins() {
+ String remoteFilename = "2015-05-24-12-00-00-consensus";
+ String firstTimestamp = "24-May-2015 12:04";
+ String secondTimestamp = "24-May-2015 12:08";
+ String lineFormat = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"%s\">2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">%s </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>\n";
+ String directoryListing = String.format(lineFormat + lineFormat,
+ remoteFilename, firstTimestamp, remoteFilename, secondTimestamp);
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertSame(1, remoteFiles.size());
+ assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
+ remoteFiles.firstKey());
+ assertEquals((Long) 1432469280000L,
+ remoteFiles.get(remoteFiles.firstKey()));
+ }
+
+ @Test()
+ public void testSubDirectoryOnly() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/folder.gif\" alt=\"[DIR]\"></td><td>"
+ + "<a href=\"subdir/\">subdir/</a></td>"
+ + "<td align=\"right\">27-May-2015 14:07 </td>"
+ + "<td align=\"right\"> - </td><td> </td></tr>";
+ DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
+ SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testParentDirectoryOnly() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/back.gif\" alt=\"[DIR]\"></td><td>"
+ + "<a href=\"/recent/relay-descriptors/\">Parent Directory</a>"
+ + "</td><td> </td><td align=\"right\"> - </td>"
+ + "<td> </td></tr>";
+ DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
+ SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testUnexpectedDateFormat() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">2015-05-24 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testInvalidDate() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">34-May-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNull(remoteFiles);
+ }
+
+ @Test()
+ public void testInvalidLocaleDe() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">24-Mai-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNull(remoteFiles);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
new file mode 100644
index 0000000..a563857
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
@@ -0,0 +1,131 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListImplTest {
+
+ @Test()
+ public void testAnnotatedInput() throws Exception {
+ ExitListImpl result = new ExitListImpl((tordnselAnnotation + input)
+ .getBytes("US-ASCII"), fileName, false);
+ assertEquals("Expected one annotation.", 1,
+ result.getAnnotations().size());
+ assertEquals(tordnselAnnotation.substring(0, 18),
+ result.getAnnotations().get(0));
+ assertEquals(1441065722000L, result.getDownloadedMillis());
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ assertEquals("Found: " + result.getExitListEntries(), 7,
+ result.getExitListEntries().size());
+ assertEquals("Found: " + result.getEntries(), 5,
+ result.getEntries().size());
+ }
+
+ @Test()
+ public void testMultipleOldExitAddresses() throws Exception {
+ ExitListImpl result = new ExitListImpl(
+ (tordnselAnnotation + multiExitAddressInput)
+ .getBytes("US-ASCII"), fileName, false);
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ assertEquals("Found: " + result.getExitListEntries(),
+ 3, result.getExitListEntries().size());
+ Map<String, Long> testMap = new HashMap();
+ testMap.put("81.7.17.171", 1441044592000L);
+ testMap.put("81.7.17.172", 1441044652000L);
+ testMap.put("81.7.17.173", 1441044712000L);
+ for (ExitListEntry ele : result.getExitListEntries()) {
+ Map<String, Long> map = ele.getExitAddresses();
+ assertEquals("Found: " + map, 1, map.size());
+ Map.Entry<String, Long> ea = map.entrySet().iterator().next();
+ assertTrue("Map: " + testMap,
+ testMap.keySet().contains(ea.getKey()));
+ assertTrue("Map: " + testMap + " exitaddress: " + ea,
+ testMap.values().contains(ea.getValue()));
+ testMap.remove(ea.getKey());
+ }
+ assertTrue("Map: " + testMap, testMap.isEmpty());
+ }
+
+ @Test()
+ public void testMultipleExitAddresses() throws Exception {
+ ExitListImpl result = new ExitListImpl(
+ (tordnselAnnotation + multiExitAddressInput)
+ .getBytes("US-ASCII"), fileName, false);
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ Map<String, Long> map = result.getEntries()
+ .iterator().next().getExitAddresses();
+ assertEquals("Found: " + map, 3, map.size());
+ assertTrue("Map: " + map, map.containsKey("81.7.17.171"));
+ assertTrue("Map: " + map, map.containsKey("81.7.17.172"));
+ assertTrue("Map: " + map, map.containsKey("81.7.17.173"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testInsufficientInput0() throws Exception {
+ new ExitListImpl((tordnselAnnotation + insufficientInput[0])
+ .getBytes("US-ASCII"), fileName, false);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testInsufficientInput1() throws Exception {
+ new ExitListImpl((tordnselAnnotation + insufficientInput[1])
+ .getBytes("US-ASCII"), fileName, false);
+ }
+
+ private static final String tordnselAnnotation = "@type tordnsel 1.0\n";
+ private static final String fileName = "2015-09-01-00-02-02";
+ private static final String[] insufficientInput = new String[] {
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n",
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n" };
+
+ private static final String multiExitAddressInput =
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
+ + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n";
+ private static final String input = "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 162.247.72.201 2015-08-31 17:09:23\n"
+ + "ExitNode 0098C475875ABC4AA864738B1D1079F711C38287\n"
+ + "Published 2015-08-31 13:59:24\n"
+ + "LastStatus 2015-08-31 15:03:20\n"
+ + "ExitAddress 162.248.160.151 2015-08-31 15:07:27\n"
+ + "ExitNode 00C4B4731658D3B4987132A3F77100CFCB190D97\n"
+ + "Published 2015-08-31 17:47:52\n"
+ + "LastStatus 2015-08-31 18:03:17\n"
+ + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
+ + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n"
+ + "ExitNode 00F2D93EBAF2F51D6EE4DCB0F37D91D72F824B16\n"
+ + "Published 2015-08-31 14:39:05\n"
+ + "LastStatus 2015-08-31 16:02:18\n"
+ + "ExitAddress 23.239.18.57 2015-08-31 16:06:07\n"
+ + "ExitNode 011B1D1E876B2C835D01FB9D407F2E00B28077F6\n"
+ + "Published 2015-08-31 05:14:35\n"
+ + "LastStatus 2015-08-31 06:03:29\n"
+ + "ExitAddress 104.131.51.150 2015-08-31 06:04:07\n";
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
new file mode 100644
index 0000000..6843196
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
@@ -0,0 +1,1737 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.junit.Test;
+import org.torproject.descriptor.BridgeExtraInfoDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.RelayExtraInfoDescriptor;
+
+/* Test parsing of extra-info descriptors. */
+public class ExtraInfoDescriptorImplTest {
+
+ /* Helper class to build a descriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String extraInfoLine = "extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26";
+ private static ExtraInfoDescriptor createWithExtraInfoLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.extraInfoLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String publishedLine = "published 2012-02-11 09:08:36";
+ private static ExtraInfoDescriptor createWithPublishedLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.publishedLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String writeHistoryLine = "write-history 2012-02-11 09:03:39 "
+ + "(900 s) 4713350144,4723824640,4710717440,4572675072";
+ private static ExtraInfoDescriptor createWithWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.writeHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String readHistoryLine = "read-history 2012-02-11 09:03:39 "
+ + "(900 s) 4707695616,4699666432,4650004480,4489718784";
+ private static ExtraInfoDescriptor createWithReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.readHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqWriteHistoryLine = "dirreq-write-history "
+ + "2012-02-11 09:03:39 (900 s) 81281024,64996352,60625920,"
+ + "67922944";
+ private static ExtraInfoDescriptor createWithDirreqWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqWriteHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqReadHistoryLine = "dirreq-read-history "
+ + "2012-02-11 09:03:39 (900 s) 17074176,16235520,16005120,"
+ + "16209920";
+ private static ExtraInfoDescriptor createWithDirreqReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqReadHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoipDbDigestLine = null;
+ private static ExtraInfoDescriptor createWithGeoipDbDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoipDbDigestLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoip6DbDigestLine = null;
+ private static ExtraInfoDescriptor createWithGeoip6DbDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoip6DbDigestLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoipStatsLines = null;
+ private static ExtraInfoDescriptor createWithGeoipStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoipStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqStatsLines = null;
+ private static ExtraInfoDescriptor createWithDirreqStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String entryStatsLines = null;
+ private static ExtraInfoDescriptor createWithEntryStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.entryStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String cellStatsLines = null;
+ private static ExtraInfoDescriptor createWithCellStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.cellStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String connBiDirectLine = null;
+ private static ExtraInfoDescriptor createWithConnBiDirectLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.connBiDirectLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String exitStatsLines = null;
+ private static ExtraInfoDescriptor createWithExitStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.exitStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String bridgeStatsLines = null;
+ private static ExtraInfoDescriptor createWithBridgeStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.bridgeStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hidservStatsLines = null;
+ private static ExtraInfoDescriptor createWithHidservStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hidservStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String unrecognizedLine = null;
+ private static ExtraInfoDescriptor createWithUnrecognizedLine(
+ String line, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.unrecognizedLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private byte[] nonAsciiLineBytes = null;
+ private static ExtraInfoDescriptor createWithNonAsciiLineBytes(
+ byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.nonAsciiLineBytes = lineBytes;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String routerSignatureLines = "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----";
+ private static ExtraInfoDescriptor createWithRouterSignatureLines(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerSignatureLines = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String identityEd25519Lines = null,
+ masterKeyEd25519Line = null, routerSigEd25519Line = null;
+ private static ExtraInfoDescriptor createWithEd25519Lines(
+ String identityEd25519Lines, String masterKeyEd25519Line,
+ String routerSigEd25519Line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.identityEd25519Lines = identityEd25519Lines;
+ db.masterKeyEd25519Line = masterKeyEd25519Line;
+ db.routerSigEd25519Line = routerSigEd25519Line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.extraInfoLine != null) {
+ sb.append(this.extraInfoLine).append("\n");
+ }
+ if (this.identityEd25519Lines != null) {
+ sb.append(this.identityEd25519Lines).append("\n");
+ }
+ if (this.masterKeyEd25519Line != null) {
+ sb.append(this.masterKeyEd25519Line).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.writeHistoryLine != null) {
+ sb.append(this.writeHistoryLine).append("\n");
+ }
+ if (this.readHistoryLine != null) {
+ sb.append(this.readHistoryLine).append("\n");
+ }
+ if (this.dirreqWriteHistoryLine != null) {
+ sb.append(this.dirreqWriteHistoryLine).append("\n");
+ }
+ if (this.dirreqReadHistoryLine != null) {
+ sb.append(this.dirreqReadHistoryLine).append("\n");
+ }
+ if (this.geoipDbDigestLine != null) {
+ sb.append(this.geoipDbDigestLine).append("\n");
+ }
+ if (this.geoip6DbDigestLine != null) {
+ sb.append(this.geoip6DbDigestLine).append("\n");
+ }
+ if (this.geoipStatsLines != null) {
+ sb.append(this.geoipStatsLines).append("\n");
+ }
+ if (this.dirreqStatsLines != null) {
+ sb.append(this.dirreqStatsLines).append("\n");
+ }
+ if (this.entryStatsLines != null) {
+ sb.append(this.entryStatsLines).append("\n");
+ }
+ if (this.cellStatsLines != null) {
+ sb.append(this.cellStatsLines).append("\n");
+ }
+ if (this.connBiDirectLine != null) {
+ sb.append(this.connBiDirectLine).append("\n");
+ }
+ if (this.exitStatsLines != null) {
+ sb.append(this.exitStatsLines).append("\n");
+ }
+ if (this.bridgeStatsLines != null) {
+ sb.append(this.bridgeStatsLines).append("\n");
+ }
+ if (this.hidservStatsLines != null) {
+ sb.append(this.hidservStatsLines).append("\n");
+ }
+ if (this.unrecognizedLine != null) {
+ sb.append(this.unrecognizedLine).append("\n");
+ }
+ if (this.nonAsciiLineBytes != null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(sb.toString().getBytes());
+ baos.write(this.nonAsciiLineBytes);
+ baos.write("\n".getBytes());
+ if (this.routerSignatureLines != null) {
+ baos.write(this.routerSignatureLines.getBytes());
+ }
+ return baos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ if (this.routerSigEd25519Line != null) {
+ sb.append(this.routerSigEd25519Line).append("\n");
+ }
+ if (this.routerSignatureLines != null) {
+ sb.append(this.routerSignatureLines).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ /* Helper class to build a set of geoip-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class GeoipStatsBuilder {
+ private String geoipStartTimeLine = "geoip-start-time 2012-02-10 "
+ + "18:32:51";
+ private static ExtraInfoDescriptor createWithGeoipStartTimeLine(
+ String line) throws DescriptorParseException {
+ GeoipStatsBuilder gsb = new GeoipStatsBuilder();
+ gsb.geoipStartTimeLine = line;
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ gsb.buildGeoipStatsLines());
+ }
+ private String geoipClientOriginsLine = "geoip-client-origins "
+ + "de=1152,cn=896,us=712,it=504,ru=352,fr=208,gb=208,ir=200";
+ private static ExtraInfoDescriptor createWithGeoipClientOriginsLine(
+ String line) throws DescriptorParseException {
+ GeoipStatsBuilder gsb = new GeoipStatsBuilder();
+ gsb.geoipClientOriginsLine = line;
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ gsb.buildGeoipStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ new GeoipStatsBuilder().buildGeoipStatsLines());
+ }
+ private String buildGeoipStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.geoipStartTimeLine != null) {
+ sb.append(this.geoipStartTimeLine).append("\n");
+ }
+ if (this.geoipClientOriginsLine != null) {
+ sb.append(this.geoipClientOriginsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of dirreq-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class DirreqStatsBuilder {
+ private String dirreqStatsEndLine = "dirreq-stats-end 2012-02-11 "
+ + "00:59:53 (86400 s)";
+ private static ExtraInfoDescriptor createWithDirreqStatsEndLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqStatsEndLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3IpsLine = "dirreq-v3-ips us=1544,de=1056,"
+ + "it=1032,fr=784,es=640,ru=440,br=312,gb=272,kr=224,sy=192";
+ private static ExtraInfoDescriptor createWithDirreqV3IpsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3IpsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2IpsLine = "dirreq-v2-ips ";
+ private static ExtraInfoDescriptor createWithDirreqV2IpsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2IpsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3ReqsLine = "dirreq-v3-reqs us=1744,de=1224,"
+ + "it=1080,fr=832,es=664,ru=536,br=344,gb=296,kr=272,in=216";
+ private static ExtraInfoDescriptor createWithDirreqV3ReqsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3ReqsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2ReqsLine = "dirreq-v2-reqs ";
+ private static ExtraInfoDescriptor createWithDirreqV2ReqsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2ReqsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3RespLine = "dirreq-v3-resp ok=10848,"
+ + "not-enough-sigs=8,unavailable=0,not-found=0,not-modified=0,"
+ + "busy=80";
+ private static ExtraInfoDescriptor createWithDirreqV3RespLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3RespLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2RespLine = "dirreq-v2-resp ok=0,unavailable=0,"
+ + "not-found=1576,not-modified=0,busy=0";
+ private static ExtraInfoDescriptor createWithDirreqV2RespLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2RespLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2ShareLine = "dirreq-v2-share 0.37%";
+ private static ExtraInfoDescriptor createWithDirreqV2ShareLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2ShareLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3ShareLine = "dirreq-v3-share 0.37%";
+ private static ExtraInfoDescriptor createWithDirreqV3ShareLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3ShareLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3DirectDlLine = "dirreq-v3-direct-dl "
+ + "complete=36,timeout=4,running=0,min=7538,d1=20224,d2=28950,"
+ + "q1=40969,d3=55786,d4=145813,md=199164,d6=267230,d7=480900,"
+ + "q3=481049,d8=531276,d9=778086,max=15079428";
+ private static ExtraInfoDescriptor createWithDirreqV3DirectDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3DirectDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2DirectDlLine = "dirreq-v2-direct-dl "
+ + "complete=0,timeout=0,running=0";
+ private static ExtraInfoDescriptor createWithDirreqV2DirectDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2DirectDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3TunneledDlLine = "dirreq-v3-tunneled-dl "
+ + "complete=10608,timeout=204,running=4,min=507,d1=20399,"
+ + "d2=27588,q1=29292,d3=30889,d4=40624,md=59967,d6=103333,"
+ + "d7=161170,q3=209415,d8=256711,d9=452503,max=23417777";
+ private static ExtraInfoDescriptor createWithDirreqV3TunneledDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3TunneledDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2TunneledDlLine = "dirreq-v2-tunneled-dl "
+ + "complete=0,timeout=0,running=0";
+ private static ExtraInfoDescriptor createWithDirreqV2TunneledDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2TunneledDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ new DirreqStatsBuilder().buildDirreqStatsLines());
+ }
+ private String buildDirreqStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.dirreqStatsEndLine != null) {
+ sb.append(this.dirreqStatsEndLine).append("\n");
+ }
+ if (this.dirreqV3IpsLine != null) {
+ sb.append(this.dirreqV3IpsLine).append("\n");
+ }
+ if (this.dirreqV2IpsLine != null) {
+ sb.append(this.dirreqV2IpsLine).append("\n");
+ }
+ if (this.dirreqV3ReqsLine != null) {
+ sb.append(this.dirreqV3ReqsLine).append("\n");
+ }
+ if (this.dirreqV2ReqsLine != null) {
+ sb.append(this.dirreqV2ReqsLine).append("\n");
+ }
+ if (this.dirreqV3RespLine != null) {
+ sb.append(this.dirreqV3RespLine).append("\n");
+ }
+ if (this.dirreqV2RespLine != null) {
+ sb.append(this.dirreqV2RespLine).append("\n");
+ }
+ if (this.dirreqV2ShareLine != null) {
+ sb.append(this.dirreqV2ShareLine).append("\n");
+ }
+ if (this.dirreqV3ShareLine != null) {
+ sb.append(this.dirreqV3ShareLine).append("\n");
+ }
+ if (this.dirreqV3DirectDlLine != null) {
+ sb.append(this.dirreqV3DirectDlLine).append("\n");
+ }
+ if (this.dirreqV2DirectDlLine != null) {
+ sb.append(this.dirreqV2DirectDlLine).append("\n");
+ }
+ if (this.dirreqV3TunneledDlLine != null) {
+ sb.append(this.dirreqV3TunneledDlLine).append("\n");
+ }
+ if (this.dirreqV2TunneledDlLine != null) {
+ sb.append(this.dirreqV2TunneledDlLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of entry-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class EntryStatsBuilder {
+ private String entryStatsEndLine = "entry-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithEntryStatsEndLine(
+ String line) throws DescriptorParseException {
+ EntryStatsBuilder esb = new EntryStatsBuilder();
+ esb.entryStatsEndLine = line;
+ return DescriptorBuilder.createWithEntryStatsLines(
+ esb.buildEntryStatsLines());
+ }
+ private String entryIpsLine = "entry-ips ir=25368,us=15744,it=14816,"
+ + "de=13256,es=8280,fr=8120,br=5176,sy=4760,ru=4504,sa=4216,"
+ + "gb=3152,pl=2928,nl=2208,kr=1856,ca=1792,ua=1272,in=1192";
+ private static ExtraInfoDescriptor createWithEntryIpsLine(
+ String line) throws DescriptorParseException {
+ EntryStatsBuilder esb = new EntryStatsBuilder();
+ esb.entryIpsLine = line;
+ return DescriptorBuilder.createWithEntryStatsLines(
+ esb.buildEntryStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithEntryStatsLines(
+ new EntryStatsBuilder().buildEntryStatsLines());
+ }
+ private String buildEntryStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.entryStatsEndLine != null) {
+ sb.append(this.entryStatsEndLine).append("\n");
+ }
+ if (this.entryIpsLine != null) {
+ sb.append(this.entryIpsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of cell-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class CellStatsBuilder {
+ private String cellStatsEndLine = "cell-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithCellStatsEndLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellStatsEndLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellProcessedCellsLine = "cell-processed-cells "
+ + "1441,11,6,4,2,1,1,1,1,1";
+ private static ExtraInfoDescriptor createWithCellProcessedCellsLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellProcessedCellsLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellQueuedCellsLine = "cell-queued-cells "
+ + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00";
+ private static ExtraInfoDescriptor createWithCellQueuedCellsLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellQueuedCellsLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellTimeInQueueLine = "cell-time-in-queue "
+ + "524,1,1,0,0,25,0,0,0,0";
+ private static ExtraInfoDescriptor createWithCellTimeInQueueLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellTimeInQueueLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellCircuitsPerDecileLine = "cell-circuits-per-decile "
+ + "866";
+ private static ExtraInfoDescriptor
+ createWithCellCircuitsPerDecileLine(String line)
+ throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellCircuitsPerDecileLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithCellStatsLines(
+ new CellStatsBuilder().buildCellStatsLines());
+ }
+ private String buildCellStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.cellStatsEndLine != null) {
+ sb.append(this.cellStatsEndLine).append("\n");
+ }
+ if (this.cellProcessedCellsLine != null) {
+ sb.append(this.cellProcessedCellsLine).append("\n");
+ }
+ if (this.cellQueuedCellsLine != null) {
+ sb.append(this.cellQueuedCellsLine).append("\n");
+ }
+ if (this.cellTimeInQueueLine != null) {
+ sb.append(this.cellTimeInQueueLine).append("\n");
+ }
+ if (this.cellCircuitsPerDecileLine != null) {
+ sb.append(this.cellCircuitsPerDecileLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of exit-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class ExitStatsBuilder {
+ private String exitStatsEndLine = "exit-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithExitStatsEndLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitStatsEndLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitKibibytesWrittenLine = "exit-kibibytes-written "
+ + "25=74647,80=31370,443=20577,49755=23,52563=12,52596=1111,"
+ + "57528=4,60912=11,61351=6,64811=3365,other=2592";
+ private static ExtraInfoDescriptor createWithExitKibibytesWrittenLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitKibibytesWrittenLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitKibibytesReadLine = "exit-kibibytes-read "
+ + "25=35562,80=1254256,443=110279,49755=9396,52563=1911,"
+ + "52596=648,57528=1188,60912=1427,61351=1824,64811=14,"
+ + "other=3054";
+ private static ExtraInfoDescriptor createWithExitKibibytesReadLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitKibibytesReadLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitStreamsOpenedLine = "exit-streams-opened "
+ + "25=369748,80=64212,443=151660,49755=4,52563=4,52596=4,57528=4,"
+ + "60912=4,61351=4,64811=4,other=1212";
+ private static ExtraInfoDescriptor createWithExitStreamsOpenedLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitStreamsOpenedLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithExitStatsLines(
+ new ExitStatsBuilder().buildExitStatsLines());
+ }
+ private String buildExitStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.exitStatsEndLine != null) {
+ sb.append(this.exitStatsEndLine).append("\n");
+ }
+ if (this.exitKibibytesWrittenLine != null) {
+ sb.append(this.exitKibibytesWrittenLine).append("\n");
+ }
+ if (this.exitKibibytesReadLine != null) {
+ sb.append(this.exitKibibytesReadLine).append("\n");
+ }
+ if (this.exitStreamsOpenedLine != null) {
+ sb.append(this.exitStreamsOpenedLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of bridge-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class BridgeStatsBuilder {
+ private String bridgeStatsEndLine = "bridge-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithBridgeStatsEndLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeStatsEndLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpsLine = "bridge-ips ir=24,sy=16,??=8,cn=8,"
+ + "de=8,es=8,fr=8,gb=8,in=8,jp=8,kz=8,nl=8,ua=8,us=8,vn=8,za=8";
+ private static ExtraInfoDescriptor createWithBridgeIpsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpVersionsLine = "bridge-ip-versions v4=8,v6=16";
+ private static ExtraInfoDescriptor createWithBridgeIpVersionsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpVersionsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpTransportsLine = "bridge-ip-transports "
+ + "<OR>=8,obfs2=792,obfs3=1728";
+ private static ExtraInfoDescriptor createWithBridgeIpTransportsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpTransportsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ new BridgeStatsBuilder().buildBridgeStatsLines());
+ }
+ private String buildBridgeStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.bridgeStatsEndLine != null) {
+ sb.append(this.bridgeStatsEndLine).append("\n");
+ }
+ if (this.bridgeIpsLine != null) {
+ sb.append(this.bridgeIpsLine).append("\n");
+ }
+ if (this.bridgeIpVersionsLine != null) {
+ sb.append(this.bridgeIpVersionsLine).append("\n");
+ }
+ if (this.bridgeIpTransportsLine != null) {
+ sb.append(this.bridgeIpTransportsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of hidserv-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class HidservStatsBuilder {
+ private String hidservStatsEndLine = "hidserv-stats-end 2015-12-03 "
+ + "14:26:56 (86400 s)";
+ private static ExtraInfoDescriptor createWithHidservStatsEndLine(
+ String line) throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservStatsEndLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private String hidservRendRelayedCellsLine =
+ "hidserv-rend-relayed-cells 36474281 delta_f=2048 epsilon=0.30 "
+ + "bin_size=1024";
+ private static ExtraInfoDescriptor
+ createWithHidservRendRelayedCellsLine(String line)
+ throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservRendRelayedCellsLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private String hidservDirOnionsSeenLine = "hidserv-dir-onions-seen "
+ + "-3 delta_f=8 epsilon=0.30 bin_size=8";
+ private static ExtraInfoDescriptor createWithHidservDirOnionsSeenLine(
+ String line) throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservDirOnionsSeenLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithHidservStatsLines(
+ new HidservStatsBuilder().buildHidservStatsLines());
+ }
+ private String buildHidservStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.hidservStatsEndLine != null) {
+ sb.append(this.hidservStatsEndLine).append("\n");
+ }
+ if (this.hidservRendRelayedCellsLine != null) {
+ sb.append(this.hidservRendRelayedCellsLine).append("\n");
+ }
+ if (this.hidservDirOnionsSeenLine != null) {
+ sb.append(this.hidservDirOnionsSeenLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ @Test()
+ public void testSampleDescriptor() throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ ExtraInfoDescriptor descriptor =
+ new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ assertNotNull(descriptor.getWriteHistory());
+ assertEquals(1328951019000L, descriptor.getWriteHistory().
+ getHistoryEndMillis());
+ assertEquals(900L, descriptor.getWriteHistory().getIntervalLength());
+ assertEquals(4572675072L, (long) descriptor.getWriteHistory().
+ getBandwidthValues().get(1328951019000L));
+ assertNotNull(descriptor.getReadHistory());
+ assertNotNull(descriptor.getDirreqWriteHistory());
+ assertNotNull(descriptor.getDirreqReadHistory());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoLineMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine(null);
+ }
+
+ @Test()
+ public void testExtraInfoOpt() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ }
+
+ @Test()
+ public void testExtraInfoNicknameTwoSpaces()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoLineNotFirst()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8\n"
+ + "extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameInvalidChar() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub% A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5ReallyLongNickname "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintG() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 G9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C6");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C63B26"
+ + "A9C0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine(null);
+ }
+
+ @Test()
+ public void testPublishedOpt() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-02-11 09:08:36");
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ }
+
+ @Test()
+ public void testPublishedMillis() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-02-11 09:08:36.123");
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNegativeBytes()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-02-11 09:03:39 (900 s) "
+ + "-4713350144,-4723824640,-4710717440,-4572675072");
+ }
+
+ @Test()
+ public void testReadHistoryTabInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (900\ts) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test()
+ public void testReadHistoryTabIntervalBytes()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (900 s)\t"
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testReadHistoryNegativeInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (-900 s) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test()
+ public void testReadHistoryNonStandardInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (1800 s) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqWriteHistoryMissingBytesBegin()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqWriteHistoryLine(
+ "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
+ + ",64996352,60625920,67922944");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqWriteHistoryMissingBytesMiddle()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqWriteHistoryLine(
+ "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
+ + "81281024,,60625920,67922944");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqReadHistoryMissingBytesEnd()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqReadHistoryLine(
+ "dirreq-read-history 2012-02-11 09:03:39 (900 s) "
+ + "17074176,16235520,16005120,");
+ }
+
+ @Test()
+ public void testGeoipDbDigestValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithGeoipDbDigestLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
+ descriptor.getGeoipDbDigest());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestTooShort()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301C");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestIllegalChars()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
+ + "&%6A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest");
+ }
+
+ @Test()
+ public void testGeoip6DbDigestValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithGeoip6DbDigestLine("geoip6-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
+ descriptor.getGeoip6DbDigest());
+ }
+
+ @Test()
+ public void testGeoipStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = GeoipStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328898771000L, descriptor.getGeoipStartTimeMillis());
+ SortedMap<String, Integer> ips = descriptor.getGeoipClientOrigins();
+ assertNotNull(ips);
+ assertEquals(1152, ips.get("de").intValue());
+ assertEquals(896, ips.get("cn").intValue());
+ assertFalse(ips.containsKey("pl"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipStartTimeDateOnly()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipStartTimeLine("geoip-start-time "
+ + "2012-02-10");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsDash()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de-1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsZero()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=zero,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsNone()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=none,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsOther()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,other=200");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsQuestionMarks()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,??=200");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsCapital()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins DE=1152,CN=896,US=712,IT=504,RU=352,FR=208,"
+ + "GB=208,IR=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingBegin()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins ,cn=896,us=712,it=504,ru=352,fr=208,gb=208,"
+ + "ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingMiddle()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingEnd()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsDuplicate()
+ throws DescriptorParseException {
+ /* dir-spec.txt doesn't say anything about duplicate country codes, so
+ * this line is valid, even though it leads to a somewhat undefined
+ * parse result. */
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,de=952,cn=896,us=712,it=504,"
+ + "ru=352,fr=208,gb=208,ir=200");
+ }
+
+ @Test()
+ public void testDirreqStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DirreqStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328921993000L, descriptor.getDirreqStatsEndMillis());
+ assertEquals(86400L, descriptor.getDirreqStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getDirreqV3Ips();
+ assertNotNull(ips);
+ assertEquals(1544, ips.get("us").intValue());
+ assertFalse(ips.containsKey("no"));
+ assertTrue(descriptor.getDirreqV2Ips().isEmpty());
+ SortedMap<String, Integer> reqs = descriptor.getDirreqV3Reqs();
+ assertEquals(832, reqs.get("fr").intValue());
+ assertTrue(descriptor.getDirreqV2Reqs().isEmpty());
+ SortedMap<String, Integer> resp = descriptor.getDirreqV3Resp();
+ assertEquals(10848, resp.get("ok").intValue());
+ assertEquals(8, resp.get("not-enough-sigs").intValue());
+ resp = descriptor.getDirreqV2Resp();
+ assertEquals(1576, resp.get("not-found").intValue());
+ assertEquals(0.37, descriptor.getDirreqV2Share(), 0.0001);
+ assertEquals(0.37, descriptor.getDirreqV3Share(), 0.0001);
+ SortedMap<String, Integer> dl = descriptor.getDirreqV3DirectDl();
+ assertEquals(36, dl.get("complete").intValue());
+ dl = descriptor.getDirreqV2DirectDl();
+ assertEquals(0, dl.get("timeout").intValue());
+ dl = descriptor.getDirreqV3TunneledDl();
+ assertEquals(10608, dl.get("complete").intValue());
+ dl = descriptor.getDirreqV2TunneledDl();
+ assertEquals(0, dl.get("complete").intValue());
+ }
+
+ @Test()
+ public void testDirreqStatsIntervalTwoDays()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqStatsEndLine("dirreq-stats-end "
+ + "2012-02-11 00:59:53 (172800 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3IpsThreeLetterCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3IpsLine("dirreq-v3-ips "
+ + "usa=1544");
+ }
+
+ @Test()
+ public void testDirreqV2IpsDigitCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2IpsLine("dirreq-v2-ips 00=8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3ReqsOneLetterCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3ReqsLine("dirreq-v3-reqs "
+ + "u=1744");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2ReqsNoNumber()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2ReqsLine("dirreq-v2-reqs us=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3RespTwoEqualSigns()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3RespLine("dirreq-v3-resp "
+ + "ok==10848");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2RespNull()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2RespLine("dirreq-v2-resp "
+ + "ok=null");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2ShareComma()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2ShareLine("dirreq-v2-share "
+ + "0,37%");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3ShareNoPercent()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3ShareLine("dirreq-v3-share "
+ + "0.37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3DirectDlSpace()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3DirectDlLine(
+ "dirreq-v3-direct-dl complete 36");
+ }
+
+ @Test()
+ public void testDirreqV2DirectDlNegative()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2DirectDlLine(
+ "dirreq-v2-direct-dl complete=-8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3TunneledDlTooLarge()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3TunneledDlLine(
+ "dirreq-v3-tunneled-dl complete=2147483648");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3TunneledDlDouble()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2TunneledDlLine(
+ "dirreq-v2-tunneled-dl complete=0.001");
+ }
+
+ @Test()
+ public void testEntryStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = EntryStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getEntryStatsEndMillis());
+ assertEquals(86400L, descriptor.getEntryStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getEntryIps();
+ assertNotNull(ips);
+ assertEquals(25368, ips.get("ir").intValue());
+ assertFalse(ips.containsKey("no"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEntryStatsEndNoDate() throws DescriptorParseException {
+ EntryStatsBuilder.createWithEntryStatsEndLine("entry-stats-end "
+ + "01:59:39 (86400 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEntryStatsIpsSemicolon()
+ throws DescriptorParseException {
+ EntryStatsBuilder.createWithEntryIpsLine("entry-ips "
+ + "ir=25368;us=15744");
+ }
+
+ @Test()
+ public void testCellStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = CellStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getCellStatsEndMillis());
+ assertEquals(86400L, descriptor.getCellStatsIntervalLength());
+ List<Integer> processedCells = descriptor.getCellProcessedCells();
+ assertEquals(10, processedCells.size());
+ assertEquals(1441, processedCells.get(0).intValue());
+ assertEquals(11, processedCells.get(1).intValue());
+ List<Double> queuedCells = descriptor.getCellQueuedCells();
+ assertEquals(10, queuedCells.size());
+ assertEquals(3.29, queuedCells.get(0), 0.001);
+ assertEquals(0.00, queuedCells.get(1), 0.001);
+ List<Integer> timeInQueue = descriptor.getCellTimeInQueue();
+ assertEquals(10, timeInQueue.size());
+ assertEquals(524, timeInQueue.get(0).intValue());
+ assertEquals(1, timeInQueue.get(1).intValue());
+ assertEquals(866, descriptor.getCellCircuitsPerDecile());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellStatsEndNoSeconds()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellStatsEndLine("cell-stats-end "
+ + "2012-02-11 01:59:39 (86400)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellProcessedCellsNineComma()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellProcessedCellsLine(
+ "cell-processed-cells 1441,11,6,4,2,1,1,1,1,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellProcessedCellsEleven()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellQueuedCellsLine("cell-queued-cells "
+ + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellTimeInQueueDouble()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellTimeInQueueLine("cell-time-in-queue "
+ + "524.0,1.0,1.0,0.0,0.0,25.0,0.0,0.0,0.0,0.0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellCircuitsPerDecileNegative()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellCircuitsPerDecileLine(
+ "cell-circuits-per-decile -866");
+ }
+
+ @Test()
+ public void testConnBiDirectValid()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithConnBiDirectLine("conn-bi-direct 2012-02-11 01:59:39 "
+ + "(86400 s) 42173,1591,1310,1744");
+ assertEquals(1328925579000L,
+ descriptor.getConnBiDirectStatsEndMillis());
+ assertEquals(86400L, descriptor.getConnBiDirectStatsIntervalLength());
+ assertEquals(42173, descriptor.getConnBiDirectBelow());
+ assertEquals(1591, descriptor.getConnBiDirectRead());
+ assertEquals(1310, descriptor.getConnBiDirectWrite());
+ assertEquals(1744, descriptor.getConnBiDirectBoth());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConnBiDirectStatsFive()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithConnBiDirectLine("conn-bi-direct "
+ + "2012-02-11 01:59:39 (86400 s) 42173,1591,1310,1744,42");
+ }
+
+ @Test()
+ public void testExitStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = ExitStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getExitStatsEndMillis());
+ assertEquals(86400L, descriptor.getExitStatsIntervalLength());
+ String[] ports = new String[] { "25", "80", "443", "49755",
+ "52563", "52596", "57528", "60912", "61351", "64811", "other" };
+ int[] writtenValues = new int[] { 74647, 31370, 20577, 23, 12, 1111,
+ 4, 11, 6, 3365, 2592 };
+ int i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitKibibytesWritten().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(writtenValues[i++], e.getValue().intValue());
+ }
+ int[] readValues = new int[] { 35562, 1254256, 110279, 9396, 1911,
+ 648, 1188, 1427, 1824, 14, 3054 };
+ i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitKibibytesRead().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(readValues[i++], e.getValue().intValue());
+ }
+ int[] streamsValues = new int[] { 369748, 64212, 151660, 4, 4, 4, 4,
+ 4, 4, 4, 1212 };
+ i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitStreamsOpened().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(streamsValues[i++], e.getValue().intValue());
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsEndNoSeconds()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitStatsEndLine("exit-stats-end "
+ + "2012-02-11 01:59 (86400 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsWrittenNegativePort()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesWrittenLine(
+ "exit-kibibytes-written -25=74647");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsWrittenUnknown()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesWrittenLine(
+ "exit-kibibytes-written unknown=74647");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsReadNegativeBytes()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesReadLine(
+ "exit-kibibytes-read 25=-35562");
+ }
+
+ @Test()
+ public void testExitStatsReadTooLarge()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesReadLine(
+ "exit-kibibytes-read other=2282907805");
+ }
+
+ @Test()
+ public void testExitStatsStreamsTooLarge()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitStreamsOpenedLine(
+ "exit-streams-opened 25=2147483648");
+ }
+
+ @Test()
+ public void testBridgeStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = BridgeStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getBridgeStatsEndMillis());
+ assertEquals(86400L, descriptor.getBridgeStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getBridgeIps();
+ assertNotNull(ips);
+ assertEquals(24, ips.get("ir").intValue());
+ assertEquals(16, ips.get("sy").intValue());
+ assertFalse(ips.containsKey("no"));
+ SortedMap<String, Integer> ver = descriptor.getBridgeIpVersions();
+ assertNotNull(ver);
+ assertEquals(8, ver.get("v4").intValue());
+ assertEquals(16, ver.get("v6").intValue());
+ assertFalse(ver.containsKey("v8"));
+ SortedMap<String, Integer> trans = descriptor.getBridgeIpTransports();
+ assertNotNull(trans);
+ assertEquals(8, trans.get("<OR>").intValue());
+ assertEquals(792, trans.get("obfs2").intValue());
+ assertEquals(1728, trans.get("obfs3").intValue());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeStatsEndIntervalZero()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeStatsEndLine("bridge-stats-end "
+ + "2012-02-11 01:59:39 (0 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpsLine("bridge-ips ir=24.5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpsNonAsciiKeyword()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
+ 0x14, (byte) 0xfe, 0x18, // non-ascii chars
+ 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2d, // "bridge-"
+ 0x69, 0x70, 0x73 }, false); // "ips" (no newline)
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpVersionsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpVersionsLine(
+ "bridge-ip-versions v4=24.5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpTransportsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpTransportsLine(
+ "bridge-ip-transports obfs2=24.5");
+ }
+
+ @Test()
+ public void testBridgeIpTransportsUnderscore()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpTransportsLine(
+ "bridge-ip-transports meek=32,obfs3_websocket=8,websocket=64");
+ }
+
+ @Test()
+ public void testHidservStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = HidservStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1449152816000L, descriptor.getHidservStatsEndMillis());
+ assertEquals(86400L, descriptor.getHidservStatsIntervalLength());
+ assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
+ 0.0001);
+ Map<String, Double> params =
+ descriptor.getHidservRendRelayedCellsParameters();
+ assertTrue(params.containsKey("delta_f"));
+ assertEquals(2048.0, params.remove("delta_f"), 0.0001);
+ assertTrue(params.containsKey("epsilon"));
+ assertEquals(0.3, params.remove("epsilon"), 0.0001);
+ assertTrue(params.containsKey("bin_size"));
+ assertEquals(1024.0, params.remove("bin_size"), 0.0001);
+ assertTrue(params.isEmpty());
+ assertEquals(-3.0, descriptor.getHidservDirOnionsSeen(), 0.0001);
+ params = descriptor.getHidservDirOnionsSeenParameters();
+ assertTrue(params.containsKey("delta_f"));
+ assertEquals(8.0, params.remove("delta_f"), 0.0001);
+ assertTrue(params.containsKey("epsilon"));
+ assertEquals(0.3, params.remove("epsilon"), 0.0001);
+ assertTrue(params.containsKey("bin_size"));
+ assertEquals(8.0, params.remove("bin_size"), 0.0001);
+ assertTrue(params.isEmpty());
+ }
+
+ @Test()
+ public void testHidservStatsEndLineMissing()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ HidservStatsBuilder.createWithHidservStatsEndLine(null);
+ assertEquals(-1L, descriptor.getHidservStatsEndMillis());
+ assertEquals(-1L, descriptor.getHidservStatsIntervalLength());
+ }
+
+ @Test()
+ public void testHidservRendRelayedCellsNoParams()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ HidservStatsBuilder.createWithHidservRendRelayedCellsLine(
+ "hidserv-rend-relayed-cells 36474281");
+ assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
+ 0.0001);
+ assertTrue(
+ descriptor.getHidservRendRelayedCellsParameters().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHidservDirOnionsSeenCommaSeparatedParams()
+ throws DescriptorParseException {
+ HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
+ "hidserv-dir-onions-seen -3 delta_f=8,epsilon=0.30,bin_size=8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHidservDirOnionsSeenNoDoubleParams()
+ throws DescriptorParseException {
+ HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
+ "hidserv-dir-onions-seen -3 delta_f=A epsilon=B bin_size=C");
+ }
+
+ @Test()
+ public void testRouterSignatureOpt()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("opt "
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "crypto lines are ignored anyway\n"
+ + "-----END SIGNATURE-----");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterSignatureNotLastLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----\npublished 2012-02-11 09:08:36");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ private static final String IDENTITY_ED25519_LINES =
+ "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
+ + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
+ + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
+ + "\n"
+ + "-----END ED25519 CERT-----";
+
+ private static final String MASTER_KEY_ED25519_LINE =
+ "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
+
+ private static final String ROUTER_SIG_ED25519_LINE =
+ "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
+ + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
+
+ @Test()
+ public void testEd25519() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ assertEquals(IDENTITY_ED25519_LINES.substring(
+ IDENTITY_ED25519_LINES.indexOf("\n") + 1),
+ descriptor.getIdentityEd25519());
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ assertEquals(ROUTER_SIG_ED25519_LINE.substring(
+ ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getRouterSignatureEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityMasterKeyMismatch()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519IdentityMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(null,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
+ + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityEmptyCrypto()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519MasterKeyMissing()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ null, ROUTER_SIG_ED25519_LINE);
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519MasterKeyDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519RouterSigMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519RouterSigDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
+ + ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testExtraInfoDigestSha256Relay()
+ throws DescriptorParseException {
+ byte[] descriptorBytes = ("extra-info Unnamed "
+ + "EA5B335055D2F03013FF030381F02B1C631EC723\n"
+ + "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiZRAenzZorGtx6xapoEeaqcLLOk3uWwJXTvOVLluSXXbRSZAQAgBADLN5"
+ + "wp\nCEOrRbshSbj1NDAUgc6cxU65M/Vx1x+b5+EXbkQZ5uiyB4pphVF5kPPT1P"
+ + "SleYqM\n8j+tlKh2i6+Xr0xScSPpmtG00/D0MoRlT7ZdaaaT5iw1DWDQCZ8BHG"
+ + "lAZwU=\n"
+ + "-----END ED25519 CERT-----\n"
+ + "published 2015-12-01 04:38:12\n"
+ + "write-history 2015-12-01 01:40:37 (14400 s) 88704000,60825600,"
+ + "61747200,76953600,61516800,59443200\n"
+ + "read-history 2015-12-01 01:40:37 (14400 s) 87321600,59443200,"
+ + "59904000,74880000,60364800,58060800\n"
+ + "router-sig-ed25519 c6eUeJs/SVjun3JhmjByEeWdRDyunSMAnGVhx71JiRj"
+ + "YzR8x5IcPebylG7m10wiolFxinvw78UhrrGo9Sq5ZBw\n"
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "oC2qFHCDOKSRoIPR86jdRxEYia390Z4d8fT0yr/1mg4RQ7lHmxlzFT6QxCswdX"
+ + "Ry\nvGNGR0wARySgyE+YKKWYn/Hp547JhhWd9Oc7BuFMY0XMvl/HOo+B9VjW+l"
+ + "nv6UBE\niqxx3C3Iw0ymohvOenyCUa/7TmsT7eVotDO57uIoGEc=\n"
+ + "-----END SIGNATURE-----\n"
+ + "").getBytes();
+ RelayExtraInfoDescriptor descriptor =
+ new RelayExtraInfoDescriptorImpl(descriptorBytes, true);
+ assertEquals("Pt1BtzfRwhYqGCDo8jjchS8nJP3ovrDyHGn+dqPbMgw",
+ descriptor.getExtraInfoDigestSha256());
+ }
+
+ @Test()
+ public void testExtraInfoDigestSha256Bridge()
+ throws DescriptorParseException {
+ byte[] descriptorBytes = ("extra-info idideditheconfig "
+ + "DC28749EC9E26E61DE492E46CD830379E9931B09\n"
+ + "master-key-ed25519 "
+ + "38FzmOIE6Mm85Ytx0MhFM6X9EuxWRUgb6HjyMGuO2AU\n"
+ + "published 2015-12-03 13:23:19\n"
+ + "write-history 2015-12-03 09:59:32 (14400 s) 53913600,52992000,"
+ + "53222400,53222400,53452800,53222400\n"
+ + "read-history 2015-12-03 09:59:32 (14400 s) 61056000,60364800,"
+ + "60364800,60134400,60595200,60364800\n"
+ + "geoip-db-digest 5BF366AD4A0572D82A1A0F6628AF8EF7725E3AB9\n"
+ + "geoip6-db-digest 212DE17D5A368DCAFA19B95F168BFFA101145A93\n"
+ + "router-digest-sha256 "
+ + "TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4\n"
+ + "router-digest 00B98F076B586272C3172B7F3DA29ADEE75F2ED8\n").getBytes();
+ BridgeExtraInfoDescriptor descriptor =
+ new BridgeExtraInfoDescriptorImpl(descriptorBytes, true);
+ assertEquals("TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4",
+ descriptor.getExtraInfoDigestSha256());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
new file mode 100644
index 0000000..abb51db
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
@@ -0,0 +1,82 @@
+package org.torproject.descriptor.impl;
+
+import org.junit.Test;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.Microdescriptor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class MicrodescriptorImplTest {
+
+ /* Helper class to build a microdescriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String onionKeyLines = "onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALNZ4pNsHHkl7a+kFWbBmPHNAepjjvuhjTr1TaMB3UKuCRaXJmS2Qr"
+ + "CW\nkTmINqdQUccwb3ghb7EBZfDtCUvjcwMSEsRRTVIZqVQsYj6m3n1CegOc4o"
+ + "UutXaZ\nfkyty5XOgV4Qucx9wokzTMCHlO0V0x9y0FwFsK5Nb6ugqfQLLQ6XAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static Microdescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ return new MicrodescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyLine =
+ "ntor-onion-key PXLa7IGE+TzPDMsM5j9rFnDa37rd6kfZa5QuzqqJukw=";
+ private String idLine = "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8";
+ private static Microdescriptor createWithIdLine(String line)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.idLine = line;
+ return new MicrodescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.onionKeyLines != null) {
+ sb.append(this.onionKeyLines).append("\n");
+ }
+ if (this.ntorOnionKeyLine != null) {
+ sb.append(this.ntorOnionKeyLine).append("\n");
+ }
+ if (this.idLine != null) {
+ sb.append(this.idLine).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ @Test()
+ public void testDefaults() throws DescriptorParseException {
+ DescriptorBuilder.createWithDefaultLines();
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024TooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id rsa1024 AAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024TooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id ed25519 AAAAAAAAAAAAAAAAAAAAAA"
+ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa512() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id rsa512 "
+ + "bvegfGxp8k7T9QFpjPTrPaJTa/8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdEd25519Duplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine(
+ "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8\n"
+ + "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8");
+ }
+}
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
new file mode 100644
index 0000000..d864337
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
@@ -0,0 +1,1272 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+import org.junit.Test;
+import org.torproject.descriptor.NetworkStatusEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* TODO Add test cases for all lines starting with "opt ". */
+
+/* Test parsing of network status consensuses. The main focus is on
+ * making sure that the parser is as robust as possible and doesn't break,
+ * no matter what gets fed into it. A secondary focus is to ensure that
+ * a parsed consensus is fully compatible to dir-spec.txt. */
+public class RelayNetworkStatusConsensusImplTest {
+
+ /* Helper class to build a directory source based on default data and
+ * modifications requested by test methods. */
+ private static class DirSourceBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithDirSource(String dirSourceString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.dirSources.add(dirSourceString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String nickname = "gabelmoo";
+ private static RelayNetworkStatusConsensus
+ createWithNickname(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.nickname = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
+ private static RelayNetworkStatusConsensus
+ createWithIdentity(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.identity = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String hostName = "212.112.245.170";
+ private static RelayNetworkStatusConsensus
+ createWithHostName(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.hostName = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String address = "212.112.245.170";
+ private static RelayNetworkStatusConsensus
+ createWithAddress(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.address = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String dirPort = "80";
+ private static RelayNetworkStatusConsensus
+ createWithDirPort(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.dirPort = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String orPort = "443";
+ private static RelayNetworkStatusConsensus
+ createWithOrPort(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.orPort = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String contactLine = "contact 4096R/C5AA446D Sebastian Hahn "
+ + "<tor@xxxxxxxxxxxxxxxxx>";
+ private static RelayNetworkStatusConsensus
+ createWithContactLine(String line)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.contactLine = line;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String voteDigestLine =
+ "vote-digest 0F398A5834D2C139E1D92310B09F814F243354D1";
+ private static RelayNetworkStatusConsensus
+ createWithVoteDigestLine(String line)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.voteDigestLine = line;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String buildDirSource() {
+ StringBuilder sb = new StringBuilder();
+ String dirSourceLine = "dir-source " + this.nickname + " "
+ + this.identity + " " + this.hostName + " " + this.address + " "
+ + this.dirPort + " " + this.orPort;
+ sb.append(dirSourceLine).append("\n");
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.voteDigestLine != null) {
+ sb.append(this.voteDigestLine).append("\n");
+ }
+ String dirSourceWithTrailingNewLine = sb.toString();
+ String dirSource = dirSourceWithTrailingNewLine.substring(0,
+ dirSourceWithTrailingNewLine.length() - 1);
+ return dirSource;
+ }
+ }
+
+ /* Helper class to build a status entry based on default data and
+ * modifications requested by test methods. */
+ private static class StatusEntryBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithStatusEntry(String statusEntryString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(statusEntryString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String nickname = "right2privassy3";
+ private static RelayNetworkStatusConsensus
+ createWithNickname(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.nickname = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String fingerprintBase64 = "ADQ6gCT3DiFHKPDFr3rODBUI8HM";
+ private static RelayNetworkStatusConsensus
+ createWithFingerprintBase64(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.fingerprintBase64 = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String descriptorBase64 = "Yiti+nayuT2Efe2X1+M4nslwVuU";
+ private static RelayNetworkStatusConsensus
+ createWithDescriptorBase64(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.descriptorBase64 = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String publishedString = "2011-11-29 21:34:27";
+ private static RelayNetworkStatusConsensus
+ createWithPublishedString(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.publishedString = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String address = "50.63.8.215";
+ private static RelayNetworkStatusConsensus
+ createWithAddress(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.address = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String orPort = "9023";
+ private static RelayNetworkStatusConsensus
+ createWithOrPort(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.orPort = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String dirPort = "0";
+ private static RelayNetworkStatusConsensus
+ createWithDirPort(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.dirPort = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String sLine = "s Exit Fast Named Running Stable Valid";
+ private static RelayNetworkStatusConsensus
+ createWithSLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.sLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String vLine = "v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)";
+ private static RelayNetworkStatusConsensus
+ createWithVLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.vLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String wLine = "w Bandwidth=1";
+ private static RelayNetworkStatusConsensus
+ createWithWLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.wLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String pLine = "p accept 80,1194,1220,1293";
+ private static RelayNetworkStatusConsensus
+ createWithPLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.pLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String buildStatusEntry() {
+ StringBuilder sb = new StringBuilder();
+ String rLine = "r " + nickname + " " + fingerprintBase64 + " "
+ + descriptorBase64 + " " + publishedString + " " + address + " "
+ + orPort + " " + dirPort;
+ sb.append(rLine).append("\n");
+ if (this.sLine != null) {
+ sb.append(this.sLine).append("\n");
+ }
+ if (this.vLine != null) {
+ sb.append(this.vLine).append("\n");
+ }
+ if (this.wLine != null) {
+ sb.append(this.wLine).append("\n");
+ }
+ if (this.pLine != null) {
+ sb.append(this.pLine).append("\n");
+ }
+ String statusEntryWithTrailingNewLine = sb.toString();
+ String statusEntry = statusEntryWithTrailingNewLine.substring(0,
+ statusEntryWithTrailingNewLine.length() - 1);
+ return statusEntry;
+ }
+ }
+
+ /* Helper class to build a directory signature based on default data and
+ * modifications requested by test methods. */
+ private static class DirectorySignatureBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithDirectorySignature(String directorySignatureString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.addDirectorySignature(directorySignatureString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
+ private static RelayNetworkStatusConsensus
+ createWithIdentity(String string)
+ throws DescriptorParseException {
+ DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
+ dsb.identity = string;
+ return createWithDirectorySignature(dsb.buildDirectorySignature());
+ }
+ private String signingKey =
+ "845CF1D0B370CA443A8579D18E7987E7E532F639";
+ private static RelayNetworkStatusConsensus
+ createWithSigningKey(String string)
+ throws DescriptorParseException {
+ DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
+ dsb.signingKey = string;
+ return createWithDirectorySignature(dsb.buildDirectorySignature());
+ }
+ private String buildDirectorySignature() {
+ String directorySignature = "directory-signature " + identity + " "
+ + signingKey + "\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "gE64+/4BH43v1+7jS9FK1tu2+94at8xhVSPn4O/PpOx7b0Yb+S1hac1QHAiS"
+ + "Ll+k\n"
+ + "6OiANKzhj54WHSrUswBPrOzjmKj0OhGXSAe5nHZUFX9a1MDQLDCoZBj536X9"
+ + "P3JG\n"
+ + "z89A+wrsN17I5490y66AEvws54BYZMbgRfp8HXn/0Ss=\n"
+ + "-----END SIGNATURE-----";
+ return directorySignature;
+ }
+ }
+
+ @Test()
+ public void testSampleConsensus() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertEquals(3, consensus.getNetworkStatusVersion());
+ assertEquals(11, consensus.getConsensusMethod());
+ assertEquals(1322643600000L, consensus.getValidAfterMillis());
+ assertEquals(1322647200000L, consensus.getFreshUntilMillis());
+ assertEquals(1322654400000L, consensus.getValidUntilMillis());
+ assertEquals(300L, consensus.getVoteSeconds());
+ assertEquals(300L, consensus.getDistSeconds());
+ assertTrue(consensus.getRecommendedClientVersions().contains(
+ "0.2.3.8-alpha"));
+ assertTrue(consensus.getRecommendedServerVersions().contains(
+ "0.2.3.8-alpha"));
+ assertTrue(consensus.getKnownFlags().contains("Running"));
+ assertEquals(30000, (int) consensus.getConsensusParams().get(
+ "CircuitPriorityHalflifeMsec"));
+ assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
+ "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getHostname());
+ assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
+ "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getIp());
+ assertTrue(consensus.containsStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894"));
+ assertEquals("188.177.149.216", consensus.getStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894").getAddress());
+ for (DirectorySignature signature : consensus.getSignatures()) {
+ if ("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4".equals(
+ signature.getIdentity())) {
+ assertEquals("3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86",
+ signature.getSigningKeyDigest());
+ }
+ }
+ assertEquals(285, (int) consensus.getBandwidthWeights().get("Wbd"));
+ assertTrue(consensus.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLineSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n ");
+ }
+
+ @Test()
+ public void testNetworkStatusVersionPrefixLineAtChar()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "@consensus\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "directory-footer\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLinePoundChar()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "#consensus\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersion42()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 42");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionFourtyTwo()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version FourtyTwo");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionSpaceBefore()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ " network-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusSpaceBefore() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(" vote-status consensus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status ");
+ }
+
+ @Test()
+ public void testVoteStatusConsensusOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status consensus ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusVote() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status vote");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusTheMagicVoteStatus()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(
+ "vote-status TheMagicVoteStatus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodEleven()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(
+ "consensus-method eleven");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodMinusOne()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNinePeriod()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method "
+ + "999999999999999999999999999999999999999999999999999999999999");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodTwoLines()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(
+ "consensus-method 1\nconsensus-method 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterLongAgo() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after long ago");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterFeb30() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine(
+ "valid-after 2011-02-30 09:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithFreshUntilLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilAroundTen() throws DescriptorParseException {
+ ConsensusBuilder.createWithFreshUntilLine(
+ "fresh-until 2011-11-30 around ten");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidUntilTomorrowMorning()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithValidUntilLine(
+ "valid-until tomorrow morning");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayTriple() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine(
+ "voting-delay 300 300 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelaySingle() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneTwo() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay one two");
+ }
+
+ @Test()
+ public void testClientServerVersionsNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.clientVersionsLine = null;
+ cb.serverVersionsLine = null;
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertNull(consensus.getRecommendedClientVersions());
+ assertNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testServerVersionsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithServerVersionsLine(null);
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testClientVersionsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine(null);
+ assertNull(consensus.getRecommendedClientVersions());
+ assertNotNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testClientVersionsNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine("client-versions");
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertTrue(consensus.getRecommendedClientVersions().isEmpty());
+ }
+
+ @Test()
+ public void testClientVersionsOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine("client-versions ");
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertTrue(consensus.getRecommendedClientVersions().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsComma() throws DescriptorParseException {
+ ConsensusBuilder.createWithClientVersionsLine("client-versions ,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsCommaVersion()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithClientVersionsLine(
+ "client-versions ,0.2.2.34");
+ }
+
+ @Test()
+ public void testPackageNone() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(null);
+ assertNull(consensus.getPackageLines());
+ }
+
+ @Test()
+ public void testPackageOne() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http digest=digest";
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ assertEquals(packageLine.substring("package ".length()),
+ consensus.getPackageLines().get(0));
+ }
+
+ @Test()
+ public void testPackageTwo() throws DescriptorParseException {
+ List<String> packageLines = Arrays.asList(
+ "package shouldbesecond 0 http digest=digest",
+ "package outoforder 0 http digest=digest");
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(packageLines.get(0)
+ + "\n" + packageLines.get(1));
+ for (int i = 0; i < packageLines.size(); i++) {
+ assertEquals(packageLines.get(i).substring("package ".length()),
+ consensus.getPackageLines().get(i));
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPackageIncomplete() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http";
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine("known-flags");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine("known-flags ");
+ }
+
+ @Test()
+ public void testParamsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine(null);
+ assertNull(consensus.getConsensusParams());
+ }
+
+ @Test()
+ public void testParamsNoSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test()
+ public void testParamsOneSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params ");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test()
+ public void testParamsThreeSpaces() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params ");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsNoEqualSign() throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params key-value");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsOneTooLargeNegative()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params min=-2147483649");
+ }
+
+ @Test()
+ public void testParamsLargestNegative()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params min=-2147483648");
+ assertEquals(1, consensus.getConsensusParams().size());
+ assertEquals(-2147483648,
+ (int) consensus.getConsensusParams().get("min"));
+ }
+
+ @Test()
+ public void testParamsLargestPositive()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params max=2147483647");
+ assertEquals(1, consensus.getConsensusParams().size());
+ assertEquals(2147483647,
+ (int) consensus.getConsensusParams().get("max"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsOneTooLargePositive()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params max=2147483648");
+ }
+
+ @Test()
+ public void testDirSourceLegacyNickname()
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.nickname = "gabelmoo-legacy";
+ dsb.identity = "81349FC1F2DBA2C2C11B45CB9706637D480AB913";
+ dsb.contactLine = null;
+ dsb.voteDigestLine = null;
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithDirSource(dsb.buildDirSource());
+ assertEquals(3, consensus.getDirSourceEntries().size());
+ assertTrue(consensus.getDirSourceEntries().get(
+ "81349FC1F2DBA2C2C11B45CB9706637D480AB913").isLegacy());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceNicknameTooLong()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithNickname("gabelmooisfinebutthisistoolong");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceIdentityTooShort()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceIdentityTooLong()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111"
+ + "4BB25CEF515B226ED03BB616EB2F60BEC8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceHostnameMissing()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithHostName("");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceAddress24() throws DescriptorParseException {
+ DirSourceBuilder.createWithAddress("212.112.245");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceAddress40() throws DescriptorParseException {
+ DirSourceBuilder.createWithAddress("212.112.245.170.123");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPortMinusOne()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("-1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPort66666()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPortOnions()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("onions");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceOrPortOnions()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithOrPort("onions");
+ }
+
+ @Test()
+ public void testDirSourceContactNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine(null);
+ assertNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test()
+ public void testDirSourceContactLineNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine("contact");
+ assertNotNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test()
+ public void testDirSourceContactLineOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine("contact ");
+ assertNotNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestNoLine()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestLineNoSpace()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine("vote-digest");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestLineOneSpace()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine("vote-digest ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameNotAllowedChars()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithNickname("notAll()wed");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithNickname("1234567890123456789tooLong");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64("TooShort");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintEndsWithEqualSign()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8HMAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorTooShort() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64("TooShort");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorEndsWithEqualSign()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64(
+ "Yiti+nayuT2Efe2X1+M4nslwVuUAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished1960() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPublishedString("1960-11-29 21:34:27");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished9999() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPublishedString("9999-11-29 21:34:27");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress256() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("256.63.8.215");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress24() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("50.63.8/24");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressV6() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("::1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPort66666() throws DescriptorParseException {
+ StatusEntryBuilder.createWithOrPort("66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortEighty() throws DescriptorParseException {
+ StatusEntryBuilder.createWithOrPort("eighty");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMinusOne() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDirPort("-1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortZero() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDirPort("zero");
+ }
+
+ @Test()
+ public void testSLineNoSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithSLine("s");
+ assertTrue(consensus.getStatusEntry(
+ "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
+ }
+
+ @Test()
+ public void testSLineOneSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithSLine("s ");
+ assertTrue(consensus.getStatusEntry(
+ "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoSLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.sLine = sb.sLine + "\n" + sb.sLine;
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWLineNoSpace() throws DescriptorParseException {
+ StatusEntryBuilder.createWithWLine("w");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWLineOneSpace() throws DescriptorParseException {
+ StatusEntryBuilder.createWithWLine("w ");
+ }
+
+ @Test()
+ public void testWLineWarpSeven() throws DescriptorParseException {
+ StatusEntryBuilder.createWithWLine("w Warp=7");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoWLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.wLine = sb.wLine + "\n" + sb.wLine;
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+
+ @Test()
+ public void testWLineUnmeasured() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.wLine = "w Bandwidth=42424242 Unmeasured=1";
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
+ if (s.getBandwidth() == 42424242L) {
+ assertTrue(s.getUnmeasured());
+ }
+ }
+ }
+
+ @Test()
+ public void testWLineNotUnmeasured() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithWLine("w Bandwidth=20");
+ for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
+ assertFalse(s.getUnmeasured());
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPolicy() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPorts() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p accept");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPolicyNoPorts() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineProject() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p project 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoPLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.pLine = sb.pLine + "\n" + sb.pLine;
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+
+ @Test()
+ public void testNoStatusEntries() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.clear();
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertFalse(consensus.containsStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectoryFooterNoLine()
+ throws DescriptorParseException {
+ /* This breaks, because a bandwidth-weights line without a preceding
+ * directory-footer line is not allowed. */
+ ConsensusBuilder.createWithDirectoryFooterLine(null);
+ }
+
+ @Test()
+ public void testDirectoryFooterMissing()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.setDirectoryFooterLine(null);
+ cb.setBandwidthWeightsLine(null);
+ /* This does not break, because directory footers were optional before
+ * consensus method 9. */
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testDirectoryFooterLineSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithDirectoryFooterLine("directory-footer ");
+ }
+
+ @Test()
+ public void testBandwidthWeightsNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithBandwidthWeightsLine(null);
+ assertNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testBandwidthWeightsLineNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithBandwidthWeightsLine("bandwidth-weights");
+ assertNotNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testBandwidthWeightsLineOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithBandwidthWeightsLine("bandwidth-weights ");
+ assertNotNull(consensus.getBandwidthWeights());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthWeightsLineNoEqualSign()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithBandwidthWeightsLine(
+ "bandwidth-weights Wbd-285");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignatureIdentityTooShort()
+ throws DescriptorParseException {
+ DirectorySignatureBuilder.createWithIdentity("ED03BB616EB2F60");
+ }
+
+ @Test()
+ public void testDirectorySignatureIdentityTooLong()
+ throws DescriptorParseException {
+ /* This hex string has an unusual length of 58 hex characters, but
+ * dir-spec.txt only requires a hex string, and we can't know all hex
+ * string lengths for all future digest algorithms, so let's just
+ * accept this. */
+ DirectorySignatureBuilder.createWithIdentity(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226ED03BB616EB2F60BEC");
+ }
+
+ @Test()
+ public void testDirectorySignatureSigningKeyTooShort()
+ throws DescriptorParseException {
+ /* See above, we accept this hex string even though it's unusually
+ * short. */
+ DirectorySignatureBuilder.createWithSigningKey("845CF1D0B370CA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignatureSigningKeyTooShortOddNumber()
+ throws DescriptorParseException {
+ /* We don't accept this hex string, because it contains an odd number
+ * of hex characters. */
+ DirectorySignatureBuilder.createWithSigningKey("845");
+ }
+
+ @Test()
+ public void testDirectorySignatureSigningKeyTooLong()
+ throws DescriptorParseException {
+ /* See above, we accept this hex string even though it's unusually
+ * long. */
+ DirectorySignatureBuilder.createWithSigningKey(
+ "845CF1D0B370CA443A8579D18E7987E7E532F639845CF1D0B370CA443A");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNonAsciiByte20() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ byte[] consensusBytes = cb.buildConsensus();
+ consensusBytes[20] = (byte) 200;
+ new RelayNetworkStatusConsensusImpl(consensusBytes, true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNonAsciiByteMinusOne()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.networkStatusVersionLine = "Xnetwork-status-version 3";
+ byte[] consensusBytes = cb.buildConsensus();
+ consensusBytes[0] = (byte) 200;
+ new RelayNetworkStatusConsensusImpl(consensusBytes, true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedHeaderLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedHeaderLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedHeaderLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirSourceLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirSourceLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedStatusEntryLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedStatusEntryLine(
+ unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedStatusEntryLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedStatusEntryLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirectoryFooterLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedFooterLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirectoryFooterLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedFooterLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirectorySignatureLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedDirectorySignatureLine(
+ unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirectorySignatureLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedDirectorySignatureLine(unrecognizedLine,
+ false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
new file mode 100644
index 0000000..1c840f5
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
@@ -0,0 +1,1373 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.RelayNetworkStatusVote;
+
+/* TODO Add test cases for all lines starting with "opt ". */
+
+/* Test parsing of network status votes. Some of the vote-parsing code is
+ * already tested in the consensus-parsing tests. The tests in this class
+ * focus on the differences between votes and consensuses that are mostly
+ * in the directory header. */
+public class RelayNetworkStatusVoteImplTest {
+
+ /* Helper class to build a vote based on default data and modifications
+ * requested by test methods. */
+ private static class VoteBuilder {
+ private String networkStatusVersionLine = "network-status-version 3";
+ private static RelayNetworkStatusVote
+ createWithNetworkStatusVersionLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.networkStatusVersionLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String voteStatusLine = "vote-status vote";
+ private static RelayNetworkStatusVote
+ createWithVoteStatusLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.voteStatusLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String consensusMethodsLine =
+ "consensus-methods 1 2 3 4 5 6 7 8 9 10 11";
+ private static RelayNetworkStatusVote
+ createWithConsensusMethodsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.consensusMethodsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String publishedLine = "published 2011-11-30 08:50:01";
+ private static RelayNetworkStatusVote
+ createWithPublishedLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.publishedLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String validAfterLine = "valid-after 2011-11-30 09:00:00";
+ private static RelayNetworkStatusVote
+ createWithValidAfterLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.validAfterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
+ private static RelayNetworkStatusVote
+ createWithFreshUntilLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.freshUntilLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String validUntilLine = "valid-until 2011-11-30 12:00:00";
+ private static RelayNetworkStatusVote
+ createWithValidUntilLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.validUntilLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String votingDelayLine = "voting-delay 300 300";
+ private static RelayNetworkStatusVote
+ createWithVotingDelayLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.votingDelayLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String clientVersionsLine = "client-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ private static RelayNetworkStatusVote
+ createWithClientVersionsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.clientVersionsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String serverVersionsLine = "server-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ private static RelayNetworkStatusVote
+ createWithServerVersionsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.serverVersionsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String packageLines = null;
+ protected static RelayNetworkStatusVote
+ createWithPackageLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.packageLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String knownFlagsLine = "known-flags Authority BadExit Exit "
+ + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
+ private static RelayNetworkStatusVote
+ createWithKnownFlagsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.knownFlagsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String flagThresholdsLine = "flag-thresholds "
+ + "stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 "
+ + "guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 "
+ + "guard-bw-exc-exits=184320 enough-mtbf=1";
+ private static RelayNetworkStatusVote
+ createWithFlagThresholdsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.flagThresholdsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String paramsLine = "params "
+ + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
+ + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
+ + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
+ + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
+ private static RelayNetworkStatusVote
+ createWithParamsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.paramsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirSourceLine = "dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80";
+ private static RelayNetworkStatusVote
+ createWithDirSourceLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirSourceLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String contactLine = "contact 4096R/E012B42D Jacob Appelbaum "
+ + "<jacob@xxxxxxxxxxxxx>";
+ private static RelayNetworkStatusVote
+ createWithContactLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.contactLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String legacyDirKeyLine = null;
+ private static RelayNetworkStatusVote
+ createWithLegacyDirKeyLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.legacyDirKeyLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCertificateVersionLine =
+ "dir-key-certificate-version 3";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCertificateVersionLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCertificateVersionLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String fingerprintLine = "fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C";
+ private static RelayNetworkStatusVote
+ createWithFingerprintLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.fingerprintLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyPublishedLine = "dir-key-published 2011-04-27 "
+ + "05:34:37";
+ private static RelayNetworkStatusVote
+ createWithDirKeyPublishedLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyPublishedLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyExpiresLine = "dir-key-expires 2012-04-27 "
+ + "05:34:37";
+ private static RelayNetworkStatusVote
+ createWithDirKeyExpiresLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyExpiresLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirIdentityKeyLines = "dir-identity-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIIBigKCAYEAtKpuLgVK25sfScjsxfVU1ljofrDygt9GP7bNJl/rghX42KUT97"
+ + "5W\nrGp/fbhF7p+FcKCzNOhJFINQbRf/5E3lN8mzoamIU43QqQ9RRVf94688Us"
+ + "azVsAN\nNVT0v9J0cr387WePjenRuIE1MmiP0nmw/XdvbPTayqax7VYlcUMXGH"
+ + "l8DnWix1EN\nRwmeig+JBte0JS12oo2HG9zcSfjLJVjY6ZmvRrVycXiRxGc/Jg"
+ + "NlSrV4cxUNykaB\nJ6pO6J499OZfQu7m1vAPTENrVJ4yEfRGRwFIY+d/s8BkKc"
+ + "aiWtXAfTe31uBI6GEH\nmS3HNu1JVSuoaUiQIvVYDLMfBvMcNyAx97UT1l6E0T"
+ + "n6a7pgChrquGwXai1xGzk8\n58aXwdSFoFBSTCkyemopq5H20p/nkPAO0pHL1k"
+ + "TvcaKz9CEj4XcKm+kOmzejYmIa\nkbWNcRpXPiUZ+xmwGtsq30xrzqiONmERkx"
+ + "qlmf7bVQPFvh3Kz6hGcmTBhTbHSe9h\nzDgmdaTNn3EHAgMBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static RelayNetworkStatusVote
+ createWithDirIdentityKeyLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirIdentityKeyLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirSigningKeyLines = "dir-signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAN05qyHFQlTqykMP8yLuD4G2UuYulD4Xs8iSX5uqF+WGsUA1E4zZh4"
+ + "8h\nDFj8+drFiCu3EqhMEmVG4ACtJK2uz6D1XohUsbPWTR6LSnWJ8q6/zfTSLu"
+ + "mBGsN7\nPUXyMNjwRKL6UvrcbYk1d2mRBLO7SAP/sFW5fHhIBVeLIWrzQ19rAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static RelayNetworkStatusVote
+ createWithDirSigningKeyLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirSigningKeyLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCrosscertLines = "dir-key-crosscert\n"
+ + "-----BEGIN ID SIGNATURE-----\n"
+ + "rPBFn6IJ6TvAHj4pSwlg+RTn1fP89JGSVa08wuyJr5dAvZsdakQXvRjamT9oJU"
+ + "aZ\nnY5Rl/tRlGuSQ0BglTPPKoXdKERK0FUr9f0EKrQy7NDUgE2j9losiRuyKz"
+ + "hA3neZ\nK4yF8bhqAwM51u7fzAhIjNeRif9c04rhFJJCseco84w=\n"
+ + "-----END ID SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCrosscertLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCrosscertLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCertificationLines = "dir-key-certification\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "hPSh6FuohNF5ccjiMbkvr8cZJwGFuL11cNtwN9k0X3pUdFZVATIEkqBe7z+rE2"
+ + "PX\nPw+BGyC6wYAieoTVIhLpwKqd7DXLYjuhPZ28+7MQaDL01AqYeRp5PT01Px"
+ + "rFY0Um\nlVf95uqUitgvDT76Ne4ExWk6UvGlYB9OBgBySZz8VWe9znoMqb0uHn"
+ + "/p8IzqTApT\nAxRWXBHClntMeRqtGxaj8DcdJFn8yMxQiZG7MfDg2sq2ySPJyG"
+ + "lN+neoVDVhZiDI\n9LTNmw60gWlUp2erFeam8Mo1ZBC4DPNjQEm6QeHZFZMkhD"
+ + "uO6SwS/FL712A42+Co\nYtMaVot/p5FG2ZSBXbgl2XP5/z8ELnpmXqMbPAoWRo"
+ + "3BPNSJkIQQNog8Q5ZrK+av\nZDw5eGPltGKsXOkvuzIMM8nBeAnDPDgYvzrIFO"
+ + "bEGbvY/P8mzVAZxp3Yz+sRtNel\nC1SWz/Fx+Saex5oI7DJ3xtSD4XqKb/wYwZ"
+ + "FT8IxDYq1t2tFXdHxd4QPRVcvc0zYC\n"
+ + "-----END SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCertificationLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCertificationLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private List<String> statusEntries = null;
+ private static RelayNetworkStatusVote createWithStatusEntries(
+ List<String> statusEntries) throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.statusEntries = statusEntries;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String directoryFooterLine = "directory-footer";
+ private static RelayNetworkStatusVote
+ createWithDirectoryFooterLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.directoryFooterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String directorySignatureLines = "directory-signature "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C "
+ + "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
+ + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
+ + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirectorySignatureLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.directorySignatureLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String unrecognizedHeaderLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedHeaderLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirSourceLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedDirSourceLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedDirSourceLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedStatusEntryLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedFooterLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedFooterLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedFooterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirectorySignatureLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedDirectorySignatureLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedDirectorySignatureLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+
+ private VoteBuilder() {
+ if (this.statusEntries != null) {
+ return;
+ }
+ this.statusEntries = new ArrayList<>();
+ this.statusEntries.add("r right2privassy3 "
+ + "ADQ6gCT3DiFHKPDFr3rODBUI8HM lJY5Vf7kXec+VdkGW2flEsfkFC8 "
+ + "2011-11-12 00:03:40 50.63.8.215 9023 0\n"
+ + "s Exit Fast Guard Running Stable Valid\n"
+ + "opt v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)\n"
+ + "w Bandwidth=297 Measured=73\n"
+ + "p accept 80,1194,1220,1293,1500,1533,1677,1723,1863,"
+ + "2082-2083,2086-2087,2095-2096,2102-2104,3128,3389,3690,4321,"
+ + "4643,5050,5190,5222-5223,5228,5900,6660-6669,6679,6697,8000,"
+ + "8008,8074,8080,8087-8088,8443,8888,9418,9999-10000,19294,"
+ + "19638\n"
+ + "m 8,9,10,11 "
+ + "sha256=9ciEx9t0McXk9A06I7qwN7pxuNOdpCP64RV/6cx2Zkc");
+ }
+ private byte[] buildVote() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendDirSource(sb);
+ this.appendStatusEntries(sb);
+ this.appendFooter(sb);
+ this.appendDirectorySignature(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.networkStatusVersionLine != null) {
+ sb.append(this.networkStatusVersionLine).append("\n");
+ }
+ if (this.voteStatusLine != null) {
+ sb.append(this.voteStatusLine).append("\n");
+ }
+ if (this.consensusMethodsLine != null) {
+ sb.append(this.consensusMethodsLine).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.validAfterLine != null) {
+ sb.append(this.validAfterLine).append("\n");
+ }
+ if (this.freshUntilLine != null) {
+ sb.append(this.freshUntilLine).append("\n");
+ }
+ if (this.validUntilLine != null) {
+ sb.append(this.validUntilLine).append("\n");
+ }
+ if (this.votingDelayLine != null) {
+ sb.append(this.votingDelayLine).append("\n");
+ }
+ if (this.clientVersionsLine != null) {
+ sb.append(this.clientVersionsLine).append("\n");
+ }
+ if (this.serverVersionsLine != null) {
+ sb.append(this.serverVersionsLine).append("\n");
+ }
+ if (this.packageLines != null) {
+ sb.append(this.packageLines).append("\n");
+ }
+ if (this.knownFlagsLine != null) {
+ sb.append(this.knownFlagsLine).append("\n");
+ }
+ if (this.flagThresholdsLine != null) {
+ sb.append(this.flagThresholdsLine).append("\n");
+ }
+ if (this.paramsLine != null) {
+ sb.append(this.paramsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendDirSource(StringBuilder sb) {
+ if (this.dirSourceLine != null) {
+ sb.append(this.dirSourceLine).append("\n");
+ }
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.legacyDirKeyLine != null) {
+ sb.append(this.legacyDirKeyLine).append("\n");
+ }
+ if (this.dirKeyCertificateVersionLine != null) {
+ sb.append(this.dirKeyCertificateVersionLine).append("\n");
+ }
+ if (this.fingerprintLine != null) {
+ sb.append(this.fingerprintLine).append("\n");
+ }
+ if (this.dirKeyPublishedLine != null) {
+ sb.append(this.dirKeyPublishedLine).append("\n");
+ }
+ if (this.dirKeyExpiresLine != null) {
+ sb.append(this.dirKeyExpiresLine).append("\n");
+ }
+ if (this.dirIdentityKeyLines != null) {
+ sb.append(this.dirIdentityKeyLines).append("\n");
+ }
+ if (this.dirSigningKeyLines != null) {
+ sb.append(this.dirSigningKeyLines).append("\n");
+ }
+ if (this.dirKeyCrosscertLines != null) {
+ sb.append(this.dirKeyCrosscertLines).append("\n");
+ }
+ if (this.dirKeyCertificationLines != null) {
+ sb.append(this.dirKeyCertificationLines).append("\n");
+ }
+ if (this.unrecognizedDirSourceLine != null) {
+ sb.append(this.unrecognizedDirSourceLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ private void appendFooter(StringBuilder sb) {
+ if (this.directoryFooterLine != null) {
+ sb.append(this.directoryFooterLine).append("\n");
+ }
+ if (this.unrecognizedFooterLine != null) {
+ sb.append(this.unrecognizedFooterLine).append("\n");
+ }
+ }
+ private void appendDirectorySignature(StringBuilder sb) {
+ if (this.directorySignatureLines != null) {
+ sb.append(directorySignatureLines).append("\n");
+ }
+ if (this.unrecognizedDirectorySignatureLine != null) {
+ sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
+ }
+ }
+ }
+
+ @Test()
+ public void testSampleVote() throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ RelayNetworkStatusVote vote =
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ assertEquals(3, vote.getNetworkStatusVersion());
+ List<Integer> consensusMethods = Arrays.asList(
+ new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ assertEquals(vote.getConsensusMethods(), consensusMethods);
+ assertEquals(1322643001000L, vote.getPublishedMillis());
+ assertEquals(1322643600000L, vote.getValidAfterMillis());
+ assertEquals(1322647200000L, vote.getFreshUntilMillis());
+ assertEquals(1322654400000L, vote.getValidUntilMillis());
+ assertEquals(300L, vote.getVoteSeconds());
+ assertEquals(300L, vote.getDistSeconds());
+ assertTrue(vote.getKnownFlags().contains("Running"));
+ assertEquals(30000, (int) vote.getConsensusParams().get(
+ "CircuitPriorityHalflifeMsec"));
+ assertEquals("Tor 0.2.1.29 (r8e9b25e6c7a2e70c)",
+ vote.getStatusEntry("00343A8024F70E214728F0C5AF7ACE0C1508F073").
+ getVersion());
+ assertEquals(3, vote.getDirKeyCertificateVersion());
+ assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
+ vote.getIdentity());
+ assertEquals(1303882477000L, /* 2011-04-27 05:34:37 */
+ vote.getDirKeyPublishedMillis());
+ assertEquals(1335504877000L, /* 2012-04-27 05:34:37 */
+ vote.getDirKeyExpiresMillis());
+ assertEquals("-----BEGIN RSA PUBLIC KEY-----",
+ vote.getDirIdentityKey().split("\n")[0]);
+ assertEquals("-----BEGIN RSA PUBLIC KEY-----",
+ vote.getDirSigningKey().split("\n")[0]);
+ assertEquals("-----BEGIN ID SIGNATURE-----",
+ vote.getDirKeyCrosscert().split("\n")[0]);
+ assertEquals("-----BEGIN SIGNATURE-----",
+ vote.getDirKeyCertification().split("\n")[0]);
+ assertEquals(1, vote.getSignatures().size());
+ DirectorySignature signature = vote.getSignatures().get(0);
+ assertEquals("sha1", signature.getAlgorithm());
+ assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
+ signature.getIdentity());
+ assertEquals("EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19",
+ signature.getSigningKeyDigest());
+ assertEquals("-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
+ + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
+ + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----\n", signature.getSignature());
+ assertTrue(vote.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLineSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n ");
+ }
+
+ @Test()
+ public void testNetworkStatusVersionPrefixLineAtChar()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "@vote\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "directory-footer\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLinePoundChar()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "#vote\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersion42()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 42");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionFourtyTwo()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version FourtyTwo");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionSpaceBefore()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ " network-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusSpaceBefore() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(" vote-status vote");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status ");
+ }
+
+ @Test()
+ public void testVoteStatusVoteOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status vote ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusConsensus() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status consensus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusTheMagicVoteStatus()
+ throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(
+ "vote-status TheMagicVoteStatus");
+ }
+
+ @Test()
+ public void testConsensusMethodNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithConsensusMethodsLine(null);
+ assertNull(vote.getConsensusMethods());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodEleven()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine(
+ "consensus-methods eleven");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodMinusOne()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNinePeriod()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods "
+ + "999999999999999999999999999999999999999999999999999999999999");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodTwoLines()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine(
+ "consensus-method 1\nconsensus-method 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithPublishedLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterLongAgo() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after long ago");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterFeb30() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine(
+ "valid-after 2011-02-30 09:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithFreshUntilLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilAroundTen() throws DescriptorParseException {
+ VoteBuilder.createWithFreshUntilLine(
+ "fresh-until 2011-11-30 around ten");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidUntilTomorrowMorning()
+ throws DescriptorParseException {
+ VoteBuilder.createWithValidUntilLine(
+ "valid-until tomorrow morning");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayTriple() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine(
+ "voting-delay 300 300 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelaySingle() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneTwo() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay one two");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsComma() throws DescriptorParseException {
+ VoteBuilder.createWithClientVersionsLine("client-versions ,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsCommaVersion()
+ throws DescriptorParseException {
+ VoteBuilder.createWithClientVersionsLine(
+ "client-versions ,0.2.2.34");
+ }
+
+ @Test()
+ public void testPackageNone() throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(null);
+ assertNull(vote.getPackageLines());
+ }
+
+ @Test()
+ public void testPackageOne() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http digest=digest";
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(packageLine);
+ assertEquals(packageLine.substring("package ".length()),
+ vote.getPackageLines().get(0));
+ }
+
+ @Test()
+ public void testPackageTwo() throws DescriptorParseException {
+ List<String> packageLines = Arrays.asList(
+ "package shouldbesecond 0 http digest=digest",
+ "package outoforder 0 http digest=digest");
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(packageLines.get(0)
+ + "\n" + packageLines.get(1));
+ for (int i = 0; i < packageLines.size(); i++) {
+ assertEquals(packageLines.get(i).substring("package ".length()),
+ vote.getPackageLines().get(i));
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPackageIncomplete() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http";
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine("known-flags");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine("known-flags ");
+ }
+
+ @Test()
+ public void testFlagThresholdsLine() throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ RelayNetworkStatusVote vote =
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ assertEquals(693369L, vote.getStableUptime());
+ assertEquals(153249L, vote.getStableMtbf());
+ assertEquals(40960L, vote.getFastBandwidth());
+ assertEquals(94.669, vote.getGuardWfu(), 0.001);
+ assertEquals(691200L, vote.getGuardTk());
+ assertEquals(174080L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(184320L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(1, vote.getEnoughMtbfInfo());
+ }
+
+ @Test()
+ public void testFlagThresholdsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithFlagThresholdsLine(null);
+ assertEquals(-1L, vote.getStableUptime());
+ assertEquals(-1L, vote.getStableMtbf());
+ assertEquals(-1L, vote.getFastBandwidth());
+ assertEquals(-1.0, vote.getGuardWfu(), 0.001);
+ assertEquals(-1L, vote.getGuardTk());
+ assertEquals(-1L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(-1L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(-1, vote.getEnoughMtbfInfo());
+ }
+
+ @Test()
+ public void testFlagThresholdsAllZeroes()
+ throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds "
+ + "stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.0% "
+ + "guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 "
+ + "enough-mtbf=0");
+ assertEquals(0L, vote.getStableUptime());
+ assertEquals(0L, vote.getStableMtbf());
+ assertEquals(0L, vote.getFastBandwidth());
+ assertEquals(0.0, vote.getGuardWfu(), 0.001);
+ assertEquals(0L, vote.getGuardTk());
+ assertEquals(0L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(0L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(0, vote.getEnoughMtbfInfo());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdsNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdsOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.flagThresholdsLine = vb.flagThresholdsLine + "\n"
+ + vb.flagThresholdsLine;
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source "
+ + "urrassssssssssssssssssssssssssssssssssssssssssssssss "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameIllegalCharacters()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urra$ "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testFingerprintLowerCase() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987e1d626e3eba5e5e75a458de0626d088c 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintIllegalCharacters()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "ABCDEFGHIJKLM6E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + " 208.83.223.34 208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testHostname256()
+ throws DescriptorParseException {
+ /* This test doesn't fail, because we're not parsing the hostname. */
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 256.256.256.256 "
+ + "208.83.223.34 443 80");
+ assertEquals("256.256.256.256", vote.getHostname());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHostnameMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
+ + "80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress256()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "256.256.256.256 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
+ + "80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMinus443()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 -443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortFourFourThree()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 four-four-three 80");
+ }
+
+ @Test()
+ public void testDirPort0() throws DescriptorParseException {
+ /* This test doesn't fail, because we're accepting DirPort 0, even
+ * though it doesn't make sense from Tor's view. */
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 0 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortMissing() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 ");
+ }
+
+ @Test()
+ public void testDirPortOrPortIdentical()
+ throws DescriptorParseException {
+ /* This test doesn't fail, even though identical OR and Dir port don't
+ * make much sense from Tor's view. */
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 80 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80\ndir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testContactLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithContactLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testContactLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithContactLine("contact 4096R/E012B42D Jacob "
+ + "Appelbaum <jacob@xxxxxxxxxxxxx>\ncontact 4096R/E012B42D Jacob "
+ + "Appelbaum <jacob@xxxxxxxxxxxxx>");
+ }
+
+ @Test()
+ public void testLegacyDirKeyLine() throws DescriptorParseException {
+ RelayNetworkStatusVote vote = VoteBuilder.createWithLegacyDirKeyLine(
+ "legacy-dir-key 81349FC1F2DBA2C2C11B45CB9706637D480AB913");
+ assertEquals("81349FC1F2DBA2C2C11B45CB9706637D480AB913",
+ vote.getLegacyDirKey());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testLegacyDirKeyLineNoId() throws DescriptorParseException {
+ VoteBuilder.createWithLegacyDirKeyLine("legacy-dir-key ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificateVersionLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificateVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificateVersionLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificateVersionLine(
+ "dir-key-certificate-version 3\ndir-key-certificate-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C\nfingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineTooLong()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineTooShort()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublished3011()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "3011-04-27 05:34:37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublishedRecentlyAtNoon()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "recently 12:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublishedRecentlyNoTime()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "recently");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresSoonAtNoon()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires "
+ + "soon 12:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires 2012-04-27 "
+ + "05:34:37\ndir-key-expires 2012-04-27 05:34:37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirIdentityKeyLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirIdentityKeyLines(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSigningKeyLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSigningKeyLines(null);
+ }
+
+ @Test()
+ public void testDirKeyCrosscertLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCrosscertLines(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificationLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificationLines(null);
+ }
+
+ @Test()
+ public void testDirectoryFooterLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirectoryFooterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignaturesLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirectorySignatureLines(null);
+ }
+
+ @Test()
+ public void testDirectorySignaturesLinesTwoAlgorithms()
+ throws DescriptorParseException {
+ String identitySha256 = "32519E5CB7254AB5A94CC9925EC7676E53D5D52EEAB7"
+ + "914BD3ED751E537CAFCC";
+ String signingKeyDigestSha256 = "5A59D99C17831B9254422B6C5AA10CC59381"
+ + "6CAA5241E22ECAE8BBB4E8E9D1FC";
+ String signatureSha256 = "-----BEGIN SIGNATURE-----\n"
+ + "x57Alc424/zHS73SHokghGtNBVrBjtUz+gSL5w9AHGKUQcMyfw4Z9aDlKpTbFc"
+ + "5W\nnyIvFmM9C2OAH0S1+a647HHIxhE0zKf4+yKSwzqSyL6sbKQygVlJsRHNRr"
+ + "cFg8lp\nqBxEwvxQoA4xEDqnerR92pbK9l42nNLiKOcoReUqbbQ=\n"
+ + "-----END SIGNATURE-----";
+ String identitySha1 = "80550987E1D626E3EBA5E5E75A458DE0626D088C";
+ String signingKeyDigestSha1 =
+ "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19";
+ String signatureSha1 = "-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxnF3"
+ + "Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40OikfOI"
+ + "wEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----";
+ String signaturesLines = String.format(
+ "directory-signature sha256 %s %s\n%s\n"
+ + "directory-signature %s %s\n%s", identitySha256,
+ signingKeyDigestSha256, signatureSha256, identitySha1,
+ signingKeyDigestSha1, signatureSha1);
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
+ assertEquals(2, vote.getSignatures().size());
+ DirectorySignature firstSignature = vote.getSignatures().get(0);
+ assertEquals("sha256", firstSignature.getAlgorithm());
+ assertEquals(identitySha256, firstSignature.getIdentity());
+ assertEquals(signingKeyDigestSha256,
+ firstSignature.getSigningKeyDigest());
+ assertEquals(signatureSha256 + "\n", firstSignature.getSignature());
+ DirectorySignature secondSignature = vote.getSignatures().get(1);
+ assertEquals("sha1", secondSignature.getAlgorithm());
+ assertEquals(identitySha1, secondSignature.getIdentity());
+ assertEquals(signingKeyDigestSha1,
+ secondSignature.getSigningKeyDigest());
+ assertEquals(signatureSha1 + "\n", secondSignature.getSignature());
+ assertEquals(signingKeyDigestSha1, vote.getSigningKeyDigest());
+ }
+
+ @Test()
+ public void testDirectorySignaturesLinesTwoAlgorithmsSameDigests()
+ throws DescriptorParseException {
+ String signaturesLines = "directory-signature 00 00\n"
+ + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----\n"
+ + "directory-signature sha256 00 00\n"
+ + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----";
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
+ assertEquals(2, vote.getSignatures().size());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedHeaderLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedHeaderLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedHeaderLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirSourceLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirSourceLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedFooterLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedFooterLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedFooterLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedFooterLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testIdEd25519MasterKey()
+ throws DescriptorParseException {
+ String masterKey25519 = "8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8";
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r PDrelay1 AAFJ5u9xAqrKlpDW6N0pMhJLlKs "
+ + "bgJiI/la3e9u0K7cQ5pMSXhigHI 2015-12-01 04:54:30 95.215.44.189 "
+ + "8080 0\n"
+ + "id ed25519 " + masterKey25519);
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ String fingerprint = vote.getStatusEntries().firstKey();
+ assertEquals(masterKey25519,
+ vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
+ }
+
+ @Test()
+ public void testIdEd25519None()
+ throws DescriptorParseException {
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
+ + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
+ + "443 9030\n"
+ + "id ed25519 none");
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ String fingerprint = vote.getStatusEntries().firstKey();
+ assertEquals("none",
+ vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024None()
+ throws DescriptorParseException {
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
+ + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
+ + "443 9030\n"
+ + "id rsa1024 none");
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
new file mode 100644
index 0000000..cd3f1a5
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -0,0 +1,1605 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.SortedMap;
+
+import org.junit.Test;
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* Test parsing of relay server descriptors. */
+public class ServerDescriptorImplTest {
+
+ /* Helper class to build a descriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String routerLine = "router saberrider2008 94.134.192.243 "
+ + "9001 0 0";
+ private static ServerDescriptor createWithRouterLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String bandwidthLine = "bandwidth 51200 51200 53470";
+ private static ServerDescriptor createWithBandwidthLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.bandwidthLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String platformLine = "platform Tor 0.2.2.35 "
+ + "(git-b04388f9e7546a9f) on Linux i686";
+ private static ServerDescriptor createWithPlatformLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.platformLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String publishedLine = "published 2012-01-01 04:03:19";
+ private static ServerDescriptor createWithPublishedLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.publishedLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String fingerprintLine = "opt fingerprint D873 3048 FC8E "
+ + "C910 2466 AD8F 3098 622B F1BF 71FD";
+ private static ServerDescriptor createWithFingerprintLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.fingerprintLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hibernatingLine = null;
+ private static ServerDescriptor createWithHibernatingLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hibernatingLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String uptimeLine = "uptime 48";
+ private static ServerDescriptor createWithUptimeLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.uptimeLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String onionKeyLines = "onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
+ + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
+ + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static ServerDescriptor createWithOnionKeyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.onionKeyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String signingKeyLines = "signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
+ + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
+ + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static ServerDescriptor createWithSigningKeyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.signingKeyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String onionKeyCrosscertLines = null;
+ private static ServerDescriptor createWithOnionKeyCrosscertLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.onionKeyCrosscertLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyCrosscertLines = null;
+ private static ServerDescriptor createWithNtorOnionKeyCrosscertLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ntorOnionKeyCrosscertLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String exitPolicyLines = "reject *:*";
+ private static ServerDescriptor createWithExitPolicyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.exitPolicyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String contactLine = "contact Random Person <nobody AT "
+ + "example dot com>";
+ private static ServerDescriptor createWithContactLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.contactLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String familyLine = null;
+ private static ServerDescriptor createWithFamilyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.familyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String readHistoryLine = null;
+ private static ServerDescriptor createWithReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.readHistoryLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String writeHistoryLine = null;
+ private static ServerDescriptor createWithWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.writeHistoryLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String eventdnsLine = null;
+ private static ServerDescriptor createWithEventdnsLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.eventdnsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String cachesExtraInfoLine = null;
+ private static ServerDescriptor createWithCachesExtraInfoLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.cachesExtraInfoLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String extraInfoDigestLine = "opt extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74";
+ private static ServerDescriptor createWithExtraInfoDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.extraInfoDigestLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hiddenServiceDirLine = "opt hidden-service-dir";
+ private static ServerDescriptor createWithHiddenServiceDirLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hiddenServiceDirLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String protocolsLine = "opt protocols Link 1 2 Circuit 1";
+ private static ServerDescriptor createWithProtocolsLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.protocolsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String allowSingleHopExitsLine = null;
+ private static ServerDescriptor
+ createWithAllowSingleHopExitsLine(String line)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.allowSingleHopExitsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ipv6PolicyLine = null;
+ private static ServerDescriptor createWithIpv6PolicyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ipv6PolicyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyLine = null;
+ private static ServerDescriptor createWithNtorOnionKeyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ntorOnionKeyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String tunnelledDirServerLine = null;
+ private static ServerDescriptor createWithTunnelledDirServerLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.tunnelledDirServerLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String routerSignatureLines = "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----";
+ private static ServerDescriptor createWithRouterSignatureLines(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerSignatureLines = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String unrecognizedLine = null;
+ private static ServerDescriptor createWithUnrecognizedLine(
+ String line, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.unrecognizedLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private byte[] nonAsciiLineBytes = null;
+ private static ServerDescriptor createWithNonAsciiLineBytes(
+ byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.nonAsciiLineBytes = lineBytes;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String identityEd25519Lines = null,
+ masterKeyEd25519Line = null, routerSigEd25519Line = null;
+ private static ServerDescriptor createWithEd25519Lines(
+ String identityEd25519Lines, String masterKeyEd25519Line,
+ String routerSigEd25519Line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.identityEd25519Lines = identityEd25519Lines;
+ db.masterKeyEd25519Line = masterKeyEd25519Line;
+ db.routerSigEd25519Line = routerSigEd25519Line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.routerLine != null) {
+ sb.append(this.routerLine).append("\n");
+ }
+ if (this.identityEd25519Lines != null) {
+ sb.append(this.identityEd25519Lines).append("\n");
+ }
+ if (this.masterKeyEd25519Line != null) {
+ sb.append(this.masterKeyEd25519Line).append("\n");
+ }
+ if (this.bandwidthLine != null) {
+ sb.append(this.bandwidthLine).append("\n");
+ }
+ if (this.platformLine != null) {
+ sb.append(this.platformLine).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.fingerprintLine != null) {
+ sb.append(this.fingerprintLine).append("\n");
+ }
+ if (this.hibernatingLine != null) {
+ sb.append(this.hibernatingLine).append("\n");
+ }
+ if (this.uptimeLine != null) {
+ sb.append(this.uptimeLine).append("\n");
+ }
+ if (this.onionKeyLines != null) {
+ sb.append(this.onionKeyLines).append("\n");
+ }
+ if (this.signingKeyLines != null) {
+ sb.append(this.signingKeyLines).append("\n");
+ }
+ if (this.onionKeyCrosscertLines != null) {
+ sb.append(this.onionKeyCrosscertLines).append("\n");
+ }
+ if (this.ntorOnionKeyCrosscertLines != null) {
+ sb.append(this.ntorOnionKeyCrosscertLines).append("\n");
+ }
+ if (this.exitPolicyLines != null) {
+ sb.append(this.exitPolicyLines).append("\n");
+ }
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.familyLine != null) {
+ sb.append(this.familyLine).append("\n");
+ }
+ if (this.readHistoryLine != null) {
+ sb.append(this.readHistoryLine).append("\n");
+ }
+ if (this.writeHistoryLine != null) {
+ sb.append(this.writeHistoryLine).append("\n");
+ }
+ if (this.eventdnsLine != null) {
+ sb.append(this.eventdnsLine).append("\n");
+ }
+ if (this.cachesExtraInfoLine != null) {
+ sb.append(this.cachesExtraInfoLine).append("\n");
+ }
+ if (this.extraInfoDigestLine != null) {
+ sb.append(this.extraInfoDigestLine).append("\n");
+ }
+ if (this.hiddenServiceDirLine != null) {
+ sb.append(this.hiddenServiceDirLine).append("\n");
+ }
+ if (this.protocolsLine != null) {
+ sb.append(this.protocolsLine).append("\n");
+ }
+ if (this.allowSingleHopExitsLine != null) {
+ sb.append(this.allowSingleHopExitsLine).append("\n");
+ }
+ if (this.ipv6PolicyLine != null) {
+ sb.append(this.ipv6PolicyLine).append("\n");
+ }
+ if (this.ntorOnionKeyLine != null) {
+ sb.append(this.ntorOnionKeyLine).append("\n");
+ }
+ if (this.tunnelledDirServerLine != null) {
+ sb.append(this.tunnelledDirServerLine).append("\n");
+ }
+ if (this.unrecognizedLine != null) {
+ sb.append(this.unrecognizedLine).append("\n");
+ }
+ if (this.nonAsciiLineBytes != null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(sb.toString().getBytes());
+ baos.write(this.nonAsciiLineBytes);
+ baos.write("\n".getBytes());
+ if (this.routerSignatureLines != null) {
+ baos.write(this.routerSignatureLines.getBytes());
+ }
+ return baos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ if (this.routerSigEd25519Line != null) {
+ sb.append(this.routerSigEd25519Line).append("\n");
+ }
+ if (this.routerSignatureLines != null) {
+ sb.append(this.routerSignatureLines).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ @Test()
+ public void testSampleDescriptor() throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ ServerDescriptor descriptor =
+ new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ assertEquals(9001, (int) descriptor.getOrPort());
+ assertEquals(0, (int) descriptor.getSocksPort());
+ assertEquals(0, (int) descriptor.getDirPort());
+ assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
+ descriptor.getPlatform());
+ assertEquals(Arrays.asList(new Integer[] {1, 2}),
+ descriptor.getLinkProtocolVersions());
+ assertEquals(Arrays.asList(new Integer[] {1}),
+ descriptor.getCircuitProtocolVersions());
+ assertEquals(1325390599000L, descriptor.getPublishedMillis());
+ assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
+ descriptor.getFingerprint());
+ assertEquals(48, descriptor.getUptime().longValue());
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(53470, (int) descriptor.getBandwidthObserved());
+ assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
+ descriptor.getExtraInfoDigest());
+ assertEquals(Arrays.asList(new Integer[] {2}),
+ descriptor.getHiddenServiceDirVersions());
+ assertEquals("Random Person <nobody AT example dot com>",
+ descriptor.getContact());
+ assertEquals(Arrays.asList(new String[] {"reject *:*"}),
+ descriptor.getExitPolicyLines());
+ assertFalse(descriptor.isHibernating());
+ assertNull(descriptor.getFamilyEntries());
+ assertNull(descriptor.getReadHistory());
+ assertNull(descriptor.getWriteHistory());
+ assertFalse(descriptor.getUsesEnhancedDnsLogic());
+ assertFalse(descriptor.getCachesExtraInfo());
+ assertFalse(descriptor.getAllowSingleHopExits());
+ assertTrue(descriptor.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterLineMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine(null);
+ }
+
+ @Test()
+ public void testRouterOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithRouterLine("opt router saberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ assertEquals(9001, (int) descriptor.getOrPort());
+ assertEquals(0, (int) descriptor.getSocksPort());
+ assertEquals(0, (int) descriptor.getDirPort());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterLinePrecedingHibernatingLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("hibernating 1\nrouter "
+ + "saberrider2008 94.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router 94.134.192.243 9001 "
+ + "0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameInvalidChar() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router $aberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router "
+ + "saberrider2008ReallyLongNickname 94.134.192.243 9001 0 0");
+ }
+
+ @Test()
+ public void testNicknameTwoSpaces() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress24() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192/24 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress294() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "294.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 9001 "
+ + "0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPort99001() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 99001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortOne() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 one 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortNewline() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 0\n 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 9001 0 ");
+ }
+
+ @Test()
+ public void testPlatformMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine(null);
+ assertNull(descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("opt platform Tor 0.2.2.35 "
+ + "(git-b04388f9e7546a9f) on Linux i686");
+ assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
+ descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformNoSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("platform");
+ assertEquals("", descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("platform ");
+ assertEquals("", descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testProtocolsNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithProtocolsLine("protocols Link 1 2 Circuit 1");
+ assertEquals(Arrays.asList(new Integer[] {1, 2}),
+ descriptor.getLinkProtocolVersions());
+ assertEquals(Arrays.asList(new Integer[] {1}),
+ descriptor.getCircuitProtocolVersions());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testProtocolsAB() throws DescriptorParseException {
+ DescriptorBuilder.createWithProtocolsLine("opt protocols Link A B "
+ + "Circuit 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testProtocolsNoCircuitVersions()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithProtocolsLine("opt protocols Link 1 2");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine(null);
+ }
+
+ @Test()
+ public void testPublishedOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-01-01 04:03:19");
+ assertEquals(1325390599000L, descriptor.getPublishedMillis());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished2039() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2039-01-01 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished1912() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 1912-01-01 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedFeb31() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2012-02-31 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedNoTime() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2012-01-01");
+ }
+
+ @Test()
+ public void testPublishedMillis() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-01-01 04:03:19.123");
+ assertEquals(1325390599000L, descriptor.getPublishedMillis());
+ }
+
+ @Test()
+ public void testFingerprintNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFingerprintLine("fingerprint D873 3048 FC8E C910 2466 "
+ + "AD8F 3098 622B F1BF 71FD");
+ assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
+ descriptor.getFingerprint());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintG() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint G873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD D873");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintNoSpaces() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint "
+ + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ }
+
+ @Test()
+ public void testUptimeMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUptimeLine(null);
+ assertNull(descriptor.getUptime());
+ }
+
+ @Test()
+ public void testUptimeOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUptimeLine("opt uptime 48");
+ assertEquals(48, descriptor.getUptime().longValue());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeFourtyEight() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime fourty-eight");
+ }
+
+ @Test()
+ public void testUptimeMinusOne() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeNoSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeFourEight() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime 4 8");
+ }
+
+ @Test()
+ public void testBandwidthOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithBandwidthLine("opt bandwidth 51200 51200 53470");
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(53470, (int) descriptor.getBandwidthObserved());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthOneValue() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth 51200");
+ }
+
+ @Test()
+ public void testBandwidthTwoValues() throws DescriptorParseException {
+ /* This is allowed, because Tor versions 0.0.8 and older only wrote
+ * bandwidth lines with rate and burst values, but no observed
+ * value. */
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithBandwidthLine("bandwidth 51200 51200");
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(-1, (int) descriptor.getBandwidthObserved());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthFourValues() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth 51200 51200 "
+ + "53470 53470");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthMinusOneTwoThree()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth -1 -2 -3");
+ }
+
+ @Test()
+ public void testExtraInfoDigestNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine("extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74");
+ assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
+ descriptor.getExtraInfoDigest());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestNoSpace()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestTooShort()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest 1469D1550738A25B1E7B47CDDBCD7B2899F5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestTooLong()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B741469");
+ }
+
+ @Test()
+ public void testExtraInfoDigestMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine(null);
+ assertNull(descriptor.getExtraInfoDigest());
+ }
+
+ @Test()
+ public void testExtraInfoDigestAdditionalDigest()
+ throws DescriptorParseException {
+ String extraInfoDigest = "0879DB7B765218D7B3AE7557669D20307BB21CAA";
+ String additionalExtraInfoDigest =
+ "V609l+N6ActBveebfNbH5lQ6wHDNstDkFgyqEhBHwtA";
+ String extraInfoDigestLine = String.format("extra-info-digest %s %s",
+ extraInfoDigest, additionalExtraInfoDigest);
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine(extraInfoDigestLine);
+ assertEquals(extraInfoDigest, descriptor.getExtraInfoDigest());
+ }
+
+ @Test()
+ public void testOnionKeyOpt() throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyLines("opt onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
+ + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
+ + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----");
+ }
+
+ @Test()
+ public void testSigningKeyOpt() throws DescriptorParseException {
+ DescriptorBuilder.createWithSigningKeyLines("opt signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
+ + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
+ + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----");
+ }
+
+ @Test()
+ public void testHiddenServiceDirMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine(null);
+ assertNull(descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testHiddenServiceDirNoOpt()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine("hidden-service-dir");
+ assertEquals(Arrays.asList(new Integer[] {2}),
+ descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testHiddenServiceDirVersions2And3()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine("hidden-service-dir 2 3");
+ assertEquals(Arrays.asList(new Integer[] {2, 3}),
+ descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testContactMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine(null);
+ assertNull(descriptor.getContact());
+ }
+
+ @Test()
+ public void testContactOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("opt contact Random Person");
+ assertEquals("Random Person", descriptor.getContact());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testContactDuplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithContactLine("contact Random "
+ + "Person\ncontact Random Person");
+ }
+
+ @Test()
+ public void testContactNoSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("contact");
+ assertEquals("", descriptor.getContact());
+ }
+
+ @Test()
+ public void testContactCarriageReturn()
+ throws DescriptorParseException {
+ String contactString = "Random "
+ + "Person -----BEGIN PGP PUBLIC KEY BLOCK-----\r"
+ + "Version: GnuPG v1 dot 4 dot 7 (Darwin)\r\r"
+ + "mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd"
+ + "07\rmTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESm"
+ + "oGEdAX\roid3XuIYRpRnqoafbFg9sg+OofX/mGrO+5ACfagQ9rlfx2oxCWijYw"
+ + "pYFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("contact " + contactString);
+ assertEquals(contactString, descriptor.getContact());
+ }
+
+ @Test()
+ public void testExitPolicyRejectAllAcceptAll()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("reject *:*\naccept *:*");
+ assertEquals(Arrays.asList(new String[] {"reject *:*", "accept *:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test()
+ public void testExitPolicyOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("opt reject *:*");
+ assertEquals(Arrays.asList(new String[] {"reject *:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyNoPort() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject *");
+ }
+
+ @Test()
+ public void testExitPolicyAccept80RejectAll()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("accept *:80\nreject *:*");
+ assertEquals(Arrays.asList(new String[] {"accept *:80",
+ "reject *:*"}), descriptor.getExitPolicyLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyReject321() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject "
+ + "123.123.123.321:80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyRejectPort66666()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject *:66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyProjectAll() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("project *:*");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines(null);
+ }
+
+ @Test()
+ public void testExitPolicyMaskTypes() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("reject 192.168.0.0/16:*\n"
+ + "reject 94.134.192.243/255.255.255.0:*");
+ assertEquals(Arrays.asList(new String[] { "reject 192.168.0.0/16:*",
+ "reject 94.134.192.243/255.255.255.0:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test()
+ public void testRouterSignatureOpt()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("opt "
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "crypto lines are ignored anyway\n"
+ + "-----END SIGNATURE-----");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterSignatureNotLastLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----\ncontact me");
+ }
+
+ @Test()
+ public void testHibernatingOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("opt hibernating 1");
+ assertTrue(descriptor.isHibernating());
+ }
+
+ @Test()
+ public void testHibernatingFalse() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("hibernating 0");
+ assertFalse(descriptor.isHibernating());
+ }
+
+ @Test()
+ public void testHibernatingTrue() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("hibernating 1");
+ assertTrue(descriptor.isHibernating());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHibernatingYep() throws DescriptorParseException {
+ DescriptorBuilder.createWithHibernatingLine("hibernating yep");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHibernatingNoSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithHibernatingLine("hibernating");
+ }
+
+ @Test()
+ public void testFamilyOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("opt family saberrider2008");
+ assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyFingerprint() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ assertEquals(Arrays.asList(new String[] {
+ "$D8733048FC8EC9102466AD8F3098622BF1BF71FD"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyNickname() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family saberrider2008");
+ assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyDuplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family "
+ + "saberrider2008\nfamily saberrider2008");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyNicknamePrefix() throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family $saberrider2008");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyFingerprintNoPrefix()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family "
+ + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ }
+
+ @Test()
+ public void testFamilyFingerprintNicknameNamed()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008");
+ assertEquals(Arrays.asList(new String[]
+ { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008" }),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyFingerprintNicknameUnnamed()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008");
+ assertEquals(Arrays.asList(new String[]
+ { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008" }),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testWriteHistory() throws DescriptorParseException {
+ String writeHistoryLine = "write-history 2012-01-01 03:51:44 (900 s) "
+ + "4345856,261120,7591936,1748992";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine(writeHistoryLine);
+ assertNotNull(descriptor.getWriteHistory());
+ BandwidthHistory parsedWriteHistory = descriptor.getWriteHistory();
+ assertEquals(writeHistoryLine, parsedWriteHistory.getLine());
+ assertEquals(1325389904000L, (long) parsedWriteHistory.
+ getHistoryEndMillis());
+ assertEquals(900L, (long) parsedWriteHistory.getIntervalLength());
+ SortedMap<Long, Long> bandwidthValues = parsedWriteHistory.
+ getBandwidthValues();
+ assertEquals(4345856L, (long) bandwidthValues.remove(1325387204000L));
+ assertEquals(261120L, (long) bandwidthValues.remove(1325388104000L));
+ assertEquals(7591936L, (long) bandwidthValues.remove(1325389004000L));
+ assertEquals(1748992L, (long) bandwidthValues.remove(1325389904000L));
+ assertTrue(bandwidthValues.isEmpty());
+ }
+
+ @Test()
+ public void testWriteHistoryOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("opt write-history 2012-01-01 "
+ + "03:51:44 (900 s) 4345856,261120,7591936,1748992");
+ assertNotNull(descriptor.getWriteHistory());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistory3012() throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "3012-01-01 03:51:44 (900 s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoSeconds()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51 (900 s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoParathenses()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 900 s 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoSpaceSeconds()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryTrailingComma()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) 4345856,261120,7591936,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryOneTwoThree()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) one,two,three");
+ }
+
+ @Test()
+ public void testWriteHistoryNoValuesSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(900 s) ");
+ assertEquals(900, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ assertTrue(descriptor.getWriteHistory().getBandwidthValues().
+ isEmpty());
+ }
+
+ @Test()
+ public void testWriteHistoryNoValuesNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(900 s)");
+ assertEquals(900, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ assertTrue(descriptor.getWriteHistory().getBandwidthValues().
+ isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoS() throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine(
+ "write-history 2012-01-01 03:51:44 (900 ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryTrailingNumber()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) 4345856 1");
+ }
+
+ @Test()
+ public void testWriteHistory1800Seconds()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(1800 s) 4345856");
+ assertEquals(1800L, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ }
+
+ @Test()
+ public void testReadHistory() throws DescriptorParseException {
+ String readHistoryLine = "read-history 2012-01-01 03:51:44 (900 s) "
+ + "4268032,139264,7797760,1415168";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithReadHistoryLine(readHistoryLine);
+ assertNotNull(descriptor.getReadHistory());
+ BandwidthHistory parsedReadHistory = descriptor.getReadHistory();
+ assertEquals(readHistoryLine, parsedReadHistory.getLine());
+ assertEquals(1325389904000L, (long) parsedReadHistory.
+ getHistoryEndMillis());
+ assertEquals(900L, (long) parsedReadHistory.getIntervalLength());
+ SortedMap<Long, Long> bandwidthValues = parsedReadHistory.
+ getBandwidthValues();
+ assertEquals(4268032L, (long) bandwidthValues.remove(1325387204000L));
+ assertEquals(139264L, (long) bandwidthValues.remove(1325388104000L));
+ assertEquals(7797760L, (long) bandwidthValues.remove(1325389004000L));
+ assertEquals(1415168L, (long) bandwidthValues.remove(1325389904000L));
+ assertTrue(bandwidthValues.isEmpty());
+ }
+
+ @Test()
+ public void testReadHistoryTwoSpaces() throws DescriptorParseException {
+ /* There are some server descriptors from older Tor versions that
+ * contain "opt read-history " lines. */
+ String readHistoryLine = "opt read-history 2012-01-01 03:51:44 "
+ + "(900 s) 4268032,139264,7797760,1415168";
+ DescriptorBuilder.createWithReadHistoryLine(readHistoryLine);
+ }
+
+ @Test()
+ public void testEventdnsOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("opt eventdns 1");
+ assertTrue(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test()
+ public void testEventdns1() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("eventdns 1");
+ assertTrue(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test()
+ public void testEventdns0() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("eventdns 0");
+ assertFalse(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEventdnsTrue() throws DescriptorParseException {
+ DescriptorBuilder.createWithEventdnsLine("eventdns true");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEventdnsNo() throws DescriptorParseException {
+ DescriptorBuilder.createWithEventdnsLine("eventdns no");
+ }
+
+ @Test()
+ public void testCachesExtraInfoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithCachesExtraInfoLine("opt caches-extra-info");
+ assertTrue(descriptor.getCachesExtraInfo());
+ }
+
+ @Test()
+ public void testCachesExtraInfoNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithCachesExtraInfoLine("caches-extra-info");
+ assertTrue(descriptor.getCachesExtraInfo());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCachesExtraInfoTrue() throws DescriptorParseException {
+ DescriptorBuilder.createWithCachesExtraInfoLine("caches-extra-info "
+ + "true");
+ }
+
+ @Test()
+ public void testAllowSingleHopExitsOpt()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithAllowSingleHopExitsLine("opt allow-single-hop-exits");
+ assertTrue(descriptor.getAllowSingleHopExits());
+ }
+
+ @Test()
+ public void testAllowSingleHopExitsNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithAllowSingleHopExitsLine("allow-single-hop-exits");
+ assertTrue(descriptor.getAllowSingleHopExits());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAllowSingleHopExitsTrue()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithAllowSingleHopExitsLine(
+ "allow-single-hop-exits true");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAllowSingleHopExitsNonAsciiKeyword()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
+ 0x14, (byte) 0xfe, 0x18, // non-ascii chars
+ 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2d, // "allow-"
+ 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2d, // "single-"
+ 0x68, 0x6f, 0x70, 0x2d, // "hop-"
+ 0x65, 0x78, 0x69, 0x74, 0x73 }, // "exits" (no newline)
+ false);
+ }
+
+ @Test()
+ public void testIpv6PolicyLine() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithIpv6PolicyLine("ipv6-policy accept 80,1194,1220,1293");
+ assertEquals("accept", descriptor.getIpv6DefaultPolicy());
+ assertEquals("80,1194,1220,1293", descriptor.getIpv6PortList());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPolicy()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPorts()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy accept");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPolicyNoPorts()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineProject()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy project 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoIpv6PolicyLines() throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine(
+ "ipv6-policy accept 80,1194,1220,1293\n"
+ + "ipv6-policy accept 80,1194,1220,1293");
+ }
+
+ @Test()
+ public void testNtorOnionKeyLine() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY=");
+ assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
+ descriptor.getNtorOnionKey());
+ }
+
+ @Test()
+ public void testNtorOnionKeyLineNoPadding()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
+ assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
+ descriptor.getNtorOnionKey());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyLineNoKey()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyLineTwoKeys()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoNtorOnionKeyLines() throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\nntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\n");
+ }
+
+ @Test()
+ public void testTunnelledDirServerTrue()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder
+ .createWithTunnelledDirServerLine("tunnelled-dir-server");
+ assertTrue(descriptor.getTunnelledDirServer());
+ }
+
+ @Test()
+ public void testTunnelledDirServerFalse()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder
+ .createWithTunnelledDirServerLine(null);
+ assertFalse(descriptor.getTunnelledDirServer());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerTypo()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunneled-dir-server");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerTwice()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunnelled-dir-server\ntunnelled-dir-server");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerArgs()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunnelled-dir-server 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testSomeOtherKey() throws DescriptorParseException {
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add("some-other-key");
+ unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
+ unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
+ + "1U4V9SeiKooSo5BpPL");
+ unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
+ + "IynCI4QryfCEuC3cTF");
+ unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
+ + "facOkpAgMBAAE=");
+ unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
+ StringBuilder sb = new StringBuilder();
+ for (String line : unrecognizedLines) {
+ sb.append("\n").append(line);
+ }
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(sb.toString().substring(1), false);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testUnrecognizedCryptoBlockNoKeyword()
+ throws DescriptorParseException {
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
+ unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
+ + "1U4V9SeiKooSo5BpPL");
+ unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
+ + "IynCI4QryfCEuC3cTF");
+ unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
+ + "facOkpAgMBAAE=");
+ unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
+ StringBuilder sb = new StringBuilder();
+ for (String line : unrecognizedLines) {
+ sb.append("\n").append(line);
+ }
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(sb.toString().substring(1), false);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ private static final String IDENTITY_ED25519_LINES =
+ "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
+ + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
+ + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
+ + "\n"
+ + "-----END ED25519 CERT-----";
+
+ private static final String MASTER_KEY_ED25519_LINE =
+ "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
+
+ private static final String ROUTER_SIG_ED25519_LINE =
+ "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
+ + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
+
+ @Test()
+ public void testEd25519() throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ assertEquals(IDENTITY_ED25519_LINES.substring(
+ IDENTITY_ED25519_LINES.indexOf("\n") + 1),
+ descriptor.getIdentityEd25519());
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ assertEquals(ROUTER_SIG_ED25519_LINE.substring(
+ ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getRouterSignatureEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityMasterKeyMismatch()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519IdentityMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(null,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
+ + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityEmptyCrypto()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519MasterKeyMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ null, ROUTER_SIG_ED25519_LINE);
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519MasterKeyDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519RouterSigMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519RouterSigDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
+ + ROUTER_SIG_ED25519_LINE);
+ }
+
+ private static final String ONION_KEY_CROSSCERT_LINES =
+ "onion-key-crosscert\n"
+ + "-----BEGIN CROSSCERT-----\n"
+ + "gVWpiNgG2FekW1uonr4KKoqykjr4bqUBKGZfu6s9rvsV1TThnquZNP6ZhX2IPdQA"
+ + "\nlfKtzFggGu/4BiJ5oTSDj2sK2DMjY3rjrMQZ3I/wJ25yhc9gxjqYqUYO9MmJwA"
+ + "Lp\nfYkqp/t4WchJpyva/4hK8vITsI6eT2BfY/DWMy/suIE=\n"
+ + "-----END CROSSCERT-----";
+
+ private static final String NTOR_ONION_KEY_CROSSCERT_LINES =
+ "ntor-onion-key-crosscert 1\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQoABiUeAdauu1MxYGMmGLTCPaoes0RvW7udeLc1t8LZ4P3CDo5bAN4nrRfbCfOt"
+ + "\nz2Nwqn8tER1a+Ry6Vs+ilMZA55Rag4+f6Zdb1fmHWknCxbQlLHpqHACMtemPda"
+ + "Ka\nErPtMuiEqAc=\n"
+ + "-----END ED25519 CERT-----";
+
+ @Test()
+ public void testOnionKeyCrosscert() throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ ONION_KEY_CROSSCERT_LINES);
+ assertEquals(ONION_KEY_CROSSCERT_LINES.substring(
+ ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
+ descriptor.getOnionKeyCrosscert());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOnionKeyCrosscertDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ ONION_KEY_CROSSCERT_LINES + "\n" + ONION_KEY_CROSSCERT_LINES);
+ }
+
+ @Test()
+ public void testNtorOnionKeyCrosscert()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithNtorOnionKeyCrosscertLines(
+ NTOR_ONION_KEY_CROSSCERT_LINES);
+ assertEquals(NTOR_ONION_KEY_CROSSCERT_LINES.substring(
+ NTOR_ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
+ descriptor.getNtorOnionKeyCrosscert());
+ assertEquals(1, descriptor.getNtorOnionKeyCrosscertSign());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyCrosscertDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ NTOR_ONION_KEY_CROSSCERT_LINES + "\n"
+ + NTOR_ONION_KEY_CROSSCERT_LINES);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
new file mode 100644
index 0000000..b5cde0a
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
@@ -0,0 +1,97 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.Descriptor;
+
+public class TorperfResultImplTest {
+
+ @Test()
+ public void testAnnotatedInput() throws Exception{
+ TorperfResultImpl result = (TorperfResultImpl)
+ (TorperfResultImpl.parseTorperfResults((torperfAnnotation + input)
+ .getBytes("US-ASCII"), false).get(0));
+ assertEquals("Expected one annotation.", 1,
+ result.getAnnotations().size());
+ assertEquals(torperfAnnotation.substring(0, 17),
+ result.getAnnotations().get(0));
+ int count = 0;
+ for (Long l: result.getDataPercentiles().values()) {
+ assertNotNull(l);
+ assertEquals(l.longValue(), deciles[count++]);
+ }
+ }
+
+ @Test()
+ public void testPartiallyAnnotatedInput() throws Exception{
+ byte[] asciiBytes = (torperfAnnotation
+ + input + input + input).getBytes("US-ASCII");
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ asciiBytes, false);
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
+ assertEquals(3, result.size());
+ assertEquals("Expected zero annotations.", 0,
+ ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
+ assertEquals("Expected zero annotations.", 0,
+ ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
+ }
+
+ @Test()
+ public void testAllAnnotatedInput() throws Exception {
+ byte[] asciiBytes = (torperfAnnotation + input
+ + torperfAnnotation + input
+ + torperfAnnotation + input).getBytes("US-ASCII");
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ asciiBytes, false);
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
+ assertEquals(3, result.size());
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
+ }
+
+ private static long[] deciles = new long[] {
+ 1441065602980L, 1441065603030L, 1441065603090L, 1441065603120L,
+ 1441065603230L, 1441065603250L, 1441065603310L, 1441065603370L,
+ 1441065603370L };
+
+ private static final String torperfAnnotation = "@type torperf 1.0\n";
+
+ private static final String input =
+ "BUILDTIMES=0.872834920883,1.09103679657,1.49180984497 "
+ + "CIRC_ID=1228 CONNECT=1441065601.86 DATACOMPLETE=1441065603.39 "
+ + "DATAPERC10=1441065602.98 DATAPERC20=1441065603.03 "
+ + "DATAPERC30=1441065603.09 DATAPERC40=1441065603.12 "
+ + "DATAPERC50=1441065603.23 DATAPERC60=1441065603.25 "
+ + "DATAPERC70=1441065603.31 DATAPERC80=1441065603.37 "
+ + "DATAPERC90=1441065603.37 DATAREQUEST=1441065602.38 "
+ + "DATARESPONSE=1441065602.84 DIDTIMEOUT=0 FILESIZE=51200 "
+ + "LAUNCH=1441065361.30 NEGOTIATE=1441065601.86 "
+ + "PATH=$C4C9C332D25B3546BEF4E1250CF410E97EF996E6,"
+ + "$C43FA6474A9F071E9120DF63ED6EB8FDBA105234,"
+ + "$7C0AA4E3B73E407E9F5FEB1912F8BE26D8AA124D QUANTILE=0.800000 "
+ + "READBYTES=51416 REQUEST=1441065601.86 RESPONSE=1441065602.38 "
+ + "SOCKET=1441065601.86 SOURCE=moria START=1441065601.86 "
+ + "TIMEOUT=1500 USED_AT=1441065603.40 USED_BY=2475 WRITEBYTES=75\n";
+
+ @Test()
+ public void testDatapercNonNumeric() throws Exception {
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ ("DATAPERMILLE=2.0 " + input).getBytes(), false);
+ assertEquals(1, result.size());
+ TorperfResultImpl torperfResult = (TorperfResultImpl) result.get(0);
+ assertEquals(1, torperfResult.getUnrecognizedKeys().size());
+ assertEquals("DATAPERMILLE",
+ torperfResult.getUnrecognizedKeys().firstKey());
+ }
+}
+
diff --git a/test/org/torproject/descriptor/benchmark/MeasurePerformance.java b/test/org/torproject/descriptor/benchmark/MeasurePerformance.java
deleted file mode 100644
index a52020a..0000000
--- a/test/org/torproject/descriptor/benchmark/MeasurePerformance.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/* Copyright 2016 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.benchmark;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorReader;
-import org.torproject.descriptor.DescriptorSourceFactory;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.Microdescriptor;
-import org.torproject.descriptor.NetworkStatusEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-import org.torproject.descriptor.ServerDescriptor;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedMap;
-
-public class MeasurePerformance {
-
- /* Check if all necessary files are available and then measure
- * performance of some more or less common use cases. */
- public static void main(String[] args) {
- if (!filesAvailable()) {
- return;
- }
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[0]));
- pause();
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[1]));
- pause();
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[2]));
- pause();
- measureCountriesV3Requests(new File(resDir, resPaths[3]));
- pause();
- measureCountriesV3Requests(new File(resDir, resPaths[4]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[5]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[6]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[7]));
- measureFractionRelaysExit80Microdescriptors(
- new File(resDir, resPaths[8]));
- measureFractionRelaysExit80Microdescriptors(
- new File(resDir, resPaths[9]));
- }
-
- private static File resDir = new File("res");
- private static String[] resPaths = new String[] {
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11.tar.xz",
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11.tar",
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11",
- "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar.xz",
- "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar",
- "archive/relay-descriptors/consensuses/consensuses-2015-11.tar.xz",
- "archive/relay-descriptors/consensuses/consensuses-2015-11.tar",
- "archive/relay-descriptors/consensuses/consensuses-2015-11",
- "archive/relay-descriptors/microdescs/microdescs-2015-11.tar.xz",
- "archive/relay-descriptors/microdescs/microdescs-2015-11.tar"
- };
-
- private static boolean filesAvailable() {
- if (!resDir.exists() || !resDir.isDirectory()) {
- return false;
- }
- for (String resPath : resPaths) {
- if (!(new File(resDir, resPath).exists())) {
- System.err.println("Missing resource: " + resDir + "/" + resPath);
- return false;
- }
- }
- return true;
- }
-
- private static void pause() {
- try {
- Thread.sleep(15L * 1000L);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private static void measureAverageAdvertisedBandwidth(
- File tarballFileOrDirectory) {
- System.out.println("Starting measureAverageAdvertisedBandwidth");
- long startedMillis = System.currentTimeMillis();
- long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFileOrDirectory);
- descriptorReader.addDirectory(tarballFileOrDirectory);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof ServerDescriptor)) {
- continue;
- }
- ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
- sumAdvertisedBandwidth += (long) Math.min(Math.min(
- serverDescriptor.getBandwidthRate(),
- serverDescriptor.getBandwidthBurst()),
- serverDescriptor.getBandwidthObserved());
- countedServerDescriptors++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureAverageAdvertisedBandwidth");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed server descriptors: %d%n",
- countedServerDescriptors);
- System.out.printf("Average advertised bandwidth: %d%n",
- sumAdvertisedBandwidth / countedServerDescriptors);
- System.out.printf("Time per server descriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedServerDescriptors));
- }
-
- private static void measureCountriesV3Requests(File tarballFile) {
- System.out.println("Starting measureCountriesV3Requests");
- long startedMillis = System.currentTimeMillis();
- Set<String> countries = new HashSet<>();
- long countedExtraInfoDescriptors = 0;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFile);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof ExtraInfoDescriptor)) {
- continue;
- }
- ExtraInfoDescriptor extraInfoDescriptor =
- (ExtraInfoDescriptor) descriptor;
- SortedMap<String, Integer> dirreqV3Reqs =
- extraInfoDescriptor.getDirreqV3Reqs();
- if (dirreqV3Reqs != null) {
- countries.addAll(dirreqV3Reqs.keySet());
- }
- countedExtraInfoDescriptors++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureCountriesV3Requests");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed extra-info descriptors: %d%n",
- countedExtraInfoDescriptors);
- System.out.printf("Number of countries: %d%n",
- countries.size());
- System.out.printf("Time per extra-info descriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedExtraInfoDescriptors));
- }
-
- private static void measureAverageRelaysExit(
- File tarballFileOrDirectory) {
- System.out.println("Starting measureAverageRelaysExit");
- long startedMillis = System.currentTimeMillis();
- long totalRelaysWithExitFlag = 0L, totalRelays = 0L,
- countedConsensuses = 0L;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFileOrDirectory);
- descriptorReader.addDirectory(tarballFileOrDirectory);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
- continue;
- }
- RelayNetworkStatusConsensus consensus =
- (RelayNetworkStatusConsensus) descriptor;
- for (NetworkStatusEntry entry :
- consensus.getStatusEntries().values()) {
- if (entry.getFlags().contains("Exit")) {
- totalRelaysWithExitFlag++;
- }
- totalRelays++;
- }
- countedConsensuses++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureAverageRelaysExit");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed consensuses: %d%n", countedConsensuses);
- System.out.printf("Total number of status entries: %d%n",
- totalRelays);
- System.out.printf("Total number of status entries with Exit flag: "
- + "%d%n", totalRelaysWithExitFlag);
- System.out.printf("Average number of relays with Exit Flag: %.2f%n",
- (double) totalRelaysWithExitFlag / (double) totalRelays);
- System.out.printf("Time per consensus: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedConsensuses));
- }
-
- private static void measureFractionRelaysExit80Microdescriptors(
- File tarballFile) {
- System.out.println("Starting "
- + "measureFractionRelaysExit80Microdescriptors");
- long startedMillis = System.currentTimeMillis();
- long totalRelaysWithExitFlag = 0L, countedMicrodescriptors = 0L;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFile);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof Microdescriptor)) {
- continue;
- }
- countedMicrodescriptors++;
- Microdescriptor microdescriptor =
- (Microdescriptor) descriptor;
- String defaultPolicy = microdescriptor.getDefaultPolicy();
- if (defaultPolicy == null) {
- continue;
- }
- boolean accept = "accept".equals(
- microdescriptor.getDefaultPolicy());
- for (String ports : microdescriptor.getPortList().split(",")) {
- if (ports.contains("-")) {
- String[] parts = ports.split("-");
- int from = Integer.parseInt(parts[0]);
- int to = Integer.parseInt(parts[1]);
- if (from <= 80 && to >= 80) {
- if (accept) {
- totalRelaysWithExitFlag++;
- }
- } else if (to > 80) {
- if (!accept) {
- totalRelaysWithExitFlag++;
- }
- break;
- }
- } else if ("80".equals(ports)) {
- if (accept) {
- totalRelaysWithExitFlag++;
- }
- break;
- }
- }
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending "
- + "measureFractionRelaysExit80Microdescriptors");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed microdescriptors: %d%n",
- countedMicrodescriptors);
- System.out.printf("Total number of microdescriptors that exit to 80: "
- + "%d%n", totalRelaysWithExitFlag);
- System.out.printf("Average number of relays that exit to 80: %.2f%n",
- (double) totalRelaysWithExitFlag
- / (double) countedMicrodescriptors);
- System.out.printf("Time per microdescriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedMicrodescriptors));
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
deleted file mode 100644
index 0847e13..0000000
--- a/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.BridgeNetworkStatus;
-import org.torproject.descriptor.DescriptorParseException;
-
-/* Test parsing of bridge network statuses. Some of the parsing code is
- * already tested in the consensus/vote-parsing tests. */
-public class BridgeNetworkStatusTest {
-
- /* Helper class to build a bridge network status based on default data
- * and modifications requested by test methods. */
- private static class StatusBuilder {
- private String fileName = "20151121-173936-"
- + "4A0CCD2DDC7995083D73F5D667100C8A5831F16D";
- private static BridgeNetworkStatus
- createWithFileName(String fileName)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.fileName = fileName;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private String publishedLine = "published 2015-11-21 17:39:36";
- private static BridgeNetworkStatus
- createWithPublishedLine(String line)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.publishedLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private String flagThresholdsLine = "flag-thresholds "
- + "stable-uptime=3105080 stable-mtbf=2450615 fast-speed=55000 "
- + "guard-wfu=98.000% guard-tk=691200 guard-bw-inc-exits=337000 "
- + "guard-bw-exc-exits=339000 enough-mtbf=1 "
- + "ignoring-advertised-bws=0";
- private static BridgeNetworkStatus
- createWithFlagThresholdsLine(String line)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.flagThresholdsLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private List<String> statusEntries = new ArrayList<>();
- private String unrecognizedHeaderLine = null;
- protected static BridgeNetworkStatus
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.unrecognizedHeaderLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static BridgeNetworkStatus
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.unrecognizedStatusEntryLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- failUnrecognizedDescriptorLines);
- }
-
- private StatusBuilder() {
- this.statusEntries.add("r Unnamed ABk0wg4j6BLCdZKleVtmNrfzJGI "
- + "bh7gVU1Cz6+JG+7j4qGsF4prDi8 2015-11-21 15:46:25 "
- + "10.153.163.200 443 0\ns Fast Running Stable Valid\n"
- + "w Bandwidth=264\np reject 1-65535");
- }
- private byte[] buildStatus() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendStatusEntries(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.flagThresholdsLine != null) {
- sb.append(this.flagThresholdsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- }
-
- @Test()
- public void testSampleStatus() throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- BridgeNetworkStatus status =
- new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName, true);
- assertEquals(1448127576000L, status.getPublishedMillis());
- assertEquals(3105080L, status.getStableUptime());
- assertEquals(2450615L, status.getStableMtbf());
- assertEquals(55000L, status.getFastBandwidth());
- assertEquals(98.0, status.getGuardWfu(), 0.001);
- assertEquals(691200L, status.getGuardTk());
- assertEquals(337000L, status.getGuardBandwidthIncludingExits());
- assertEquals(339000L, status.getGuardBandwidthExcludingExits());
- assertEquals(1, status.getEnoughMtbfInfo());
- assertEquals(0, status.getIgnoringAdvertisedBws());
- assertEquals(264, status.getStatusEntries().get(
- "001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
- assertTrue(status.getUnrecognizedLines().isEmpty());
- }
-
- @Test()
- public void testPublishedNoLine() throws DescriptorParseException {
- BridgeNetworkStatus status =
- StatusBuilder.createWithPublishedLine(null);
- assertEquals(1448127576000L, status.getPublishedMillis());
- }
-
- @Test()
- public void testFlagThresholdsNoLine() throws DescriptorParseException {
- BridgeNetworkStatus status =
- StatusBuilder.createWithFlagThresholdsLine(null);
- assertEquals(-1L, status.getStableUptime());
- assertEquals(-1L, status.getStableMtbf());
- assertEquals(-1L, status.getFastBandwidth());
- assertEquals(-1.0, status.getGuardWfu(), 0.001);
- assertEquals(-1L, status.getGuardTk());
- assertEquals(-1L, status.getGuardBandwidthIncludingExits());
- assertEquals(-1L, status.getGuardBandwidthExcludingExits());
- assertEquals(-1, status.getEnoughMtbfInfo());
- assertEquals(-1, status.getIgnoringAdvertisedBws());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ConsensusBuilder.java b/test/org/torproject/descriptor/impl/ConsensusBuilder.java
deleted file mode 100644
index 29a2d47..0000000
--- a/test/org/torproject/descriptor/impl/ConsensusBuilder.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* Helper class to build a consensus based on default data and
- * modifications requested by test methods. */
-public class ConsensusBuilder {
- String networkStatusVersionLine = "network-status-version 3";
- protected static RelayNetworkStatusConsensus
- createWithNetworkStatusVersionLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.networkStatusVersionLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String voteStatusLine = "vote-status consensus";
- protected static RelayNetworkStatusConsensus
- createWithVoteStatusLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.voteStatusLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String consensusMethodLine = "consensus-method 11";
- protected static RelayNetworkStatusConsensus
- createWithConsensusMethodLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.consensusMethodLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String validAfterLine = "valid-after 2011-11-30 09:00:00";
- protected static RelayNetworkStatusConsensus
- createWithValidAfterLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.validAfterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
- protected static RelayNetworkStatusConsensus
- createWithFreshUntilLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.freshUntilLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String validUntilLine = "valid-until 2011-11-30 12:00:00";
- protected static RelayNetworkStatusConsensus
- createWithValidUntilLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.validUntilLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String votingDelayLine = "voting-delay 300 300";
- protected static RelayNetworkStatusConsensus
- createWithVotingDelayLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.votingDelayLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- String clientVersionsLine = "client-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- protected static RelayNetworkStatusConsensus
- createWithClientVersionsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.clientVersionsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- String serverVersionsLine = "server-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- protected static RelayNetworkStatusConsensus
- createWithServerVersionsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.serverVersionsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String packageLines = null;
- protected static RelayNetworkStatusConsensus
- createWithPackageLines(String lines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.packageLines = lines;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String knownFlagsLine = "known-flags Authority BadExit Exit "
- + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
- protected static RelayNetworkStatusConsensus
- createWithKnownFlagsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.knownFlagsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String paramsLine = "params "
- + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
- + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
- + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
- + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
- protected static RelayNetworkStatusConsensus
- createWithParamsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.paramsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- List<String> dirSources = new ArrayList<>();
- List<String> statusEntries = new ArrayList<>();
- private String directoryFooterLine = "directory-footer";
- protected void setDirectoryFooterLine(String line) {
- this.directoryFooterLine = line;
- }
- protected static RelayNetworkStatusConsensus
- createWithDirectoryFooterLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.directoryFooterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String bandwidthWeightsLine = "bandwidth-weights Wbd=285 "
- + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=1021 Wee=10000 "
- + "Weg=1021 Wem=10000 Wgb=10000 Wgd=8694 Wgg=10000 Wgm=10000 "
- + "Wmb=10000 Wmd=285 Wme=0 Wmg=0 Wmm=10000";
- protected void setBandwidthWeightsLine(String line) {
- this.bandwidthWeightsLine = line;
- }
- protected static RelayNetworkStatusConsensus
- createWithBandwidthWeightsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.bandwidthWeightsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private List<String> directorySignatures = new ArrayList<>();
- protected void addDirectorySignature(String directorySignatureString) {
- this.directorySignatures.add(directorySignatureString);
- }
- private String unrecognizedHeaderLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedHeaderLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirSourceLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedDirSourceLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedDirSourceLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedStatusEntryLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedFooterLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedFooterLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedFooterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirectorySignatureLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedDirectorySignatureLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedDirectorySignatureLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
-
- protected ConsensusBuilder() {
- this.dirSources.add("dir-source tor26 "
- + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 "
- + "86.59.21.38 80 443\ncontact Peter Palfrader\nvote-digest "
- + "0333880AA67ED7E07C11108656D0C8D6DD1C7E5D");
- this.dirSources.add("dir-source ides "
- + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 216.224.124.114 "
- + "216.224.124.114 9030 9090\ncontact Mike Perry "
- + "<mikeperryTAfsckedTODorg>\nvote-digest "
- + "1A8827ECD53184F7A771EFA9B3D30DC473FE8670");
- this.statusEntries.add("r ANONIONROUTER "
- + "AHhuQ8zFQJdT8l42Axxc6m6kNwI yEMZ5B/JQixNZgC1+2rLe0pR9rU "
- + "2011-11-30 02:52:58 93.128.66.111 24051 24052\ns Exit Fast "
- + "Named Running V2Dir Valid\nv Tor 0.2.2.34\nw "
- + "Bandwidth=1100\np reject 25,119,135-139,6881-6999");
- this.statusEntries.add("r Magellan AHlabo2RwnD8I7MPOIpJVVPgGJQ "
- + "rB/7uzI4mU38bZ9cSXEy+Z/4Cuk 2011-11-30 05:37:35 "
- + "188.177.149.216 9001 9030\ns Fast Named Running V2Dir "
- + "Valid\nv Tor 0.2.2.34\nw Bandwidth=367\np reject 1-65535");
- this.directorySignatures.add("directory-signature "
- + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
- + "3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "NYRcTWAMRiYYiGW0hIbzeZKU6sefg98AwwXrQUCudO8wfA1cfgttTDoscB9I"
- + "TbOY\nr+c30jV/qQCMamTAEDGgJTw8KghI32vytupKallI1EjCOF8UvL1UnA"
- + "LgpaR7sZ3W\n7WQZVVrWDtnYaULOEKfwnGnRC7WwE+YRSysbzwwCVs0=\n"
- + "-----END SIGNATURE-----");
- this.directorySignatures.add("directory-signature "
- + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 "
- + "D5C30C15BB3F1DA27669C2D88439939E8F418FCF\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "DzFPj3vyYrCv0W3r8qDPJPlmeLnadY+drjWkdOqO66Ih/hAWBb9KcBJAX1sX"
- + "aDA7\n/iSaDhduBXuJdcu8lbmMP8d6uYBdRjHXqWDXySUZAkSfPB4JJPNGvf"
- + "oQA/qeby7E\n5374pPPL6WwCLJHkKtk21S9oHDmFBdlZq7JWQelWlVM=\n"
- + "-----END SIGNATURE-----");
- }
- protected byte[] buildConsensus() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendDirSources(sb);
- this.appendStatusEntries(sb);
- this.appendFooter(sb);
- this.appendDirectorySignatures(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.networkStatusVersionLine != null) {
- sb.append(this.networkStatusVersionLine).append("\n");
- }
- if (this.voteStatusLine != null) {
- sb.append(this.voteStatusLine).append("\n");
- }
- if (this.consensusMethodLine != null) {
- sb.append(this.consensusMethodLine).append("\n");
- }
- if (this.validAfterLine != null) {
- sb.append(this.validAfterLine).append("\n");
- }
- if (this.freshUntilLine != null) {
- sb.append(this.freshUntilLine).append("\n");
- }
- if (this.validUntilLine != null) {
- sb.append(this.validUntilLine).append("\n");
- }
- if (this.votingDelayLine != null) {
- sb.append(this.votingDelayLine).append("\n");
- }
- if (this.clientVersionsLine != null) {
- sb.append(this.clientVersionsLine).append("\n");
- }
- if (this.serverVersionsLine != null) {
- sb.append(this.serverVersionsLine).append("\n");
- }
- if (this.packageLines != null) {
- sb.append(this.packageLines).append("\n");
- }
- if (this.knownFlagsLine != null) {
- sb.append(this.knownFlagsLine).append("\n");
- }
- if (this.paramsLine != null) {
- sb.append(this.paramsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendDirSources(StringBuilder sb) {
- for (String dirSource : this.dirSources) {
- sb.append(dirSource).append("\n");
- }
- if (this.unrecognizedDirSourceLine != null) {
- sb.append(this.unrecognizedDirSourceLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- private void appendFooter(StringBuilder sb) {
- if (this.directoryFooterLine != null) {
- sb.append(this.directoryFooterLine).append("\n");
- }
- if (this.bandwidthWeightsLine != null) {
- sb.append(this.bandwidthWeightsLine).append("\n");
- }
- if (this.unrecognizedFooterLine != null) {
- sb.append(this.unrecognizedFooterLine).append("\n");
- }
- }
- private void appendDirectorySignatures(StringBuilder sb) {
- for (String directorySignature : this.directorySignatures) {
- sb.append(directorySignature).append("\n");
- }
- if (this.unrecognizedDirectorySignatureLine != null) {
- sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
- }
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java b/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
deleted file mode 100644
index fde8e57..0000000
--- a/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.util.SortedMap;
-
-import org.junit.Test;
-
-public class DescriptorCollectorImplTest {
-
- private static final String REMOTE_DIRECTORY_CONSENSUSES =
- "/recent/relay-descriptors/consensuses/";
-
- @Test()
- public void testOneFile() {
- String remoteFilename = "2015-05-24-12-00-00-consensus";
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"" + remoteFilename + "\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">24-May-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertSame(1, remoteFiles.size());
- assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
- remoteFiles.firstKey());
- assertEquals((Long) 1432469280000L,
- remoteFiles.get(remoteFiles.firstKey()));
- }
-
- @Test()
- public void testSameFileTwoTimestampsLastWins() {
- String remoteFilename = "2015-05-24-12-00-00-consensus";
- String firstTimestamp = "24-May-2015 12:04";
- String secondTimestamp = "24-May-2015 12:08";
- String lineFormat = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"%s\">2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">%s </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>\n";
- String directoryListing = String.format(lineFormat + lineFormat,
- remoteFilename, firstTimestamp, remoteFilename, secondTimestamp);
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertSame(1, remoteFiles.size());
- assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
- remoteFiles.firstKey());
- assertEquals((Long) 1432469280000L,
- remoteFiles.get(remoteFiles.firstKey()));
- }
-
- @Test()
- public void testSubDirectoryOnly() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/folder.gif\" alt=\"[DIR]\"></td><td>"
- + "<a href=\"subdir/\">subdir/</a></td>"
- + "<td align=\"right\">27-May-2015 14:07 </td>"
- + "<td align=\"right\"> - </td><td> </td></tr>";
- DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
- SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testParentDirectoryOnly() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/back.gif\" alt=\"[DIR]\"></td><td>"
- + "<a href=\"/recent/relay-descriptors/\">Parent Directory</a>"
- + "</td><td> </td><td align=\"right\"> - </td>"
- + "<td> </td></tr>";
- DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
- SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testUnexpectedDateFormat() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">2015-05-24 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testInvalidDate() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">34-May-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNull(remoteFiles);
- }
-
- @Test()
- public void testInvalidLocaleDe() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">24-Mai-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNull(remoteFiles);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ExitListImplTest.java b/test/org/torproject/descriptor/impl/ExitListImplTest.java
deleted file mode 100644
index a563857..0000000
--- a/test/org/torproject/descriptor/impl/ExitListImplTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Test;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListImplTest {
-
- @Test()
- public void testAnnotatedInput() throws Exception {
- ExitListImpl result = new ExitListImpl((tordnselAnnotation + input)
- .getBytes("US-ASCII"), fileName, false);
- assertEquals("Expected one annotation.", 1,
- result.getAnnotations().size());
- assertEquals(tordnselAnnotation.substring(0, 18),
- result.getAnnotations().get(0));
- assertEquals(1441065722000L, result.getDownloadedMillis());
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- assertEquals("Found: " + result.getExitListEntries(), 7,
- result.getExitListEntries().size());
- assertEquals("Found: " + result.getEntries(), 5,
- result.getEntries().size());
- }
-
- @Test()
- public void testMultipleOldExitAddresses() throws Exception {
- ExitListImpl result = new ExitListImpl(
- (tordnselAnnotation + multiExitAddressInput)
- .getBytes("US-ASCII"), fileName, false);
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- assertEquals("Found: " + result.getExitListEntries(),
- 3, result.getExitListEntries().size());
- Map<String, Long> testMap = new HashMap();
- testMap.put("81.7.17.171", 1441044592000L);
- testMap.put("81.7.17.172", 1441044652000L);
- testMap.put("81.7.17.173", 1441044712000L);
- for (ExitListEntry ele : result.getExitListEntries()) {
- Map<String, Long> map = ele.getExitAddresses();
- assertEquals("Found: " + map, 1, map.size());
- Map.Entry<String, Long> ea = map.entrySet().iterator().next();
- assertTrue("Map: " + testMap,
- testMap.keySet().contains(ea.getKey()));
- assertTrue("Map: " + testMap + " exitaddress: " + ea,
- testMap.values().contains(ea.getValue()));
- testMap.remove(ea.getKey());
- }
- assertTrue("Map: " + testMap, testMap.isEmpty());
- }
-
- @Test()
- public void testMultipleExitAddresses() throws Exception {
- ExitListImpl result = new ExitListImpl(
- (tordnselAnnotation + multiExitAddressInput)
- .getBytes("US-ASCII"), fileName, false);
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- Map<String, Long> map = result.getEntries()
- .iterator().next().getExitAddresses();
- assertEquals("Found: " + map, 3, map.size());
- assertTrue("Map: " + map, map.containsKey("81.7.17.171"));
- assertTrue("Map: " + map, map.containsKey("81.7.17.172"));
- assertTrue("Map: " + map, map.containsKey("81.7.17.173"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testInsufficientInput0() throws Exception {
- new ExitListImpl((tordnselAnnotation + insufficientInput[0])
- .getBytes("US-ASCII"), fileName, false);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testInsufficientInput1() throws Exception {
- new ExitListImpl((tordnselAnnotation + insufficientInput[1])
- .getBytes("US-ASCII"), fileName, false);
- }
-
- private static final String tordnselAnnotation = "@type tordnsel 1.0\n";
- private static final String fileName = "2015-09-01-00-02-02";
- private static final String[] insufficientInput = new String[] {
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n",
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n" };
-
- private static final String multiExitAddressInput =
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
- + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n";
- private static final String input = "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 162.247.72.201 2015-08-31 17:09:23\n"
- + "ExitNode 0098C475875ABC4AA864738B1D1079F711C38287\n"
- + "Published 2015-08-31 13:59:24\n"
- + "LastStatus 2015-08-31 15:03:20\n"
- + "ExitAddress 162.248.160.151 2015-08-31 15:07:27\n"
- + "ExitNode 00C4B4731658D3B4987132A3F77100CFCB190D97\n"
- + "Published 2015-08-31 17:47:52\n"
- + "LastStatus 2015-08-31 18:03:17\n"
- + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
- + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n"
- + "ExitNode 00F2D93EBAF2F51D6EE4DCB0F37D91D72F824B16\n"
- + "Published 2015-08-31 14:39:05\n"
- + "LastStatus 2015-08-31 16:02:18\n"
- + "ExitAddress 23.239.18.57 2015-08-31 16:06:07\n"
- + "ExitNode 011B1D1E876B2C835D01FB9D407F2E00B28077F6\n"
- + "Published 2015-08-31 05:14:35\n"
- + "LastStatus 2015-08-31 06:03:29\n"
- + "ExitAddress 104.131.51.150 2015-08-31 06:04:07\n";
-}
-
diff --git a/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
deleted file mode 100644
index 6843196..0000000
--- a/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
+++ /dev/null
@@ -1,1737 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-
-import org.junit.Test;
-import org.torproject.descriptor.BridgeExtraInfoDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.RelayExtraInfoDescriptor;
-
-/* Test parsing of extra-info descriptors. */
-public class ExtraInfoDescriptorImplTest {
-
- /* Helper class to build a descriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String extraInfoLine = "extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26";
- private static ExtraInfoDescriptor createWithExtraInfoLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.extraInfoLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String publishedLine = "published 2012-02-11 09:08:36";
- private static ExtraInfoDescriptor createWithPublishedLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.publishedLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String writeHistoryLine = "write-history 2012-02-11 09:03:39 "
- + "(900 s) 4713350144,4723824640,4710717440,4572675072";
- private static ExtraInfoDescriptor createWithWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.writeHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String readHistoryLine = "read-history 2012-02-11 09:03:39 "
- + "(900 s) 4707695616,4699666432,4650004480,4489718784";
- private static ExtraInfoDescriptor createWithReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.readHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqWriteHistoryLine = "dirreq-write-history "
- + "2012-02-11 09:03:39 (900 s) 81281024,64996352,60625920,"
- + "67922944";
- private static ExtraInfoDescriptor createWithDirreqWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqWriteHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqReadHistoryLine = "dirreq-read-history "
- + "2012-02-11 09:03:39 (900 s) 17074176,16235520,16005120,"
- + "16209920";
- private static ExtraInfoDescriptor createWithDirreqReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqReadHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoipDbDigestLine = null;
- private static ExtraInfoDescriptor createWithGeoipDbDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoipDbDigestLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoip6DbDigestLine = null;
- private static ExtraInfoDescriptor createWithGeoip6DbDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoip6DbDigestLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoipStatsLines = null;
- private static ExtraInfoDescriptor createWithGeoipStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoipStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqStatsLines = null;
- private static ExtraInfoDescriptor createWithDirreqStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String entryStatsLines = null;
- private static ExtraInfoDescriptor createWithEntryStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.entryStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String cellStatsLines = null;
- private static ExtraInfoDescriptor createWithCellStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.cellStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String connBiDirectLine = null;
- private static ExtraInfoDescriptor createWithConnBiDirectLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.connBiDirectLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String exitStatsLines = null;
- private static ExtraInfoDescriptor createWithExitStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.exitStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String bridgeStatsLines = null;
- private static ExtraInfoDescriptor createWithBridgeStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.bridgeStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hidservStatsLines = null;
- private static ExtraInfoDescriptor createWithHidservStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hidservStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String unrecognizedLine = null;
- private static ExtraInfoDescriptor createWithUnrecognizedLine(
- String line, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.unrecognizedLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private byte[] nonAsciiLineBytes = null;
- private static ExtraInfoDescriptor createWithNonAsciiLineBytes(
- byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.nonAsciiLineBytes = lineBytes;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private String routerSignatureLines = "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----";
- private static ExtraInfoDescriptor createWithRouterSignatureLines(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerSignatureLines = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String identityEd25519Lines = null,
- masterKeyEd25519Line = null, routerSigEd25519Line = null;
- private static ExtraInfoDescriptor createWithEd25519Lines(
- String identityEd25519Lines, String masterKeyEd25519Line,
- String routerSigEd25519Line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.identityEd25519Lines = identityEd25519Lines;
- db.masterKeyEd25519Line = masterKeyEd25519Line;
- db.routerSigEd25519Line = routerSigEd25519Line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.extraInfoLine != null) {
- sb.append(this.extraInfoLine).append("\n");
- }
- if (this.identityEd25519Lines != null) {
- sb.append(this.identityEd25519Lines).append("\n");
- }
- if (this.masterKeyEd25519Line != null) {
- sb.append(this.masterKeyEd25519Line).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.writeHistoryLine != null) {
- sb.append(this.writeHistoryLine).append("\n");
- }
- if (this.readHistoryLine != null) {
- sb.append(this.readHistoryLine).append("\n");
- }
- if (this.dirreqWriteHistoryLine != null) {
- sb.append(this.dirreqWriteHistoryLine).append("\n");
- }
- if (this.dirreqReadHistoryLine != null) {
- sb.append(this.dirreqReadHistoryLine).append("\n");
- }
- if (this.geoipDbDigestLine != null) {
- sb.append(this.geoipDbDigestLine).append("\n");
- }
- if (this.geoip6DbDigestLine != null) {
- sb.append(this.geoip6DbDigestLine).append("\n");
- }
- if (this.geoipStatsLines != null) {
- sb.append(this.geoipStatsLines).append("\n");
- }
- if (this.dirreqStatsLines != null) {
- sb.append(this.dirreqStatsLines).append("\n");
- }
- if (this.entryStatsLines != null) {
- sb.append(this.entryStatsLines).append("\n");
- }
- if (this.cellStatsLines != null) {
- sb.append(this.cellStatsLines).append("\n");
- }
- if (this.connBiDirectLine != null) {
- sb.append(this.connBiDirectLine).append("\n");
- }
- if (this.exitStatsLines != null) {
- sb.append(this.exitStatsLines).append("\n");
- }
- if (this.bridgeStatsLines != null) {
- sb.append(this.bridgeStatsLines).append("\n");
- }
- if (this.hidservStatsLines != null) {
- sb.append(this.hidservStatsLines).append("\n");
- }
- if (this.unrecognizedLine != null) {
- sb.append(this.unrecognizedLine).append("\n");
- }
- if (this.nonAsciiLineBytes != null) {
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(sb.toString().getBytes());
- baos.write(this.nonAsciiLineBytes);
- baos.write("\n".getBytes());
- if (this.routerSignatureLines != null) {
- baos.write(this.routerSignatureLines.getBytes());
- }
- return baos.toByteArray();
- } catch (IOException e) {
- return null;
- }
- }
- if (this.routerSigEd25519Line != null) {
- sb.append(this.routerSigEd25519Line).append("\n");
- }
- if (this.routerSignatureLines != null) {
- sb.append(this.routerSignatureLines).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- /* Helper class to build a set of geoip-stats lines based on default
- * data and modifications requested by test methods. */
- private static class GeoipStatsBuilder {
- private String geoipStartTimeLine = "geoip-start-time 2012-02-10 "
- + "18:32:51";
- private static ExtraInfoDescriptor createWithGeoipStartTimeLine(
- String line) throws DescriptorParseException {
- GeoipStatsBuilder gsb = new GeoipStatsBuilder();
- gsb.geoipStartTimeLine = line;
- return DescriptorBuilder.createWithGeoipStatsLines(
- gsb.buildGeoipStatsLines());
- }
- private String geoipClientOriginsLine = "geoip-client-origins "
- + "de=1152,cn=896,us=712,it=504,ru=352,fr=208,gb=208,ir=200";
- private static ExtraInfoDescriptor createWithGeoipClientOriginsLine(
- String line) throws DescriptorParseException {
- GeoipStatsBuilder gsb = new GeoipStatsBuilder();
- gsb.geoipClientOriginsLine = line;
- return DescriptorBuilder.createWithGeoipStatsLines(
- gsb.buildGeoipStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithGeoipStatsLines(
- new GeoipStatsBuilder().buildGeoipStatsLines());
- }
- private String buildGeoipStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.geoipStartTimeLine != null) {
- sb.append(this.geoipStartTimeLine).append("\n");
- }
- if (this.geoipClientOriginsLine != null) {
- sb.append(this.geoipClientOriginsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of dirreq-stats lines based on default
- * data and modifications requested by test methods. */
- private static class DirreqStatsBuilder {
- private String dirreqStatsEndLine = "dirreq-stats-end 2012-02-11 "
- + "00:59:53 (86400 s)";
- private static ExtraInfoDescriptor createWithDirreqStatsEndLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqStatsEndLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3IpsLine = "dirreq-v3-ips us=1544,de=1056,"
- + "it=1032,fr=784,es=640,ru=440,br=312,gb=272,kr=224,sy=192";
- private static ExtraInfoDescriptor createWithDirreqV3IpsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3IpsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2IpsLine = "dirreq-v2-ips ";
- private static ExtraInfoDescriptor createWithDirreqV2IpsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2IpsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3ReqsLine = "dirreq-v3-reqs us=1744,de=1224,"
- + "it=1080,fr=832,es=664,ru=536,br=344,gb=296,kr=272,in=216";
- private static ExtraInfoDescriptor createWithDirreqV3ReqsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3ReqsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2ReqsLine = "dirreq-v2-reqs ";
- private static ExtraInfoDescriptor createWithDirreqV2ReqsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2ReqsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3RespLine = "dirreq-v3-resp ok=10848,"
- + "not-enough-sigs=8,unavailable=0,not-found=0,not-modified=0,"
- + "busy=80";
- private static ExtraInfoDescriptor createWithDirreqV3RespLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3RespLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2RespLine = "dirreq-v2-resp ok=0,unavailable=0,"
- + "not-found=1576,not-modified=0,busy=0";
- private static ExtraInfoDescriptor createWithDirreqV2RespLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2RespLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2ShareLine = "dirreq-v2-share 0.37%";
- private static ExtraInfoDescriptor createWithDirreqV2ShareLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2ShareLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3ShareLine = "dirreq-v3-share 0.37%";
- private static ExtraInfoDescriptor createWithDirreqV3ShareLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3ShareLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3DirectDlLine = "dirreq-v3-direct-dl "
- + "complete=36,timeout=4,running=0,min=7538,d1=20224,d2=28950,"
- + "q1=40969,d3=55786,d4=145813,md=199164,d6=267230,d7=480900,"
- + "q3=481049,d8=531276,d9=778086,max=15079428";
- private static ExtraInfoDescriptor createWithDirreqV3DirectDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3DirectDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2DirectDlLine = "dirreq-v2-direct-dl "
- + "complete=0,timeout=0,running=0";
- private static ExtraInfoDescriptor createWithDirreqV2DirectDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2DirectDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3TunneledDlLine = "dirreq-v3-tunneled-dl "
- + "complete=10608,timeout=204,running=4,min=507,d1=20399,"
- + "d2=27588,q1=29292,d3=30889,d4=40624,md=59967,d6=103333,"
- + "d7=161170,q3=209415,d8=256711,d9=452503,max=23417777";
- private static ExtraInfoDescriptor createWithDirreqV3TunneledDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3TunneledDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2TunneledDlLine = "dirreq-v2-tunneled-dl "
- + "complete=0,timeout=0,running=0";
- private static ExtraInfoDescriptor createWithDirreqV2TunneledDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2TunneledDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithDirreqStatsLines(
- new DirreqStatsBuilder().buildDirreqStatsLines());
- }
- private String buildDirreqStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.dirreqStatsEndLine != null) {
- sb.append(this.dirreqStatsEndLine).append("\n");
- }
- if (this.dirreqV3IpsLine != null) {
- sb.append(this.dirreqV3IpsLine).append("\n");
- }
- if (this.dirreqV2IpsLine != null) {
- sb.append(this.dirreqV2IpsLine).append("\n");
- }
- if (this.dirreqV3ReqsLine != null) {
- sb.append(this.dirreqV3ReqsLine).append("\n");
- }
- if (this.dirreqV2ReqsLine != null) {
- sb.append(this.dirreqV2ReqsLine).append("\n");
- }
- if (this.dirreqV3RespLine != null) {
- sb.append(this.dirreqV3RespLine).append("\n");
- }
- if (this.dirreqV2RespLine != null) {
- sb.append(this.dirreqV2RespLine).append("\n");
- }
- if (this.dirreqV2ShareLine != null) {
- sb.append(this.dirreqV2ShareLine).append("\n");
- }
- if (this.dirreqV3ShareLine != null) {
- sb.append(this.dirreqV3ShareLine).append("\n");
- }
- if (this.dirreqV3DirectDlLine != null) {
- sb.append(this.dirreqV3DirectDlLine).append("\n");
- }
- if (this.dirreqV2DirectDlLine != null) {
- sb.append(this.dirreqV2DirectDlLine).append("\n");
- }
- if (this.dirreqV3TunneledDlLine != null) {
- sb.append(this.dirreqV3TunneledDlLine).append("\n");
- }
- if (this.dirreqV2TunneledDlLine != null) {
- sb.append(this.dirreqV2TunneledDlLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of entry-stats lines based on default
- * data and modifications requested by test methods. */
- private static class EntryStatsBuilder {
- private String entryStatsEndLine = "entry-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithEntryStatsEndLine(
- String line) throws DescriptorParseException {
- EntryStatsBuilder esb = new EntryStatsBuilder();
- esb.entryStatsEndLine = line;
- return DescriptorBuilder.createWithEntryStatsLines(
- esb.buildEntryStatsLines());
- }
- private String entryIpsLine = "entry-ips ir=25368,us=15744,it=14816,"
- + "de=13256,es=8280,fr=8120,br=5176,sy=4760,ru=4504,sa=4216,"
- + "gb=3152,pl=2928,nl=2208,kr=1856,ca=1792,ua=1272,in=1192";
- private static ExtraInfoDescriptor createWithEntryIpsLine(
- String line) throws DescriptorParseException {
- EntryStatsBuilder esb = new EntryStatsBuilder();
- esb.entryIpsLine = line;
- return DescriptorBuilder.createWithEntryStatsLines(
- esb.buildEntryStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithEntryStatsLines(
- new EntryStatsBuilder().buildEntryStatsLines());
- }
- private String buildEntryStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.entryStatsEndLine != null) {
- sb.append(this.entryStatsEndLine).append("\n");
- }
- if (this.entryIpsLine != null) {
- sb.append(this.entryIpsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of cell-stats lines based on default
- * data and modifications requested by test methods. */
- private static class CellStatsBuilder {
- private String cellStatsEndLine = "cell-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithCellStatsEndLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellStatsEndLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellProcessedCellsLine = "cell-processed-cells "
- + "1441,11,6,4,2,1,1,1,1,1";
- private static ExtraInfoDescriptor createWithCellProcessedCellsLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellProcessedCellsLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellQueuedCellsLine = "cell-queued-cells "
- + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00";
- private static ExtraInfoDescriptor createWithCellQueuedCellsLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellQueuedCellsLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellTimeInQueueLine = "cell-time-in-queue "
- + "524,1,1,0,0,25,0,0,0,0";
- private static ExtraInfoDescriptor createWithCellTimeInQueueLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellTimeInQueueLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellCircuitsPerDecileLine = "cell-circuits-per-decile "
- + "866";
- private static ExtraInfoDescriptor
- createWithCellCircuitsPerDecileLine(String line)
- throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellCircuitsPerDecileLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithCellStatsLines(
- new CellStatsBuilder().buildCellStatsLines());
- }
- private String buildCellStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.cellStatsEndLine != null) {
- sb.append(this.cellStatsEndLine).append("\n");
- }
- if (this.cellProcessedCellsLine != null) {
- sb.append(this.cellProcessedCellsLine).append("\n");
- }
- if (this.cellQueuedCellsLine != null) {
- sb.append(this.cellQueuedCellsLine).append("\n");
- }
- if (this.cellTimeInQueueLine != null) {
- sb.append(this.cellTimeInQueueLine).append("\n");
- }
- if (this.cellCircuitsPerDecileLine != null) {
- sb.append(this.cellCircuitsPerDecileLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of exit-stats lines based on default
- * data and modifications requested by test methods. */
- private static class ExitStatsBuilder {
- private String exitStatsEndLine = "exit-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithExitStatsEndLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitStatsEndLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitKibibytesWrittenLine = "exit-kibibytes-written "
- + "25=74647,80=31370,443=20577,49755=23,52563=12,52596=1111,"
- + "57528=4,60912=11,61351=6,64811=3365,other=2592";
- private static ExtraInfoDescriptor createWithExitKibibytesWrittenLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitKibibytesWrittenLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitKibibytesReadLine = "exit-kibibytes-read "
- + "25=35562,80=1254256,443=110279,49755=9396,52563=1911,"
- + "52596=648,57528=1188,60912=1427,61351=1824,64811=14,"
- + "other=3054";
- private static ExtraInfoDescriptor createWithExitKibibytesReadLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitKibibytesReadLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitStreamsOpenedLine = "exit-streams-opened "
- + "25=369748,80=64212,443=151660,49755=4,52563=4,52596=4,57528=4,"
- + "60912=4,61351=4,64811=4,other=1212";
- private static ExtraInfoDescriptor createWithExitStreamsOpenedLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitStreamsOpenedLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithExitStatsLines(
- new ExitStatsBuilder().buildExitStatsLines());
- }
- private String buildExitStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.exitStatsEndLine != null) {
- sb.append(this.exitStatsEndLine).append("\n");
- }
- if (this.exitKibibytesWrittenLine != null) {
- sb.append(this.exitKibibytesWrittenLine).append("\n");
- }
- if (this.exitKibibytesReadLine != null) {
- sb.append(this.exitKibibytesReadLine).append("\n");
- }
- if (this.exitStreamsOpenedLine != null) {
- sb.append(this.exitStreamsOpenedLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of bridge-stats lines based on default
- * data and modifications requested by test methods. */
- private static class BridgeStatsBuilder {
- private String bridgeStatsEndLine = "bridge-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithBridgeStatsEndLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeStatsEndLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpsLine = "bridge-ips ir=24,sy=16,??=8,cn=8,"
- + "de=8,es=8,fr=8,gb=8,in=8,jp=8,kz=8,nl=8,ua=8,us=8,vn=8,za=8";
- private static ExtraInfoDescriptor createWithBridgeIpsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpVersionsLine = "bridge-ip-versions v4=8,v6=16";
- private static ExtraInfoDescriptor createWithBridgeIpVersionsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpVersionsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpTransportsLine = "bridge-ip-transports "
- + "<OR>=8,obfs2=792,obfs3=1728";
- private static ExtraInfoDescriptor createWithBridgeIpTransportsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpTransportsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithBridgeStatsLines(
- new BridgeStatsBuilder().buildBridgeStatsLines());
- }
- private String buildBridgeStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.bridgeStatsEndLine != null) {
- sb.append(this.bridgeStatsEndLine).append("\n");
- }
- if (this.bridgeIpsLine != null) {
- sb.append(this.bridgeIpsLine).append("\n");
- }
- if (this.bridgeIpVersionsLine != null) {
- sb.append(this.bridgeIpVersionsLine).append("\n");
- }
- if (this.bridgeIpTransportsLine != null) {
- sb.append(this.bridgeIpTransportsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of hidserv-stats lines based on default
- * data and modifications requested by test methods. */
- private static class HidservStatsBuilder {
- private String hidservStatsEndLine = "hidserv-stats-end 2015-12-03 "
- + "14:26:56 (86400 s)";
- private static ExtraInfoDescriptor createWithHidservStatsEndLine(
- String line) throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservStatsEndLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private String hidservRendRelayedCellsLine =
- "hidserv-rend-relayed-cells 36474281 delta_f=2048 epsilon=0.30 "
- + "bin_size=1024";
- private static ExtraInfoDescriptor
- createWithHidservRendRelayedCellsLine(String line)
- throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservRendRelayedCellsLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private String hidservDirOnionsSeenLine = "hidserv-dir-onions-seen "
- + "-3 delta_f=8 epsilon=0.30 bin_size=8";
- private static ExtraInfoDescriptor createWithHidservDirOnionsSeenLine(
- String line) throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservDirOnionsSeenLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithHidservStatsLines(
- new HidservStatsBuilder().buildHidservStatsLines());
- }
- private String buildHidservStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.hidservStatsEndLine != null) {
- sb.append(this.hidservStatsEndLine).append("\n");
- }
- if (this.hidservRendRelayedCellsLine != null) {
- sb.append(this.hidservRendRelayedCellsLine).append("\n");
- }
- if (this.hidservDirOnionsSeenLine != null) {
- sb.append(this.hidservDirOnionsSeenLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- @Test()
- public void testSampleDescriptor() throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- ExtraInfoDescriptor descriptor =
- new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- assertNotNull(descriptor.getWriteHistory());
- assertEquals(1328951019000L, descriptor.getWriteHistory().
- getHistoryEndMillis());
- assertEquals(900L, descriptor.getWriteHistory().getIntervalLength());
- assertEquals(4572675072L, (long) descriptor.getWriteHistory().
- getBandwidthValues().get(1328951019000L));
- assertNotNull(descriptor.getReadHistory());
- assertNotNull(descriptor.getDirreqWriteHistory());
- assertNotNull(descriptor.getDirreqReadHistory());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoLineMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine(null);
- }
-
- @Test()
- public void testExtraInfoOpt() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- }
-
- @Test()
- public void testExtraInfoNicknameTwoSpaces()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoLineNotFirst()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8\n"
- + "extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameInvalidChar() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub% A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5ReallyLongNickname "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintG() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 G9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C6");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C63B26"
- + "A9C0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine(null);
- }
-
- @Test()
- public void testPublishedOpt() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-02-11 09:08:36");
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- }
-
- @Test()
- public void testPublishedMillis() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-02-11 09:08:36.123");
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNegativeBytes()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-02-11 09:03:39 (900 s) "
- + "-4713350144,-4723824640,-4710717440,-4572675072");
- }
-
- @Test()
- public void testReadHistoryTabInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (900\ts) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test()
- public void testReadHistoryTabIntervalBytes()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (900 s)\t"
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testReadHistoryNegativeInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (-900 s) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test()
- public void testReadHistoryNonStandardInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (1800 s) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqWriteHistoryMissingBytesBegin()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqWriteHistoryLine(
- "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
- + ",64996352,60625920,67922944");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqWriteHistoryMissingBytesMiddle()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqWriteHistoryLine(
- "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
- + "81281024,,60625920,67922944");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqReadHistoryMissingBytesEnd()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqReadHistoryLine(
- "dirreq-read-history 2012-02-11 09:03:39 (900 s) "
- + "17074176,16235520,16005120,");
- }
-
- @Test()
- public void testGeoipDbDigestValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithGeoipDbDigestLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
- descriptor.getGeoipDbDigest());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestTooShort()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301C");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestIllegalChars()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
- + "&%6A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest");
- }
-
- @Test()
- public void testGeoip6DbDigestValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithGeoip6DbDigestLine("geoip6-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
- descriptor.getGeoip6DbDigest());
- }
-
- @Test()
- public void testGeoipStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = GeoipStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328898771000L, descriptor.getGeoipStartTimeMillis());
- SortedMap<String, Integer> ips = descriptor.getGeoipClientOrigins();
- assertNotNull(ips);
- assertEquals(1152, ips.get("de").intValue());
- assertEquals(896, ips.get("cn").intValue());
- assertFalse(ips.containsKey("pl"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipStartTimeDateOnly()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipStartTimeLine("geoip-start-time "
- + "2012-02-10");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsDash()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de-1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsZero()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=zero,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsNone()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=none,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsOther()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,other=200");
- }
-
- @Test()
- public void testGeoipClientOriginsQuestionMarks()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,??=200");
- }
-
- @Test()
- public void testGeoipClientOriginsCapital()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins DE=1152,CN=896,US=712,IT=504,RU=352,FR=208,"
- + "GB=208,IR=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingBegin()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins ,cn=896,us=712,it=504,ru=352,fr=208,gb=208,"
- + "ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingMiddle()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingEnd()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,");
- }
-
- @Test()
- public void testGeoipClientOriginsDuplicate()
- throws DescriptorParseException {
- /* dir-spec.txt doesn't say anything about duplicate country codes, so
- * this line is valid, even though it leads to a somewhat undefined
- * parse result. */
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,de=952,cn=896,us=712,it=504,"
- + "ru=352,fr=208,gb=208,ir=200");
- }
-
- @Test()
- public void testDirreqStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DirreqStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328921993000L, descriptor.getDirreqStatsEndMillis());
- assertEquals(86400L, descriptor.getDirreqStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getDirreqV3Ips();
- assertNotNull(ips);
- assertEquals(1544, ips.get("us").intValue());
- assertFalse(ips.containsKey("no"));
- assertTrue(descriptor.getDirreqV2Ips().isEmpty());
- SortedMap<String, Integer> reqs = descriptor.getDirreqV3Reqs();
- assertEquals(832, reqs.get("fr").intValue());
- assertTrue(descriptor.getDirreqV2Reqs().isEmpty());
- SortedMap<String, Integer> resp = descriptor.getDirreqV3Resp();
- assertEquals(10848, resp.get("ok").intValue());
- assertEquals(8, resp.get("not-enough-sigs").intValue());
- resp = descriptor.getDirreqV2Resp();
- assertEquals(1576, resp.get("not-found").intValue());
- assertEquals(0.37, descriptor.getDirreqV2Share(), 0.0001);
- assertEquals(0.37, descriptor.getDirreqV3Share(), 0.0001);
- SortedMap<String, Integer> dl = descriptor.getDirreqV3DirectDl();
- assertEquals(36, dl.get("complete").intValue());
- dl = descriptor.getDirreqV2DirectDl();
- assertEquals(0, dl.get("timeout").intValue());
- dl = descriptor.getDirreqV3TunneledDl();
- assertEquals(10608, dl.get("complete").intValue());
- dl = descriptor.getDirreqV2TunneledDl();
- assertEquals(0, dl.get("complete").intValue());
- }
-
- @Test()
- public void testDirreqStatsIntervalTwoDays()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqStatsEndLine("dirreq-stats-end "
- + "2012-02-11 00:59:53 (172800 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3IpsThreeLetterCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3IpsLine("dirreq-v3-ips "
- + "usa=1544");
- }
-
- @Test()
- public void testDirreqV2IpsDigitCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2IpsLine("dirreq-v2-ips 00=8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3ReqsOneLetterCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3ReqsLine("dirreq-v3-reqs "
- + "u=1744");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2ReqsNoNumber()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2ReqsLine("dirreq-v2-reqs us=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3RespTwoEqualSigns()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3RespLine("dirreq-v3-resp "
- + "ok==10848");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2RespNull()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2RespLine("dirreq-v2-resp "
- + "ok=null");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2ShareComma()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2ShareLine("dirreq-v2-share "
- + "0,37%");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3ShareNoPercent()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3ShareLine("dirreq-v3-share "
- + "0.37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3DirectDlSpace()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3DirectDlLine(
- "dirreq-v3-direct-dl complete 36");
- }
-
- @Test()
- public void testDirreqV2DirectDlNegative()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2DirectDlLine(
- "dirreq-v2-direct-dl complete=-8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3TunneledDlTooLarge()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3TunneledDlLine(
- "dirreq-v3-tunneled-dl complete=2147483648");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3TunneledDlDouble()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2TunneledDlLine(
- "dirreq-v2-tunneled-dl complete=0.001");
- }
-
- @Test()
- public void testEntryStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = EntryStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getEntryStatsEndMillis());
- assertEquals(86400L, descriptor.getEntryStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getEntryIps();
- assertNotNull(ips);
- assertEquals(25368, ips.get("ir").intValue());
- assertFalse(ips.containsKey("no"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEntryStatsEndNoDate() throws DescriptorParseException {
- EntryStatsBuilder.createWithEntryStatsEndLine("entry-stats-end "
- + "01:59:39 (86400 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEntryStatsIpsSemicolon()
- throws DescriptorParseException {
- EntryStatsBuilder.createWithEntryIpsLine("entry-ips "
- + "ir=25368;us=15744");
- }
-
- @Test()
- public void testCellStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = CellStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getCellStatsEndMillis());
- assertEquals(86400L, descriptor.getCellStatsIntervalLength());
- List<Integer> processedCells = descriptor.getCellProcessedCells();
- assertEquals(10, processedCells.size());
- assertEquals(1441, processedCells.get(0).intValue());
- assertEquals(11, processedCells.get(1).intValue());
- List<Double> queuedCells = descriptor.getCellQueuedCells();
- assertEquals(10, queuedCells.size());
- assertEquals(3.29, queuedCells.get(0), 0.001);
- assertEquals(0.00, queuedCells.get(1), 0.001);
- List<Integer> timeInQueue = descriptor.getCellTimeInQueue();
- assertEquals(10, timeInQueue.size());
- assertEquals(524, timeInQueue.get(0).intValue());
- assertEquals(1, timeInQueue.get(1).intValue());
- assertEquals(866, descriptor.getCellCircuitsPerDecile());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellStatsEndNoSeconds()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellStatsEndLine("cell-stats-end "
- + "2012-02-11 01:59:39 (86400)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellProcessedCellsNineComma()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellProcessedCellsLine(
- "cell-processed-cells 1441,11,6,4,2,1,1,1,1,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellProcessedCellsEleven()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellQueuedCellsLine("cell-queued-cells "
- + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellTimeInQueueDouble()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellTimeInQueueLine("cell-time-in-queue "
- + "524.0,1.0,1.0,0.0,0.0,25.0,0.0,0.0,0.0,0.0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellCircuitsPerDecileNegative()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellCircuitsPerDecileLine(
- "cell-circuits-per-decile -866");
- }
-
- @Test()
- public void testConnBiDirectValid()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithConnBiDirectLine("conn-bi-direct 2012-02-11 01:59:39 "
- + "(86400 s) 42173,1591,1310,1744");
- assertEquals(1328925579000L,
- descriptor.getConnBiDirectStatsEndMillis());
- assertEquals(86400L, descriptor.getConnBiDirectStatsIntervalLength());
- assertEquals(42173, descriptor.getConnBiDirectBelow());
- assertEquals(1591, descriptor.getConnBiDirectRead());
- assertEquals(1310, descriptor.getConnBiDirectWrite());
- assertEquals(1744, descriptor.getConnBiDirectBoth());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConnBiDirectStatsFive()
- throws DescriptorParseException {
- DescriptorBuilder.createWithConnBiDirectLine("conn-bi-direct "
- + "2012-02-11 01:59:39 (86400 s) 42173,1591,1310,1744,42");
- }
-
- @Test()
- public void testExitStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = ExitStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getExitStatsEndMillis());
- assertEquals(86400L, descriptor.getExitStatsIntervalLength());
- String[] ports = new String[] { "25", "80", "443", "49755",
- "52563", "52596", "57528", "60912", "61351", "64811", "other" };
- int[] writtenValues = new int[] { 74647, 31370, 20577, 23, 12, 1111,
- 4, 11, 6, 3365, 2592 };
- int i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitKibibytesWritten().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(writtenValues[i++], e.getValue().intValue());
- }
- int[] readValues = new int[] { 35562, 1254256, 110279, 9396, 1911,
- 648, 1188, 1427, 1824, 14, 3054 };
- i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitKibibytesRead().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(readValues[i++], e.getValue().intValue());
- }
- int[] streamsValues = new int[] { 369748, 64212, 151660, 4, 4, 4, 4,
- 4, 4, 4, 1212 };
- i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitStreamsOpened().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(streamsValues[i++], e.getValue().intValue());
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsEndNoSeconds()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitStatsEndLine("exit-stats-end "
- + "2012-02-11 01:59 (86400 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsWrittenNegativePort()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesWrittenLine(
- "exit-kibibytes-written -25=74647");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsWrittenUnknown()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesWrittenLine(
- "exit-kibibytes-written unknown=74647");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsReadNegativeBytes()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesReadLine(
- "exit-kibibytes-read 25=-35562");
- }
-
- @Test()
- public void testExitStatsReadTooLarge()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesReadLine(
- "exit-kibibytes-read other=2282907805");
- }
-
- @Test()
- public void testExitStatsStreamsTooLarge()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitStreamsOpenedLine(
- "exit-streams-opened 25=2147483648");
- }
-
- @Test()
- public void testBridgeStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = BridgeStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getBridgeStatsEndMillis());
- assertEquals(86400L, descriptor.getBridgeStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getBridgeIps();
- assertNotNull(ips);
- assertEquals(24, ips.get("ir").intValue());
- assertEquals(16, ips.get("sy").intValue());
- assertFalse(ips.containsKey("no"));
- SortedMap<String, Integer> ver = descriptor.getBridgeIpVersions();
- assertNotNull(ver);
- assertEquals(8, ver.get("v4").intValue());
- assertEquals(16, ver.get("v6").intValue());
- assertFalse(ver.containsKey("v8"));
- SortedMap<String, Integer> trans = descriptor.getBridgeIpTransports();
- assertNotNull(trans);
- assertEquals(8, trans.get("<OR>").intValue());
- assertEquals(792, trans.get("obfs2").intValue());
- assertEquals(1728, trans.get("obfs3").intValue());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeStatsEndIntervalZero()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeStatsEndLine("bridge-stats-end "
- + "2012-02-11 01:59:39 (0 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpsLine("bridge-ips ir=24.5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpsNonAsciiKeyword()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
- 0x14, (byte) 0xfe, 0x18, // non-ascii chars
- 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2d, // "bridge-"
- 0x69, 0x70, 0x73 }, false); // "ips" (no newline)
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpVersionsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpVersionsLine(
- "bridge-ip-versions v4=24.5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpTransportsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpTransportsLine(
- "bridge-ip-transports obfs2=24.5");
- }
-
- @Test()
- public void testBridgeIpTransportsUnderscore()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpTransportsLine(
- "bridge-ip-transports meek=32,obfs3_websocket=8,websocket=64");
- }
-
- @Test()
- public void testHidservStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = HidservStatsBuilder.
- createWithDefaultLines();
- assertEquals(1449152816000L, descriptor.getHidservStatsEndMillis());
- assertEquals(86400L, descriptor.getHidservStatsIntervalLength());
- assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
- 0.0001);
- Map<String, Double> params =
- descriptor.getHidservRendRelayedCellsParameters();
- assertTrue(params.containsKey("delta_f"));
- assertEquals(2048.0, params.remove("delta_f"), 0.0001);
- assertTrue(params.containsKey("epsilon"));
- assertEquals(0.3, params.remove("epsilon"), 0.0001);
- assertTrue(params.containsKey("bin_size"));
- assertEquals(1024.0, params.remove("bin_size"), 0.0001);
- assertTrue(params.isEmpty());
- assertEquals(-3.0, descriptor.getHidservDirOnionsSeen(), 0.0001);
- params = descriptor.getHidservDirOnionsSeenParameters();
- assertTrue(params.containsKey("delta_f"));
- assertEquals(8.0, params.remove("delta_f"), 0.0001);
- assertTrue(params.containsKey("epsilon"));
- assertEquals(0.3, params.remove("epsilon"), 0.0001);
- assertTrue(params.containsKey("bin_size"));
- assertEquals(8.0, params.remove("bin_size"), 0.0001);
- assertTrue(params.isEmpty());
- }
-
- @Test()
- public void testHidservStatsEndLineMissing()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- HidservStatsBuilder.createWithHidservStatsEndLine(null);
- assertEquals(-1L, descriptor.getHidservStatsEndMillis());
- assertEquals(-1L, descriptor.getHidservStatsIntervalLength());
- }
-
- @Test()
- public void testHidservRendRelayedCellsNoParams()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- HidservStatsBuilder.createWithHidservRendRelayedCellsLine(
- "hidserv-rend-relayed-cells 36474281");
- assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
- 0.0001);
- assertTrue(
- descriptor.getHidservRendRelayedCellsParameters().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHidservDirOnionsSeenCommaSeparatedParams()
- throws DescriptorParseException {
- HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
- "hidserv-dir-onions-seen -3 delta_f=8,epsilon=0.30,bin_size=8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHidservDirOnionsSeenNoDoubleParams()
- throws DescriptorParseException {
- HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
- "hidserv-dir-onions-seen -3 delta_f=A epsilon=B bin_size=C");
- }
-
- @Test()
- public void testRouterSignatureOpt()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("opt "
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "crypto lines are ignored anyway\n"
- + "-----END SIGNATURE-----");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterSignatureNotLastLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----\npublished 2012-02-11 09:08:36");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- private static final String IDENTITY_ED25519_LINES =
- "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
- + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
- + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
- + "\n"
- + "-----END ED25519 CERT-----";
-
- private static final String MASTER_KEY_ED25519_LINE =
- "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
-
- private static final String ROUTER_SIG_ED25519_LINE =
- "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
- + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
-
- @Test()
- public void testEd25519() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- assertEquals(IDENTITY_ED25519_LINES.substring(
- IDENTITY_ED25519_LINES.indexOf("\n") + 1),
- descriptor.getIdentityEd25519());
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- assertEquals(ROUTER_SIG_ED25519_LINE.substring(
- ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getRouterSignatureEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityMasterKeyMismatch()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519IdentityMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(null,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
- + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityEmptyCrypto()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519MasterKeyMissing()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- null, ROUTER_SIG_ED25519_LINE);
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519MasterKeyDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519RouterSigMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519RouterSigDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
- + ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testExtraInfoDigestSha256Relay()
- throws DescriptorParseException {
- byte[] descriptorBytes = ("extra-info Unnamed "
- + "EA5B335055D2F03013FF030381F02B1C631EC723\n"
- + "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiZRAenzZorGtx6xapoEeaqcLLOk3uWwJXTvOVLluSXXbRSZAQAgBADLN5"
- + "wp\nCEOrRbshSbj1NDAUgc6cxU65M/Vx1x+b5+EXbkQZ5uiyB4pphVF5kPPT1P"
- + "SleYqM\n8j+tlKh2i6+Xr0xScSPpmtG00/D0MoRlT7ZdaaaT5iw1DWDQCZ8BHG"
- + "lAZwU=\n"
- + "-----END ED25519 CERT-----\n"
- + "published 2015-12-01 04:38:12\n"
- + "write-history 2015-12-01 01:40:37 (14400 s) 88704000,60825600,"
- + "61747200,76953600,61516800,59443200\n"
- + "read-history 2015-12-01 01:40:37 (14400 s) 87321600,59443200,"
- + "59904000,74880000,60364800,58060800\n"
- + "router-sig-ed25519 c6eUeJs/SVjun3JhmjByEeWdRDyunSMAnGVhx71JiRj"
- + "YzR8x5IcPebylG7m10wiolFxinvw78UhrrGo9Sq5ZBw\n"
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "oC2qFHCDOKSRoIPR86jdRxEYia390Z4d8fT0yr/1mg4RQ7lHmxlzFT6QxCswdX"
- + "Ry\nvGNGR0wARySgyE+YKKWYn/Hp547JhhWd9Oc7BuFMY0XMvl/HOo+B9VjW+l"
- + "nv6UBE\niqxx3C3Iw0ymohvOenyCUa/7TmsT7eVotDO57uIoGEc=\n"
- + "-----END SIGNATURE-----\n"
- + "").getBytes();
- RelayExtraInfoDescriptor descriptor =
- new RelayExtraInfoDescriptorImpl(descriptorBytes, true);
- assertEquals("Pt1BtzfRwhYqGCDo8jjchS8nJP3ovrDyHGn+dqPbMgw",
- descriptor.getExtraInfoDigestSha256());
- }
-
- @Test()
- public void testExtraInfoDigestSha256Bridge()
- throws DescriptorParseException {
- byte[] descriptorBytes = ("extra-info idideditheconfig "
- + "DC28749EC9E26E61DE492E46CD830379E9931B09\n"
- + "master-key-ed25519 "
- + "38FzmOIE6Mm85Ytx0MhFM6X9EuxWRUgb6HjyMGuO2AU\n"
- + "published 2015-12-03 13:23:19\n"
- + "write-history 2015-12-03 09:59:32 (14400 s) 53913600,52992000,"
- + "53222400,53222400,53452800,53222400\n"
- + "read-history 2015-12-03 09:59:32 (14400 s) 61056000,60364800,"
- + "60364800,60134400,60595200,60364800\n"
- + "geoip-db-digest 5BF366AD4A0572D82A1A0F6628AF8EF7725E3AB9\n"
- + "geoip6-db-digest 212DE17D5A368DCAFA19B95F168BFFA101145A93\n"
- + "router-digest-sha256 "
- + "TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4\n"
- + "router-digest 00B98F076B586272C3172B7F3DA29ADEE75F2ED8\n").getBytes();
- BridgeExtraInfoDescriptor descriptor =
- new BridgeExtraInfoDescriptorImpl(descriptorBytes, true);
- assertEquals("TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4",
- descriptor.getExtraInfoDigestSha256());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java b/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
deleted file mode 100644
index abb51db..0000000
--- a/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.torproject.descriptor.impl;
-
-import org.junit.Test;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.Microdescriptor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-public class MicrodescriptorImplTest {
-
- /* Helper class to build a microdescriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String onionKeyLines = "onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALNZ4pNsHHkl7a+kFWbBmPHNAepjjvuhjTr1TaMB3UKuCRaXJmS2Qr"
- + "CW\nkTmINqdQUccwb3ghb7EBZfDtCUvjcwMSEsRRTVIZqVQsYj6m3n1CegOc4o"
- + "UutXaZ\nfkyty5XOgV4Qucx9wokzTMCHlO0V0x9y0FwFsK5Nb6ugqfQLLQ6XAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static Microdescriptor createWithDefaultLines()
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- return new MicrodescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyLine =
- "ntor-onion-key PXLa7IGE+TzPDMsM5j9rFnDa37rd6kfZa5QuzqqJukw=";
- private String idLine = "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8";
- private static Microdescriptor createWithIdLine(String line)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.idLine = line;
- return new MicrodescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.onionKeyLines != null) {
- sb.append(this.onionKeyLines).append("\n");
- }
- if (this.ntorOnionKeyLine != null) {
- sb.append(this.ntorOnionKeyLine).append("\n");
- }
- if (this.idLine != null) {
- sb.append(this.idLine).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- @Test()
- public void testDefaults() throws DescriptorParseException {
- DescriptorBuilder.createWithDefaultLines();
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024TooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id rsa1024 AAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024TooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id ed25519 AAAAAAAAAAAAAAAAAAAAAA"
- + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa512() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id rsa512 "
- + "bvegfGxp8k7T9QFpjPTrPaJTa/8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdEd25519Duplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine(
- "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8\n"
- + "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8");
- }
-}
diff --git a/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
deleted file mode 100644
index d864337..0000000
--- a/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
+++ /dev/null
@@ -1,1272 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-
-import org.junit.Test;
-import org.torproject.descriptor.NetworkStatusEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* TODO Add test cases for all lines starting with "opt ". */
-
-/* Test parsing of network status consensuses. The main focus is on
- * making sure that the parser is as robust as possible and doesn't break,
- * no matter what gets fed into it. A secondary focus is to ensure that
- * a parsed consensus is fully compatible to dir-spec.txt. */
-public class RelayNetworkStatusConsensusImplTest {
-
- /* Helper class to build a directory source based on default data and
- * modifications requested by test methods. */
- private static class DirSourceBuilder {
- private static RelayNetworkStatusConsensus
- createWithDirSource(String dirSourceString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.dirSources.add(dirSourceString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String nickname = "gabelmoo";
- private static RelayNetworkStatusConsensus
- createWithNickname(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.nickname = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
- private static RelayNetworkStatusConsensus
- createWithIdentity(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.identity = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String hostName = "212.112.245.170";
- private static RelayNetworkStatusConsensus
- createWithHostName(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.hostName = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String address = "212.112.245.170";
- private static RelayNetworkStatusConsensus
- createWithAddress(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.address = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String dirPort = "80";
- private static RelayNetworkStatusConsensus
- createWithDirPort(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.dirPort = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String orPort = "443";
- private static RelayNetworkStatusConsensus
- createWithOrPort(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.orPort = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String contactLine = "contact 4096R/C5AA446D Sebastian Hahn "
- + "<tor@xxxxxxxxxxxxxxxxx>";
- private static RelayNetworkStatusConsensus
- createWithContactLine(String line)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.contactLine = line;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String voteDigestLine =
- "vote-digest 0F398A5834D2C139E1D92310B09F814F243354D1";
- private static RelayNetworkStatusConsensus
- createWithVoteDigestLine(String line)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.voteDigestLine = line;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String buildDirSource() {
- StringBuilder sb = new StringBuilder();
- String dirSourceLine = "dir-source " + this.nickname + " "
- + this.identity + " " + this.hostName + " " + this.address + " "
- + this.dirPort + " " + this.orPort;
- sb.append(dirSourceLine).append("\n");
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.voteDigestLine != null) {
- sb.append(this.voteDigestLine).append("\n");
- }
- String dirSourceWithTrailingNewLine = sb.toString();
- String dirSource = dirSourceWithTrailingNewLine.substring(0,
- dirSourceWithTrailingNewLine.length() - 1);
- return dirSource;
- }
- }
-
- /* Helper class to build a status entry based on default data and
- * modifications requested by test methods. */
- private static class StatusEntryBuilder {
- private static RelayNetworkStatusConsensus
- createWithStatusEntry(String statusEntryString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(statusEntryString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String nickname = "right2privassy3";
- private static RelayNetworkStatusConsensus
- createWithNickname(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.nickname = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String fingerprintBase64 = "ADQ6gCT3DiFHKPDFr3rODBUI8HM";
- private static RelayNetworkStatusConsensus
- createWithFingerprintBase64(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.fingerprintBase64 = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String descriptorBase64 = "Yiti+nayuT2Efe2X1+M4nslwVuU";
- private static RelayNetworkStatusConsensus
- createWithDescriptorBase64(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.descriptorBase64 = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String publishedString = "2011-11-29 21:34:27";
- private static RelayNetworkStatusConsensus
- createWithPublishedString(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.publishedString = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String address = "50.63.8.215";
- private static RelayNetworkStatusConsensus
- createWithAddress(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.address = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String orPort = "9023";
- private static RelayNetworkStatusConsensus
- createWithOrPort(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.orPort = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String dirPort = "0";
- private static RelayNetworkStatusConsensus
- createWithDirPort(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.dirPort = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String sLine = "s Exit Fast Named Running Stable Valid";
- private static RelayNetworkStatusConsensus
- createWithSLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.sLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String vLine = "v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)";
- private static RelayNetworkStatusConsensus
- createWithVLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.vLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String wLine = "w Bandwidth=1";
- private static RelayNetworkStatusConsensus
- createWithWLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.wLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String pLine = "p accept 80,1194,1220,1293";
- private static RelayNetworkStatusConsensus
- createWithPLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.pLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String buildStatusEntry() {
- StringBuilder sb = new StringBuilder();
- String rLine = "r " + nickname + " " + fingerprintBase64 + " "
- + descriptorBase64 + " " + publishedString + " " + address + " "
- + orPort + " " + dirPort;
- sb.append(rLine).append("\n");
- if (this.sLine != null) {
- sb.append(this.sLine).append("\n");
- }
- if (this.vLine != null) {
- sb.append(this.vLine).append("\n");
- }
- if (this.wLine != null) {
- sb.append(this.wLine).append("\n");
- }
- if (this.pLine != null) {
- sb.append(this.pLine).append("\n");
- }
- String statusEntryWithTrailingNewLine = sb.toString();
- String statusEntry = statusEntryWithTrailingNewLine.substring(0,
- statusEntryWithTrailingNewLine.length() - 1);
- return statusEntry;
- }
- }
-
- /* Helper class to build a directory signature based on default data and
- * modifications requested by test methods. */
- private static class DirectorySignatureBuilder {
- private static RelayNetworkStatusConsensus
- createWithDirectorySignature(String directorySignatureString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.addDirectorySignature(directorySignatureString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
- private static RelayNetworkStatusConsensus
- createWithIdentity(String string)
- throws DescriptorParseException {
- DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
- dsb.identity = string;
- return createWithDirectorySignature(dsb.buildDirectorySignature());
- }
- private String signingKey =
- "845CF1D0B370CA443A8579D18E7987E7E532F639";
- private static RelayNetworkStatusConsensus
- createWithSigningKey(String string)
- throws DescriptorParseException {
- DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
- dsb.signingKey = string;
- return createWithDirectorySignature(dsb.buildDirectorySignature());
- }
- private String buildDirectorySignature() {
- String directorySignature = "directory-signature " + identity + " "
- + signingKey + "\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "gE64+/4BH43v1+7jS9FK1tu2+94at8xhVSPn4O/PpOx7b0Yb+S1hac1QHAiS"
- + "Ll+k\n"
- + "6OiANKzhj54WHSrUswBPrOzjmKj0OhGXSAe5nHZUFX9a1MDQLDCoZBj536X9"
- + "P3JG\n"
- + "z89A+wrsN17I5490y66AEvws54BYZMbgRfp8HXn/0Ss=\n"
- + "-----END SIGNATURE-----";
- return directorySignature;
- }
- }
-
- @Test()
- public void testSampleConsensus() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertEquals(3, consensus.getNetworkStatusVersion());
- assertEquals(11, consensus.getConsensusMethod());
- assertEquals(1322643600000L, consensus.getValidAfterMillis());
- assertEquals(1322647200000L, consensus.getFreshUntilMillis());
- assertEquals(1322654400000L, consensus.getValidUntilMillis());
- assertEquals(300L, consensus.getVoteSeconds());
- assertEquals(300L, consensus.getDistSeconds());
- assertTrue(consensus.getRecommendedClientVersions().contains(
- "0.2.3.8-alpha"));
- assertTrue(consensus.getRecommendedServerVersions().contains(
- "0.2.3.8-alpha"));
- assertTrue(consensus.getKnownFlags().contains("Running"));
- assertEquals(30000, (int) consensus.getConsensusParams().get(
- "CircuitPriorityHalflifeMsec"));
- assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
- "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getHostname());
- assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
- "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getIp());
- assertTrue(consensus.containsStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894"));
- assertEquals("188.177.149.216", consensus.getStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894").getAddress());
- for (DirectorySignature signature : consensus.getSignatures()) {
- if ("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4".equals(
- signature.getIdentity())) {
- assertEquals("3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86",
- signature.getSigningKeyDigest());
- }
- }
- assertEquals(285, (int) consensus.getBandwidthWeights().get("Wbd"));
- assertTrue(consensus.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLineSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n ");
- }
-
- @Test()
- public void testNetworkStatusVersionPrefixLineAtChar()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "@consensus\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "directory-footer\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLinePoundChar()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "#consensus\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersion42()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 42");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionFourtyTwo()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version FourtyTwo");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionSpaceBefore()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- " network-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusSpaceBefore() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(" vote-status consensus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status ");
- }
-
- @Test()
- public void testVoteStatusConsensusOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status consensus ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusVote() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status vote");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusTheMagicVoteStatus()
- throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(
- "vote-status TheMagicVoteStatus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodEleven()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(
- "consensus-method eleven");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodMinusOne()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNinePeriod()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method "
- + "999999999999999999999999999999999999999999999999999999999999");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodTwoLines()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(
- "consensus-method 1\nconsensus-method 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterLongAgo() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after long ago");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterFeb30() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine(
- "valid-after 2011-02-30 09:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithFreshUntilLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilAroundTen() throws DescriptorParseException {
- ConsensusBuilder.createWithFreshUntilLine(
- "fresh-until 2011-11-30 around ten");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidUntilTomorrowMorning()
- throws DescriptorParseException {
- ConsensusBuilder.createWithValidUntilLine(
- "valid-until tomorrow morning");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayTriple() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine(
- "voting-delay 300 300 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelaySingle() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneTwo() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay one two");
- }
-
- @Test()
- public void testClientServerVersionsNoLine()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.clientVersionsLine = null;
- cb.serverVersionsLine = null;
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertNull(consensus.getRecommendedClientVersions());
- assertNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testServerVersionsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithServerVersionsLine(null);
- assertNotNull(consensus.getRecommendedClientVersions());
- assertNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testClientVersionsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine(null);
- assertNull(consensus.getRecommendedClientVersions());
- assertNotNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testClientVersionsNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine("client-versions");
- assertNotNull(consensus.getRecommendedClientVersions());
- assertTrue(consensus.getRecommendedClientVersions().isEmpty());
- }
-
- @Test()
- public void testClientVersionsOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine("client-versions ");
- assertNotNull(consensus.getRecommendedClientVersions());
- assertTrue(consensus.getRecommendedClientVersions().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsComma() throws DescriptorParseException {
- ConsensusBuilder.createWithClientVersionsLine("client-versions ,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsCommaVersion()
- throws DescriptorParseException {
- ConsensusBuilder.createWithClientVersionsLine(
- "client-versions ,0.2.2.34");
- }
-
- @Test()
- public void testPackageNone() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(null);
- assertNull(consensus.getPackageLines());
- }
-
- @Test()
- public void testPackageOne() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http digest=digest";
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(packageLine);
- assertEquals(packageLine.substring("package ".length()),
- consensus.getPackageLines().get(0));
- }
-
- @Test()
- public void testPackageTwo() throws DescriptorParseException {
- List<String> packageLines = Arrays.asList(
- "package shouldbesecond 0 http digest=digest",
- "package outoforder 0 http digest=digest");
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(packageLines.get(0)
- + "\n" + packageLines.get(1));
- for (int i = 0; i < packageLines.size(); i++) {
- assertEquals(packageLines.get(i).substring("package ".length()),
- consensus.getPackageLines().get(i));
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPackageIncomplete() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http";
- ConsensusBuilder.createWithPackageLines(packageLine);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine("known-flags");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine("known-flags ");
- }
-
- @Test()
- public void testParamsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine(null);
- assertNull(consensus.getConsensusParams());
- }
-
- @Test()
- public void testParamsNoSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test()
- public void testParamsOneSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params ");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test()
- public void testParamsThreeSpaces() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params ");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsNoEqualSign() throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params key-value");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsOneTooLargeNegative()
- throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params min=-2147483649");
- }
-
- @Test()
- public void testParamsLargestNegative()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params min=-2147483648");
- assertEquals(1, consensus.getConsensusParams().size());
- assertEquals(-2147483648,
- (int) consensus.getConsensusParams().get("min"));
- }
-
- @Test()
- public void testParamsLargestPositive()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params max=2147483647");
- assertEquals(1, consensus.getConsensusParams().size());
- assertEquals(2147483647,
- (int) consensus.getConsensusParams().get("max"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsOneTooLargePositive()
- throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params max=2147483648");
- }
-
- @Test()
- public void testDirSourceLegacyNickname()
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.nickname = "gabelmoo-legacy";
- dsb.identity = "81349FC1F2DBA2C2C11B45CB9706637D480AB913";
- dsb.contactLine = null;
- dsb.voteDigestLine = null;
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithDirSource(dsb.buildDirSource());
- assertEquals(3, consensus.getDirSourceEntries().size());
- assertTrue(consensus.getDirSourceEntries().get(
- "81349FC1F2DBA2C2C11B45CB9706637D480AB913").isLegacy());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceNicknameTooLong()
- throws DescriptorParseException {
- DirSourceBuilder.createWithNickname("gabelmooisfinebutthisistoolong");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceIdentityTooShort()
- throws DescriptorParseException {
- DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceIdentityTooLong()
- throws DescriptorParseException {
- DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111"
- + "4BB25CEF515B226ED03BB616EB2F60BEC8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceHostnameMissing()
- throws DescriptorParseException {
- DirSourceBuilder.createWithHostName("");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceAddress24() throws DescriptorParseException {
- DirSourceBuilder.createWithAddress("212.112.245");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceAddress40() throws DescriptorParseException {
- DirSourceBuilder.createWithAddress("212.112.245.170.123");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPortMinusOne()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("-1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPort66666()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPortOnions()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("onions");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceOrPortOnions()
- throws DescriptorParseException {
- DirSourceBuilder.createWithOrPort("onions");
- }
-
- @Test()
- public void testDirSourceContactNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine(null);
- assertNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test()
- public void testDirSourceContactLineNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine("contact");
- assertNotNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test()
- public void testDirSourceContactLineOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine("contact ");
- assertNotNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestNoLine()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestLineNoSpace()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine("vote-digest");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestLineOneSpace()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine("vote-digest ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameNotAllowedChars()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithNickname("notAll()wed");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithNickname("1234567890123456789tooLong");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64("TooShort");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintEndsWithEqualSign()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8HMAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorTooShort() throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64("TooShort");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorEndsWithEqualSign()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64(
- "Yiti+nayuT2Efe2X1+M4nslwVuUAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished1960() throws DescriptorParseException {
- StatusEntryBuilder.createWithPublishedString("1960-11-29 21:34:27");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished9999() throws DescriptorParseException {
- StatusEntryBuilder.createWithPublishedString("9999-11-29 21:34:27");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress256() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("256.63.8.215");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress24() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("50.63.8/24");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressV6() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("::1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPort66666() throws DescriptorParseException {
- StatusEntryBuilder.createWithOrPort("66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortEighty() throws DescriptorParseException {
- StatusEntryBuilder.createWithOrPort("eighty");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMinusOne() throws DescriptorParseException {
- StatusEntryBuilder.createWithDirPort("-1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortZero() throws DescriptorParseException {
- StatusEntryBuilder.createWithDirPort("zero");
- }
-
- @Test()
- public void testSLineNoSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithSLine("s");
- assertTrue(consensus.getStatusEntry(
- "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
- }
-
- @Test()
- public void testSLineOneSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithSLine("s ");
- assertTrue(consensus.getStatusEntry(
- "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoSLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.sLine = sb.sLine + "\n" + sb.sLine;
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWLineNoSpace() throws DescriptorParseException {
- StatusEntryBuilder.createWithWLine("w");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWLineOneSpace() throws DescriptorParseException {
- StatusEntryBuilder.createWithWLine("w ");
- }
-
- @Test()
- public void testWLineWarpSeven() throws DescriptorParseException {
- StatusEntryBuilder.createWithWLine("w Warp=7");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoWLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.wLine = sb.wLine + "\n" + sb.wLine;
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
-
- @Test()
- public void testWLineUnmeasured() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.wLine = "w Bandwidth=42424242 Unmeasured=1";
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
- if (s.getBandwidth() == 42424242L) {
- assertTrue(s.getUnmeasured());
- }
- }
- }
-
- @Test()
- public void testWLineNotUnmeasured() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithWLine("w Bandwidth=20");
- for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
- assertFalse(s.getUnmeasured());
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPolicy() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPorts() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p accept");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPolicyNoPorts() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineProject() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p project 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoPLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.pLine = sb.pLine + "\n" + sb.pLine;
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
-
- @Test()
- public void testNoStatusEntries() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.clear();
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertFalse(consensus.containsStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectoryFooterNoLine()
- throws DescriptorParseException {
- /* This breaks, because a bandwidth-weights line without a preceding
- * directory-footer line is not allowed. */
- ConsensusBuilder.createWithDirectoryFooterLine(null);
- }
-
- @Test()
- public void testDirectoryFooterMissing()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.setDirectoryFooterLine(null);
- cb.setBandwidthWeightsLine(null);
- /* This does not break, because directory footers were optional before
- * consensus method 9. */
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testDirectoryFooterLineSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithDirectoryFooterLine("directory-footer ");
- }
-
- @Test()
- public void testBandwidthWeightsNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithBandwidthWeightsLine(null);
- assertNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testBandwidthWeightsLineNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithBandwidthWeightsLine("bandwidth-weights");
- assertNotNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testBandwidthWeightsLineOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithBandwidthWeightsLine("bandwidth-weights ");
- assertNotNull(consensus.getBandwidthWeights());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthWeightsLineNoEqualSign()
- throws DescriptorParseException {
- ConsensusBuilder.createWithBandwidthWeightsLine(
- "bandwidth-weights Wbd-285");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignatureIdentityTooShort()
- throws DescriptorParseException {
- DirectorySignatureBuilder.createWithIdentity("ED03BB616EB2F60");
- }
-
- @Test()
- public void testDirectorySignatureIdentityTooLong()
- throws DescriptorParseException {
- /* This hex string has an unusual length of 58 hex characters, but
- * dir-spec.txt only requires a hex string, and we can't know all hex
- * string lengths for all future digest algorithms, so let's just
- * accept this. */
- DirectorySignatureBuilder.createWithIdentity(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226ED03BB616EB2F60BEC");
- }
-
- @Test()
- public void testDirectorySignatureSigningKeyTooShort()
- throws DescriptorParseException {
- /* See above, we accept this hex string even though it's unusually
- * short. */
- DirectorySignatureBuilder.createWithSigningKey("845CF1D0B370CA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignatureSigningKeyTooShortOddNumber()
- throws DescriptorParseException {
- /* We don't accept this hex string, because it contains an odd number
- * of hex characters. */
- DirectorySignatureBuilder.createWithSigningKey("845");
- }
-
- @Test()
- public void testDirectorySignatureSigningKeyTooLong()
- throws DescriptorParseException {
- /* See above, we accept this hex string even though it's unusually
- * long. */
- DirectorySignatureBuilder.createWithSigningKey(
- "845CF1D0B370CA443A8579D18E7987E7E532F639845CF1D0B370CA443A");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNonAsciiByte20() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- byte[] consensusBytes = cb.buildConsensus();
- consensusBytes[20] = (byte) 200;
- new RelayNetworkStatusConsensusImpl(consensusBytes, true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNonAsciiByteMinusOne()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.networkStatusVersionLine = "Xnetwork-status-version 3";
- byte[] consensusBytes = cb.buildConsensus();
- consensusBytes[0] = (byte) 200;
- new RelayNetworkStatusConsensusImpl(consensusBytes, true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedHeaderLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedHeaderLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedHeaderLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirSourceLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirSourceLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedStatusEntryLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedStatusEntryLine(
- unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedStatusEntryLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedStatusEntryLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirectoryFooterLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedFooterLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirectoryFooterLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedFooterLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirectorySignatureLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedDirectorySignatureLine(
- unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedDirectorySignatureLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedDirectorySignatureLine(unrecognizedLine,
- false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
deleted file mode 100644
index 1c840f5..0000000
--- a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
+++ /dev/null
@@ -1,1373 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.RelayNetworkStatusVote;
-
-/* TODO Add test cases for all lines starting with "opt ". */
-
-/* Test parsing of network status votes. Some of the vote-parsing code is
- * already tested in the consensus-parsing tests. The tests in this class
- * focus on the differences between votes and consensuses that are mostly
- * in the directory header. */
-public class RelayNetworkStatusVoteImplTest {
-
- /* Helper class to build a vote based on default data and modifications
- * requested by test methods. */
- private static class VoteBuilder {
- private String networkStatusVersionLine = "network-status-version 3";
- private static RelayNetworkStatusVote
- createWithNetworkStatusVersionLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.networkStatusVersionLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String voteStatusLine = "vote-status vote";
- private static RelayNetworkStatusVote
- createWithVoteStatusLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.voteStatusLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String consensusMethodsLine =
- "consensus-methods 1 2 3 4 5 6 7 8 9 10 11";
- private static RelayNetworkStatusVote
- createWithConsensusMethodsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.consensusMethodsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String publishedLine = "published 2011-11-30 08:50:01";
- private static RelayNetworkStatusVote
- createWithPublishedLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.publishedLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String validAfterLine = "valid-after 2011-11-30 09:00:00";
- private static RelayNetworkStatusVote
- createWithValidAfterLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.validAfterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
- private static RelayNetworkStatusVote
- createWithFreshUntilLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.freshUntilLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String validUntilLine = "valid-until 2011-11-30 12:00:00";
- private static RelayNetworkStatusVote
- createWithValidUntilLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.validUntilLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String votingDelayLine = "voting-delay 300 300";
- private static RelayNetworkStatusVote
- createWithVotingDelayLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.votingDelayLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String clientVersionsLine = "client-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- private static RelayNetworkStatusVote
- createWithClientVersionsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.clientVersionsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String serverVersionsLine = "server-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- private static RelayNetworkStatusVote
- createWithServerVersionsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.serverVersionsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String packageLines = null;
- protected static RelayNetworkStatusVote
- createWithPackageLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.packageLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String knownFlagsLine = "known-flags Authority BadExit Exit "
- + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
- private static RelayNetworkStatusVote
- createWithKnownFlagsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.knownFlagsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String flagThresholdsLine = "flag-thresholds "
- + "stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 "
- + "guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 "
- + "guard-bw-exc-exits=184320 enough-mtbf=1";
- private static RelayNetworkStatusVote
- createWithFlagThresholdsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.flagThresholdsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String paramsLine = "params "
- + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
- + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
- + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
- + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
- private static RelayNetworkStatusVote
- createWithParamsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.paramsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirSourceLine = "dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80";
- private static RelayNetworkStatusVote
- createWithDirSourceLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirSourceLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String contactLine = "contact 4096R/E012B42D Jacob Appelbaum "
- + "<jacob@xxxxxxxxxxxxx>";
- private static RelayNetworkStatusVote
- createWithContactLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.contactLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String legacyDirKeyLine = null;
- private static RelayNetworkStatusVote
- createWithLegacyDirKeyLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.legacyDirKeyLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCertificateVersionLine =
- "dir-key-certificate-version 3";
- private static RelayNetworkStatusVote
- createWithDirKeyCertificateVersionLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCertificateVersionLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String fingerprintLine = "fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C";
- private static RelayNetworkStatusVote
- createWithFingerprintLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.fingerprintLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyPublishedLine = "dir-key-published 2011-04-27 "
- + "05:34:37";
- private static RelayNetworkStatusVote
- createWithDirKeyPublishedLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyPublishedLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyExpiresLine = "dir-key-expires 2012-04-27 "
- + "05:34:37";
- private static RelayNetworkStatusVote
- createWithDirKeyExpiresLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyExpiresLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirIdentityKeyLines = "dir-identity-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIIBigKCAYEAtKpuLgVK25sfScjsxfVU1ljofrDygt9GP7bNJl/rghX42KUT97"
- + "5W\nrGp/fbhF7p+FcKCzNOhJFINQbRf/5E3lN8mzoamIU43QqQ9RRVf94688Us"
- + "azVsAN\nNVT0v9J0cr387WePjenRuIE1MmiP0nmw/XdvbPTayqax7VYlcUMXGH"
- + "l8DnWix1EN\nRwmeig+JBte0JS12oo2HG9zcSfjLJVjY6ZmvRrVycXiRxGc/Jg"
- + "NlSrV4cxUNykaB\nJ6pO6J499OZfQu7m1vAPTENrVJ4yEfRGRwFIY+d/s8BkKc"
- + "aiWtXAfTe31uBI6GEH\nmS3HNu1JVSuoaUiQIvVYDLMfBvMcNyAx97UT1l6E0T"
- + "n6a7pgChrquGwXai1xGzk8\n58aXwdSFoFBSTCkyemopq5H20p/nkPAO0pHL1k"
- + "TvcaKz9CEj4XcKm+kOmzejYmIa\nkbWNcRpXPiUZ+xmwGtsq30xrzqiONmERkx"
- + "qlmf7bVQPFvh3Kz6hGcmTBhTbHSe9h\nzDgmdaTNn3EHAgMBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static RelayNetworkStatusVote
- createWithDirIdentityKeyLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirIdentityKeyLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirSigningKeyLines = "dir-signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAN05qyHFQlTqykMP8yLuD4G2UuYulD4Xs8iSX5uqF+WGsUA1E4zZh4"
- + "8h\nDFj8+drFiCu3EqhMEmVG4ACtJK2uz6D1XohUsbPWTR6LSnWJ8q6/zfTSLu"
- + "mBGsN7\nPUXyMNjwRKL6UvrcbYk1d2mRBLO7SAP/sFW5fHhIBVeLIWrzQ19rAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static RelayNetworkStatusVote
- createWithDirSigningKeyLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirSigningKeyLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCrosscertLines = "dir-key-crosscert\n"
- + "-----BEGIN ID SIGNATURE-----\n"
- + "rPBFn6IJ6TvAHj4pSwlg+RTn1fP89JGSVa08wuyJr5dAvZsdakQXvRjamT9oJU"
- + "aZ\nnY5Rl/tRlGuSQ0BglTPPKoXdKERK0FUr9f0EKrQy7NDUgE2j9losiRuyKz"
- + "hA3neZ\nK4yF8bhqAwM51u7fzAhIjNeRif9c04rhFJJCseco84w=\n"
- + "-----END ID SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirKeyCrosscertLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCrosscertLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCertificationLines = "dir-key-certification\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "hPSh6FuohNF5ccjiMbkvr8cZJwGFuL11cNtwN9k0X3pUdFZVATIEkqBe7z+rE2"
- + "PX\nPw+BGyC6wYAieoTVIhLpwKqd7DXLYjuhPZ28+7MQaDL01AqYeRp5PT01Px"
- + "rFY0Um\nlVf95uqUitgvDT76Ne4ExWk6UvGlYB9OBgBySZz8VWe9znoMqb0uHn"
- + "/p8IzqTApT\nAxRWXBHClntMeRqtGxaj8DcdJFn8yMxQiZG7MfDg2sq2ySPJyG"
- + "lN+neoVDVhZiDI\n9LTNmw60gWlUp2erFeam8Mo1ZBC4DPNjQEm6QeHZFZMkhD"
- + "uO6SwS/FL712A42+Co\nYtMaVot/p5FG2ZSBXbgl2XP5/z8ELnpmXqMbPAoWRo"
- + "3BPNSJkIQQNog8Q5ZrK+av\nZDw5eGPltGKsXOkvuzIMM8nBeAnDPDgYvzrIFO"
- + "bEGbvY/P8mzVAZxp3Yz+sRtNel\nC1SWz/Fx+Saex5oI7DJ3xtSD4XqKb/wYwZ"
- + "FT8IxDYq1t2tFXdHxd4QPRVcvc0zYC\n"
- + "-----END SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirKeyCertificationLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCertificationLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private List<String> statusEntries = null;
- private static RelayNetworkStatusVote createWithStatusEntries(
- List<String> statusEntries) throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.statusEntries = statusEntries;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String directoryFooterLine = "directory-footer";
- private static RelayNetworkStatusVote
- createWithDirectoryFooterLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.directoryFooterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String directorySignatureLines = "directory-signature "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C "
- + "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
- + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
- + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirectorySignatureLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.directorySignatureLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String unrecognizedHeaderLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedHeaderLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirSourceLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedDirSourceLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedDirSourceLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedStatusEntryLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedFooterLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedFooterLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedFooterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirectorySignatureLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedDirectorySignatureLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedDirectorySignatureLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
-
- private VoteBuilder() {
- if (this.statusEntries != null) {
- return;
- }
- this.statusEntries = new ArrayList<>();
- this.statusEntries.add("r right2privassy3 "
- + "ADQ6gCT3DiFHKPDFr3rODBUI8HM lJY5Vf7kXec+VdkGW2flEsfkFC8 "
- + "2011-11-12 00:03:40 50.63.8.215 9023 0\n"
- + "s Exit Fast Guard Running Stable Valid\n"
- + "opt v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)\n"
- + "w Bandwidth=297 Measured=73\n"
- + "p accept 80,1194,1220,1293,1500,1533,1677,1723,1863,"
- + "2082-2083,2086-2087,2095-2096,2102-2104,3128,3389,3690,4321,"
- + "4643,5050,5190,5222-5223,5228,5900,6660-6669,6679,6697,8000,"
- + "8008,8074,8080,8087-8088,8443,8888,9418,9999-10000,19294,"
- + "19638\n"
- + "m 8,9,10,11 "
- + "sha256=9ciEx9t0McXk9A06I7qwN7pxuNOdpCP64RV/6cx2Zkc");
- }
- private byte[] buildVote() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendDirSource(sb);
- this.appendStatusEntries(sb);
- this.appendFooter(sb);
- this.appendDirectorySignature(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.networkStatusVersionLine != null) {
- sb.append(this.networkStatusVersionLine).append("\n");
- }
- if (this.voteStatusLine != null) {
- sb.append(this.voteStatusLine).append("\n");
- }
- if (this.consensusMethodsLine != null) {
- sb.append(this.consensusMethodsLine).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.validAfterLine != null) {
- sb.append(this.validAfterLine).append("\n");
- }
- if (this.freshUntilLine != null) {
- sb.append(this.freshUntilLine).append("\n");
- }
- if (this.validUntilLine != null) {
- sb.append(this.validUntilLine).append("\n");
- }
- if (this.votingDelayLine != null) {
- sb.append(this.votingDelayLine).append("\n");
- }
- if (this.clientVersionsLine != null) {
- sb.append(this.clientVersionsLine).append("\n");
- }
- if (this.serverVersionsLine != null) {
- sb.append(this.serverVersionsLine).append("\n");
- }
- if (this.packageLines != null) {
- sb.append(this.packageLines).append("\n");
- }
- if (this.knownFlagsLine != null) {
- sb.append(this.knownFlagsLine).append("\n");
- }
- if (this.flagThresholdsLine != null) {
- sb.append(this.flagThresholdsLine).append("\n");
- }
- if (this.paramsLine != null) {
- sb.append(this.paramsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendDirSource(StringBuilder sb) {
- if (this.dirSourceLine != null) {
- sb.append(this.dirSourceLine).append("\n");
- }
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.legacyDirKeyLine != null) {
- sb.append(this.legacyDirKeyLine).append("\n");
- }
- if (this.dirKeyCertificateVersionLine != null) {
- sb.append(this.dirKeyCertificateVersionLine).append("\n");
- }
- if (this.fingerprintLine != null) {
- sb.append(this.fingerprintLine).append("\n");
- }
- if (this.dirKeyPublishedLine != null) {
- sb.append(this.dirKeyPublishedLine).append("\n");
- }
- if (this.dirKeyExpiresLine != null) {
- sb.append(this.dirKeyExpiresLine).append("\n");
- }
- if (this.dirIdentityKeyLines != null) {
- sb.append(this.dirIdentityKeyLines).append("\n");
- }
- if (this.dirSigningKeyLines != null) {
- sb.append(this.dirSigningKeyLines).append("\n");
- }
- if (this.dirKeyCrosscertLines != null) {
- sb.append(this.dirKeyCrosscertLines).append("\n");
- }
- if (this.dirKeyCertificationLines != null) {
- sb.append(this.dirKeyCertificationLines).append("\n");
- }
- if (this.unrecognizedDirSourceLine != null) {
- sb.append(this.unrecognizedDirSourceLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- private void appendFooter(StringBuilder sb) {
- if (this.directoryFooterLine != null) {
- sb.append(this.directoryFooterLine).append("\n");
- }
- if (this.unrecognizedFooterLine != null) {
- sb.append(this.unrecognizedFooterLine).append("\n");
- }
- }
- private void appendDirectorySignature(StringBuilder sb) {
- if (this.directorySignatureLines != null) {
- sb.append(directorySignatureLines).append("\n");
- }
- if (this.unrecognizedDirectorySignatureLine != null) {
- sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
- }
- }
- }
-
- @Test()
- public void testSampleVote() throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- RelayNetworkStatusVote vote =
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- assertEquals(3, vote.getNetworkStatusVersion());
- List<Integer> consensusMethods = Arrays.asList(
- new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
- assertEquals(vote.getConsensusMethods(), consensusMethods);
- assertEquals(1322643001000L, vote.getPublishedMillis());
- assertEquals(1322643600000L, vote.getValidAfterMillis());
- assertEquals(1322647200000L, vote.getFreshUntilMillis());
- assertEquals(1322654400000L, vote.getValidUntilMillis());
- assertEquals(300L, vote.getVoteSeconds());
- assertEquals(300L, vote.getDistSeconds());
- assertTrue(vote.getKnownFlags().contains("Running"));
- assertEquals(30000, (int) vote.getConsensusParams().get(
- "CircuitPriorityHalflifeMsec"));
- assertEquals("Tor 0.2.1.29 (r8e9b25e6c7a2e70c)",
- vote.getStatusEntry("00343A8024F70E214728F0C5AF7ACE0C1508F073").
- getVersion());
- assertEquals(3, vote.getDirKeyCertificateVersion());
- assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
- vote.getIdentity());
- assertEquals(1303882477000L, /* 2011-04-27 05:34:37 */
- vote.getDirKeyPublishedMillis());
- assertEquals(1335504877000L, /* 2012-04-27 05:34:37 */
- vote.getDirKeyExpiresMillis());
- assertEquals("-----BEGIN RSA PUBLIC KEY-----",
- vote.getDirIdentityKey().split("\n")[0]);
- assertEquals("-----BEGIN RSA PUBLIC KEY-----",
- vote.getDirSigningKey().split("\n")[0]);
- assertEquals("-----BEGIN ID SIGNATURE-----",
- vote.getDirKeyCrosscert().split("\n")[0]);
- assertEquals("-----BEGIN SIGNATURE-----",
- vote.getDirKeyCertification().split("\n")[0]);
- assertEquals(1, vote.getSignatures().size());
- DirectorySignature signature = vote.getSignatures().get(0);
- assertEquals("sha1", signature.getAlgorithm());
- assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
- signature.getIdentity());
- assertEquals("EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19",
- signature.getSigningKeyDigest());
- assertEquals("-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
- + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
- + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----\n", signature.getSignature());
- assertTrue(vote.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLineSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n ");
- }
-
- @Test()
- public void testNetworkStatusVersionPrefixLineAtChar()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "@vote\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "directory-footer\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLinePoundChar()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "#vote\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersion42()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 42");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionFourtyTwo()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version FourtyTwo");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoLine() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionSpaceBefore()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- " network-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusSpaceBefore() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(" vote-status vote");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status ");
- }
-
- @Test()
- public void testVoteStatusVoteOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status vote ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusConsensus() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status consensus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusTheMagicVoteStatus()
- throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(
- "vote-status TheMagicVoteStatus");
- }
-
- @Test()
- public void testConsensusMethodNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithConsensusMethodsLine(null);
- assertNull(vote.getConsensusMethods());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodEleven()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine(
- "consensus-methods eleven");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodMinusOne()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNinePeriod()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods "
- + "999999999999999999999999999999999999999999999999999999999999");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodTwoLines()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine(
- "consensus-method 1\nconsensus-method 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedNoLine() throws DescriptorParseException {
- VoteBuilder.createWithPublishedLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoLine() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterLongAgo() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after long ago");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterFeb30() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine(
- "valid-after 2011-02-30 09:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilNoLine() throws DescriptorParseException {
- VoteBuilder.createWithFreshUntilLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilAroundTen() throws DescriptorParseException {
- VoteBuilder.createWithFreshUntilLine(
- "fresh-until 2011-11-30 around ten");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidUntilTomorrowMorning()
- throws DescriptorParseException {
- VoteBuilder.createWithValidUntilLine(
- "valid-until tomorrow morning");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoLine() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayTriple() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine(
- "voting-delay 300 300 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelaySingle() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneTwo() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay one two");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsComma() throws DescriptorParseException {
- VoteBuilder.createWithClientVersionsLine("client-versions ,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsCommaVersion()
- throws DescriptorParseException {
- VoteBuilder.createWithClientVersionsLine(
- "client-versions ,0.2.2.34");
- }
-
- @Test()
- public void testPackageNone() throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(null);
- assertNull(vote.getPackageLines());
- }
-
- @Test()
- public void testPackageOne() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http digest=digest";
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(packageLine);
- assertEquals(packageLine.substring("package ".length()),
- vote.getPackageLines().get(0));
- }
-
- @Test()
- public void testPackageTwo() throws DescriptorParseException {
- List<String> packageLines = Arrays.asList(
- "package shouldbesecond 0 http digest=digest",
- "package outoforder 0 http digest=digest");
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(packageLines.get(0)
- + "\n" + packageLines.get(1));
- for (int i = 0; i < packageLines.size(); i++) {
- assertEquals(packageLines.get(i).substring("package ".length()),
- vote.getPackageLines().get(i));
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPackageIncomplete() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http";
- ConsensusBuilder.createWithPackageLines(packageLine);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoLine() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine("known-flags");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine("known-flags ");
- }
-
- @Test()
- public void testFlagThresholdsLine() throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- RelayNetworkStatusVote vote =
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- assertEquals(693369L, vote.getStableUptime());
- assertEquals(153249L, vote.getStableMtbf());
- assertEquals(40960L, vote.getFastBandwidth());
- assertEquals(94.669, vote.getGuardWfu(), 0.001);
- assertEquals(691200L, vote.getGuardTk());
- assertEquals(174080L, vote.getGuardBandwidthIncludingExits());
- assertEquals(184320L, vote.getGuardBandwidthExcludingExits());
- assertEquals(1, vote.getEnoughMtbfInfo());
- }
-
- @Test()
- public void testFlagThresholdsNoLine() throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithFlagThresholdsLine(null);
- assertEquals(-1L, vote.getStableUptime());
- assertEquals(-1L, vote.getStableMtbf());
- assertEquals(-1L, vote.getFastBandwidth());
- assertEquals(-1.0, vote.getGuardWfu(), 0.001);
- assertEquals(-1L, vote.getGuardTk());
- assertEquals(-1L, vote.getGuardBandwidthIncludingExits());
- assertEquals(-1L, vote.getGuardBandwidthExcludingExits());
- assertEquals(-1, vote.getEnoughMtbfInfo());
- }
-
- @Test()
- public void testFlagThresholdsAllZeroes()
- throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds "
- + "stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.0% "
- + "guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 "
- + "enough-mtbf=0");
- assertEquals(0L, vote.getStableUptime());
- assertEquals(0L, vote.getStableMtbf());
- assertEquals(0L, vote.getFastBandwidth());
- assertEquals(0.0, vote.getGuardWfu(), 0.001);
- assertEquals(0L, vote.getGuardTk());
- assertEquals(0L, vote.getGuardBandwidthIncludingExits());
- assertEquals(0L, vote.getGuardBandwidthExcludingExits());
- assertEquals(0, vote.getEnoughMtbfInfo());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdsNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdsOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdDuplicate()
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.flagThresholdsLine = vb.flagThresholdsLine + "\n"
- + vb.flagThresholdsLine;
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source "
- + "urrassssssssssssssssssssssssssssssssssssssssssssssss "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameIllegalCharacters()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urra$ "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test()
- public void testFingerprintLowerCase() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987e1d626e3eba5e5e75a458de0626d088c 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintIllegalCharacters()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "ABCDEFGHIJKLM6E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + " 208.83.223.34 208.83.223.34 443 80");
- }
-
- @Test()
- public void testHostname256()
- throws DescriptorParseException {
- /* This test doesn't fail, because we're not parsing the hostname. */
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 256.256.256.256 "
- + "208.83.223.34 443 80");
- assertEquals("256.256.256.256", vote.getHostname());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHostnameMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
- + "80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress256()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "256.256.256.256 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
- + "80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMinus443()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 -443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortFourFourThree()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 four-four-three 80");
- }
-
- @Test()
- public void testDirPort0() throws DescriptorParseException {
- /* This test doesn't fail, because we're accepting DirPort 0, even
- * though it doesn't make sense from Tor's view. */
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 0 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortMissing() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 ");
- }
-
- @Test()
- public void testDirPortOrPortIdentical()
- throws DescriptorParseException {
- /* This test doesn't fail, even though identical OR and Dir port don't
- * make much sense from Tor's view. */
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 80 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80\ndir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test()
- public void testContactLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithContactLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testContactLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithContactLine("contact 4096R/E012B42D Jacob "
- + "Appelbaum <jacob@xxxxxxxxxxxxx>\ncontact 4096R/E012B42D Jacob "
- + "Appelbaum <jacob@xxxxxxxxxxxxx>");
- }
-
- @Test()
- public void testLegacyDirKeyLine() throws DescriptorParseException {
- RelayNetworkStatusVote vote = VoteBuilder.createWithLegacyDirKeyLine(
- "legacy-dir-key 81349FC1F2DBA2C2C11B45CB9706637D480AB913");
- assertEquals("81349FC1F2DBA2C2C11B45CB9706637D480AB913",
- vote.getLegacyDirKey());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testLegacyDirKeyLineNoId() throws DescriptorParseException {
- VoteBuilder.createWithLegacyDirKeyLine("legacy-dir-key ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificateVersionLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificateVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificateVersionLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificateVersionLine(
- "dir-key-certificate-version 3\ndir-key-certificate-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C\nfingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineTooLong()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineTooShort()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublished3011()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "3011-04-27 05:34:37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublishedRecentlyAtNoon()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "recently 12:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublishedRecentlyNoTime()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "recently");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresSoonAtNoon()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires "
- + "soon 12:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires 2012-04-27 "
- + "05:34:37\ndir-key-expires 2012-04-27 05:34:37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirIdentityKeyLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirIdentityKeyLines(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSigningKeyLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSigningKeyLines(null);
- }
-
- @Test()
- public void testDirKeyCrosscertLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCrosscertLines(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificationLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificationLines(null);
- }
-
- @Test()
- public void testDirectoryFooterLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirectoryFooterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignaturesLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirectorySignatureLines(null);
- }
-
- @Test()
- public void testDirectorySignaturesLinesTwoAlgorithms()
- throws DescriptorParseException {
- String identitySha256 = "32519E5CB7254AB5A94CC9925EC7676E53D5D52EEAB7"
- + "914BD3ED751E537CAFCC";
- String signingKeyDigestSha256 = "5A59D99C17831B9254422B6C5AA10CC59381"
- + "6CAA5241E22ECAE8BBB4E8E9D1FC";
- String signatureSha256 = "-----BEGIN SIGNATURE-----\n"
- + "x57Alc424/zHS73SHokghGtNBVrBjtUz+gSL5w9AHGKUQcMyfw4Z9aDlKpTbFc"
- + "5W\nnyIvFmM9C2OAH0S1+a647HHIxhE0zKf4+yKSwzqSyL6sbKQygVlJsRHNRr"
- + "cFg8lp\nqBxEwvxQoA4xEDqnerR92pbK9l42nNLiKOcoReUqbbQ=\n"
- + "-----END SIGNATURE-----";
- String identitySha1 = "80550987E1D626E3EBA5E5E75A458DE0626D088C";
- String signingKeyDigestSha1 =
- "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19";
- String signatureSha1 = "-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxnF3"
- + "Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40OikfOI"
- + "wEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----";
- String signaturesLines = String.format(
- "directory-signature sha256 %s %s\n%s\n"
- + "directory-signature %s %s\n%s", identitySha256,
- signingKeyDigestSha256, signatureSha256, identitySha1,
- signingKeyDigestSha1, signatureSha1);
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
- assertEquals(2, vote.getSignatures().size());
- DirectorySignature firstSignature = vote.getSignatures().get(0);
- assertEquals("sha256", firstSignature.getAlgorithm());
- assertEquals(identitySha256, firstSignature.getIdentity());
- assertEquals(signingKeyDigestSha256,
- firstSignature.getSigningKeyDigest());
- assertEquals(signatureSha256 + "\n", firstSignature.getSignature());
- DirectorySignature secondSignature = vote.getSignatures().get(1);
- assertEquals("sha1", secondSignature.getAlgorithm());
- assertEquals(identitySha1, secondSignature.getIdentity());
- assertEquals(signingKeyDigestSha1,
- secondSignature.getSigningKeyDigest());
- assertEquals(signatureSha1 + "\n", secondSignature.getSignature());
- assertEquals(signingKeyDigestSha1, vote.getSigningKeyDigest());
- }
-
- @Test()
- public void testDirectorySignaturesLinesTwoAlgorithmsSameDigests()
- throws DescriptorParseException {
- String signaturesLines = "directory-signature 00 00\n"
- + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----\n"
- + "directory-signature sha256 00 00\n"
- + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----";
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
- assertEquals(2, vote.getSignatures().size());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedHeaderLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedHeaderLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedHeaderLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirSourceLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirSourceLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedFooterLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedFooterLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedFooterLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedFooterLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test()
- public void testIdEd25519MasterKey()
- throws DescriptorParseException {
- String masterKey25519 = "8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8";
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r PDrelay1 AAFJ5u9xAqrKlpDW6N0pMhJLlKs "
- + "bgJiI/la3e9u0K7cQ5pMSXhigHI 2015-12-01 04:54:30 95.215.44.189 "
- + "8080 0\n"
- + "id ed25519 " + masterKey25519);
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithStatusEntries(statusEntries);
- String fingerprint = vote.getStatusEntries().firstKey();
- assertEquals(masterKey25519,
- vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
- }
-
- @Test()
- public void testIdEd25519None()
- throws DescriptorParseException {
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
- + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
- + "443 9030\n"
- + "id ed25519 none");
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithStatusEntries(statusEntries);
- String fingerprint = vote.getStatusEntries().firstKey();
- assertEquals("none",
- vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024None()
- throws DescriptorParseException {
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
- + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
- + "443 9030\n"
- + "id rsa1024 none");
- VoteBuilder.createWithStatusEntries(statusEntries);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
deleted file mode 100644
index cd3f1a5..0000000
--- a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ /dev/null
@@ -1,1605 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.SortedMap;
-
-import org.junit.Test;
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* Test parsing of relay server descriptors. */
-public class ServerDescriptorImplTest {
-
- /* Helper class to build a descriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String routerLine = "router saberrider2008 94.134.192.243 "
- + "9001 0 0";
- private static ServerDescriptor createWithRouterLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String bandwidthLine = "bandwidth 51200 51200 53470";
- private static ServerDescriptor createWithBandwidthLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.bandwidthLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String platformLine = "platform Tor 0.2.2.35 "
- + "(git-b04388f9e7546a9f) on Linux i686";
- private static ServerDescriptor createWithPlatformLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.platformLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String publishedLine = "published 2012-01-01 04:03:19";
- private static ServerDescriptor createWithPublishedLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.publishedLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String fingerprintLine = "opt fingerprint D873 3048 FC8E "
- + "C910 2466 AD8F 3098 622B F1BF 71FD";
- private static ServerDescriptor createWithFingerprintLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.fingerprintLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hibernatingLine = null;
- private static ServerDescriptor createWithHibernatingLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hibernatingLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String uptimeLine = "uptime 48";
- private static ServerDescriptor createWithUptimeLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.uptimeLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String onionKeyLines = "onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
- + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
- + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static ServerDescriptor createWithOnionKeyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.onionKeyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String signingKeyLines = "signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
- + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
- + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static ServerDescriptor createWithSigningKeyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.signingKeyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String onionKeyCrosscertLines = null;
- private static ServerDescriptor createWithOnionKeyCrosscertLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.onionKeyCrosscertLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyCrosscertLines = null;
- private static ServerDescriptor createWithNtorOnionKeyCrosscertLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ntorOnionKeyCrosscertLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String exitPolicyLines = "reject *:*";
- private static ServerDescriptor createWithExitPolicyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.exitPolicyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String contactLine = "contact Random Person <nobody AT "
- + "example dot com>";
- private static ServerDescriptor createWithContactLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.contactLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String familyLine = null;
- private static ServerDescriptor createWithFamilyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.familyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String readHistoryLine = null;
- private static ServerDescriptor createWithReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.readHistoryLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String writeHistoryLine = null;
- private static ServerDescriptor createWithWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.writeHistoryLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String eventdnsLine = null;
- private static ServerDescriptor createWithEventdnsLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.eventdnsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String cachesExtraInfoLine = null;
- private static ServerDescriptor createWithCachesExtraInfoLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.cachesExtraInfoLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String extraInfoDigestLine = "opt extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74";
- private static ServerDescriptor createWithExtraInfoDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.extraInfoDigestLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hiddenServiceDirLine = "opt hidden-service-dir";
- private static ServerDescriptor createWithHiddenServiceDirLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hiddenServiceDirLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String protocolsLine = "opt protocols Link 1 2 Circuit 1";
- private static ServerDescriptor createWithProtocolsLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.protocolsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String allowSingleHopExitsLine = null;
- private static ServerDescriptor
- createWithAllowSingleHopExitsLine(String line)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.allowSingleHopExitsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ipv6PolicyLine = null;
- private static ServerDescriptor createWithIpv6PolicyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ipv6PolicyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyLine = null;
- private static ServerDescriptor createWithNtorOnionKeyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ntorOnionKeyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String tunnelledDirServerLine = null;
- private static ServerDescriptor createWithTunnelledDirServerLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.tunnelledDirServerLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String routerSignatureLines = "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----";
- private static ServerDescriptor createWithRouterSignatureLines(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerSignatureLines = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String unrecognizedLine = null;
- private static ServerDescriptor createWithUnrecognizedLine(
- String line, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.unrecognizedLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private byte[] nonAsciiLineBytes = null;
- private static ServerDescriptor createWithNonAsciiLineBytes(
- byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.nonAsciiLineBytes = lineBytes;
- return new RelayServerDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private String identityEd25519Lines = null,
- masterKeyEd25519Line = null, routerSigEd25519Line = null;
- private static ServerDescriptor createWithEd25519Lines(
- String identityEd25519Lines, String masterKeyEd25519Line,
- String routerSigEd25519Line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.identityEd25519Lines = identityEd25519Lines;
- db.masterKeyEd25519Line = masterKeyEd25519Line;
- db.routerSigEd25519Line = routerSigEd25519Line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.routerLine != null) {
- sb.append(this.routerLine).append("\n");
- }
- if (this.identityEd25519Lines != null) {
- sb.append(this.identityEd25519Lines).append("\n");
- }
- if (this.masterKeyEd25519Line != null) {
- sb.append(this.masterKeyEd25519Line).append("\n");
- }
- if (this.bandwidthLine != null) {
- sb.append(this.bandwidthLine).append("\n");
- }
- if (this.platformLine != null) {
- sb.append(this.platformLine).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.fingerprintLine != null) {
- sb.append(this.fingerprintLine).append("\n");
- }
- if (this.hibernatingLine != null) {
- sb.append(this.hibernatingLine).append("\n");
- }
- if (this.uptimeLine != null) {
- sb.append(this.uptimeLine).append("\n");
- }
- if (this.onionKeyLines != null) {
- sb.append(this.onionKeyLines).append("\n");
- }
- if (this.signingKeyLines != null) {
- sb.append(this.signingKeyLines).append("\n");
- }
- if (this.onionKeyCrosscertLines != null) {
- sb.append(this.onionKeyCrosscertLines).append("\n");
- }
- if (this.ntorOnionKeyCrosscertLines != null) {
- sb.append(this.ntorOnionKeyCrosscertLines).append("\n");
- }
- if (this.exitPolicyLines != null) {
- sb.append(this.exitPolicyLines).append("\n");
- }
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.familyLine != null) {
- sb.append(this.familyLine).append("\n");
- }
- if (this.readHistoryLine != null) {
- sb.append(this.readHistoryLine).append("\n");
- }
- if (this.writeHistoryLine != null) {
- sb.append(this.writeHistoryLine).append("\n");
- }
- if (this.eventdnsLine != null) {
- sb.append(this.eventdnsLine).append("\n");
- }
- if (this.cachesExtraInfoLine != null) {
- sb.append(this.cachesExtraInfoLine).append("\n");
- }
- if (this.extraInfoDigestLine != null) {
- sb.append(this.extraInfoDigestLine).append("\n");
- }
- if (this.hiddenServiceDirLine != null) {
- sb.append(this.hiddenServiceDirLine).append("\n");
- }
- if (this.protocolsLine != null) {
- sb.append(this.protocolsLine).append("\n");
- }
- if (this.allowSingleHopExitsLine != null) {
- sb.append(this.allowSingleHopExitsLine).append("\n");
- }
- if (this.ipv6PolicyLine != null) {
- sb.append(this.ipv6PolicyLine).append("\n");
- }
- if (this.ntorOnionKeyLine != null) {
- sb.append(this.ntorOnionKeyLine).append("\n");
- }
- if (this.tunnelledDirServerLine != null) {
- sb.append(this.tunnelledDirServerLine).append("\n");
- }
- if (this.unrecognizedLine != null) {
- sb.append(this.unrecognizedLine).append("\n");
- }
- if (this.nonAsciiLineBytes != null) {
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(sb.toString().getBytes());
- baos.write(this.nonAsciiLineBytes);
- baos.write("\n".getBytes());
- if (this.routerSignatureLines != null) {
- baos.write(this.routerSignatureLines.getBytes());
- }
- return baos.toByteArray();
- } catch (IOException e) {
- return null;
- }
- }
- if (this.routerSigEd25519Line != null) {
- sb.append(this.routerSigEd25519Line).append("\n");
- }
- if (this.routerSignatureLines != null) {
- sb.append(this.routerSignatureLines).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- @Test()
- public void testSampleDescriptor() throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- ServerDescriptor descriptor =
- new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- assertEquals(9001, (int) descriptor.getOrPort());
- assertEquals(0, (int) descriptor.getSocksPort());
- assertEquals(0, (int) descriptor.getDirPort());
- assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
- descriptor.getPlatform());
- assertEquals(Arrays.asList(new Integer[] {1, 2}),
- descriptor.getLinkProtocolVersions());
- assertEquals(Arrays.asList(new Integer[] {1}),
- descriptor.getCircuitProtocolVersions());
- assertEquals(1325390599000L, descriptor.getPublishedMillis());
- assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
- descriptor.getFingerprint());
- assertEquals(48, descriptor.getUptime().longValue());
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(53470, (int) descriptor.getBandwidthObserved());
- assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
- descriptor.getExtraInfoDigest());
- assertEquals(Arrays.asList(new Integer[] {2}),
- descriptor.getHiddenServiceDirVersions());
- assertEquals("Random Person <nobody AT example dot com>",
- descriptor.getContact());
- assertEquals(Arrays.asList(new String[] {"reject *:*"}),
- descriptor.getExitPolicyLines());
- assertFalse(descriptor.isHibernating());
- assertNull(descriptor.getFamilyEntries());
- assertNull(descriptor.getReadHistory());
- assertNull(descriptor.getWriteHistory());
- assertFalse(descriptor.getUsesEnhancedDnsLogic());
- assertFalse(descriptor.getCachesExtraInfo());
- assertFalse(descriptor.getAllowSingleHopExits());
- assertTrue(descriptor.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterLineMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine(null);
- }
-
- @Test()
- public void testRouterOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithRouterLine("opt router saberrider2008 "
- + "94.134.192.243 9001 0 0");
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- assertEquals(9001, (int) descriptor.getOrPort());
- assertEquals(0, (int) descriptor.getSocksPort());
- assertEquals(0, (int) descriptor.getDirPort());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterLinePrecedingHibernatingLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("hibernating 1\nrouter "
- + "saberrider2008 94.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router 94.134.192.243 9001 "
- + "0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameInvalidChar() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router $aberrider2008 "
- + "94.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router "
- + "saberrider2008ReallyLongNickname 94.134.192.243 9001 0 0");
- }
-
- @Test()
- public void testNicknameTwoSpaces() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 9001 0 0");
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress24() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192/24 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress294() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "294.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 9001 "
- + "0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPort99001() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 99001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortOne() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 one 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortNewline() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 0\n 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 9001 0 ");
- }
-
- @Test()
- public void testPlatformMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine(null);
- assertNull(descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("opt platform Tor 0.2.2.35 "
- + "(git-b04388f9e7546a9f) on Linux i686");
- assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
- descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformNoSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("platform");
- assertEquals("", descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("platform ");
- assertEquals("", descriptor.getPlatform());
- }
-
- @Test()
- public void testProtocolsNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithProtocolsLine("protocols Link 1 2 Circuit 1");
- assertEquals(Arrays.asList(new Integer[] {1, 2}),
- descriptor.getLinkProtocolVersions());
- assertEquals(Arrays.asList(new Integer[] {1}),
- descriptor.getCircuitProtocolVersions());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testProtocolsAB() throws DescriptorParseException {
- DescriptorBuilder.createWithProtocolsLine("opt protocols Link A B "
- + "Circuit 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testProtocolsNoCircuitVersions()
- throws DescriptorParseException {
- DescriptorBuilder.createWithProtocolsLine("opt protocols Link 1 2");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine(null);
- }
-
- @Test()
- public void testPublishedOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-01-01 04:03:19");
- assertEquals(1325390599000L, descriptor.getPublishedMillis());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished2039() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2039-01-01 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished1912() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 1912-01-01 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedFeb31() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2012-02-31 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedNoTime() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2012-01-01");
- }
-
- @Test()
- public void testPublishedMillis() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-01-01 04:03:19.123");
- assertEquals(1325390599000L, descriptor.getPublishedMillis());
- }
-
- @Test()
- public void testFingerprintNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFingerprintLine("fingerprint D873 3048 FC8E C910 2466 "
- + "AD8F 3098 622B F1BF 71FD");
- assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
- descriptor.getFingerprint());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintG() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint G873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD D873");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintNoSpaces() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint "
- + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- }
-
- @Test()
- public void testUptimeMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUptimeLine(null);
- assertNull(descriptor.getUptime());
- }
-
- @Test()
- public void testUptimeOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUptimeLine("opt uptime 48");
- assertEquals(48, descriptor.getUptime().longValue());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeFourtyEight() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime fourty-eight");
- }
-
- @Test()
- public void testUptimeMinusOne() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeNoSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeFourEight() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime 4 8");
- }
-
- @Test()
- public void testBandwidthOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithBandwidthLine("opt bandwidth 51200 51200 53470");
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(53470, (int) descriptor.getBandwidthObserved());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthOneValue() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth 51200");
- }
-
- @Test()
- public void testBandwidthTwoValues() throws DescriptorParseException {
- /* This is allowed, because Tor versions 0.0.8 and older only wrote
- * bandwidth lines with rate and burst values, but no observed
- * value. */
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithBandwidthLine("bandwidth 51200 51200");
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(-1, (int) descriptor.getBandwidthObserved());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthFourValues() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth 51200 51200 "
- + "53470 53470");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthMinusOneTwoThree()
- throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth -1 -2 -3");
- }
-
- @Test()
- public void testExtraInfoDigestNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine("extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74");
- assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
- descriptor.getExtraInfoDigest());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestNoSpace()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestTooShort()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest 1469D1550738A25B1E7B47CDDBCD7B2899F5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestTooLong()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B741469");
- }
-
- @Test()
- public void testExtraInfoDigestMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine(null);
- assertNull(descriptor.getExtraInfoDigest());
- }
-
- @Test()
- public void testExtraInfoDigestAdditionalDigest()
- throws DescriptorParseException {
- String extraInfoDigest = "0879DB7B765218D7B3AE7557669D20307BB21CAA";
- String additionalExtraInfoDigest =
- "V609l+N6ActBveebfNbH5lQ6wHDNstDkFgyqEhBHwtA";
- String extraInfoDigestLine = String.format("extra-info-digest %s %s",
- extraInfoDigest, additionalExtraInfoDigest);
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine(extraInfoDigestLine);
- assertEquals(extraInfoDigest, descriptor.getExtraInfoDigest());
- }
-
- @Test()
- public void testOnionKeyOpt() throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyLines("opt onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
- + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
- + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----");
- }
-
- @Test()
- public void testSigningKeyOpt() throws DescriptorParseException {
- DescriptorBuilder.createWithSigningKeyLines("opt signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
- + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
- + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----");
- }
-
- @Test()
- public void testHiddenServiceDirMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine(null);
- assertNull(descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testHiddenServiceDirNoOpt()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine("hidden-service-dir");
- assertEquals(Arrays.asList(new Integer[] {2}),
- descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testHiddenServiceDirVersions2And3()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine("hidden-service-dir 2 3");
- assertEquals(Arrays.asList(new Integer[] {2, 3}),
- descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testContactMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine(null);
- assertNull(descriptor.getContact());
- }
-
- @Test()
- public void testContactOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("opt contact Random Person");
- assertEquals("Random Person", descriptor.getContact());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testContactDuplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithContactLine("contact Random "
- + "Person\ncontact Random Person");
- }
-
- @Test()
- public void testContactNoSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("contact");
- assertEquals("", descriptor.getContact());
- }
-
- @Test()
- public void testContactCarriageReturn()
- throws DescriptorParseException {
- String contactString = "Random "
- + "Person -----BEGIN PGP PUBLIC KEY BLOCK-----\r"
- + "Version: GnuPG v1 dot 4 dot 7 (Darwin)\r\r"
- + "mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd"
- + "07\rmTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESm"
- + "oGEdAX\roid3XuIYRpRnqoafbFg9sg+OofX/mGrO+5ACfagQ9rlfx2oxCWijYw"
- + "pYFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("contact " + contactString);
- assertEquals(contactString, descriptor.getContact());
- }
-
- @Test()
- public void testExitPolicyRejectAllAcceptAll()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("reject *:*\naccept *:*");
- assertEquals(Arrays.asList(new String[] {"reject *:*", "accept *:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test()
- public void testExitPolicyOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("opt reject *:*");
- assertEquals(Arrays.asList(new String[] {"reject *:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyNoPort() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject *");
- }
-
- @Test()
- public void testExitPolicyAccept80RejectAll()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("accept *:80\nreject *:*");
- assertEquals(Arrays.asList(new String[] {"accept *:80",
- "reject *:*"}), descriptor.getExitPolicyLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyReject321() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject "
- + "123.123.123.321:80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyRejectPort66666()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject *:66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyProjectAll() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("project *:*");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines(null);
- }
-
- @Test()
- public void testExitPolicyMaskTypes() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("reject 192.168.0.0/16:*\n"
- + "reject 94.134.192.243/255.255.255.0:*");
- assertEquals(Arrays.asList(new String[] { "reject 192.168.0.0/16:*",
- "reject 94.134.192.243/255.255.255.0:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test()
- public void testRouterSignatureOpt()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("opt "
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "crypto lines are ignored anyway\n"
- + "-----END SIGNATURE-----");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterSignatureNotLastLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----\ncontact me");
- }
-
- @Test()
- public void testHibernatingOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("opt hibernating 1");
- assertTrue(descriptor.isHibernating());
- }
-
- @Test()
- public void testHibernatingFalse() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("hibernating 0");
- assertFalse(descriptor.isHibernating());
- }
-
- @Test()
- public void testHibernatingTrue() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("hibernating 1");
- assertTrue(descriptor.isHibernating());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHibernatingYep() throws DescriptorParseException {
- DescriptorBuilder.createWithHibernatingLine("hibernating yep");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHibernatingNoSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithHibernatingLine("hibernating");
- }
-
- @Test()
- public void testFamilyOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("opt family saberrider2008");
- assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyFingerprint() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- assertEquals(Arrays.asList(new String[] {
- "$D8733048FC8EC9102466AD8F3098622BF1BF71FD"}),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyNickname() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family saberrider2008");
- assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
- descriptor.getFamilyEntries());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyDuplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family "
- + "saberrider2008\nfamily saberrider2008");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyNicknamePrefix() throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family $saberrider2008");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyFingerprintNoPrefix()
- throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family "
- + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- }
-
- @Test()
- public void testFamilyFingerprintNicknameNamed()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008");
- assertEquals(Arrays.asList(new String[]
- { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008" }),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyFingerprintNicknameUnnamed()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008");
- assertEquals(Arrays.asList(new String[]
- { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008" }),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testWriteHistory() throws DescriptorParseException {
- String writeHistoryLine = "write-history 2012-01-01 03:51:44 (900 s) "
- + "4345856,261120,7591936,1748992";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine(writeHistoryLine);
- assertNotNull(descriptor.getWriteHistory());
- BandwidthHistory parsedWriteHistory = descriptor.getWriteHistory();
- assertEquals(writeHistoryLine, parsedWriteHistory.getLine());
- assertEquals(1325389904000L, (long) parsedWriteHistory.
- getHistoryEndMillis());
- assertEquals(900L, (long) parsedWriteHistory.getIntervalLength());
- SortedMap<Long, Long> bandwidthValues = parsedWriteHistory.
- getBandwidthValues();
- assertEquals(4345856L, (long) bandwidthValues.remove(1325387204000L));
- assertEquals(261120L, (long) bandwidthValues.remove(1325388104000L));
- assertEquals(7591936L, (long) bandwidthValues.remove(1325389004000L));
- assertEquals(1748992L, (long) bandwidthValues.remove(1325389904000L));
- assertTrue(bandwidthValues.isEmpty());
- }
-
- @Test()
- public void testWriteHistoryOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("opt write-history 2012-01-01 "
- + "03:51:44 (900 s) 4345856,261120,7591936,1748992");
- assertNotNull(descriptor.getWriteHistory());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistory3012() throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "3012-01-01 03:51:44 (900 s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoSeconds()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51 (900 s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoParathenses()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 900 s 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoSpaceSeconds()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryTrailingComma()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) 4345856,261120,7591936,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryOneTwoThree()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) one,two,three");
- }
-
- @Test()
- public void testWriteHistoryNoValuesSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(900 s) ");
- assertEquals(900, (long) descriptor.getWriteHistory().
- getIntervalLength());
- assertTrue(descriptor.getWriteHistory().getBandwidthValues().
- isEmpty());
- }
-
- @Test()
- public void testWriteHistoryNoValuesNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(900 s)");
- assertEquals(900, (long) descriptor.getWriteHistory().
- getIntervalLength());
- assertTrue(descriptor.getWriteHistory().getBandwidthValues().
- isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoS() throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine(
- "write-history 2012-01-01 03:51:44 (900 ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryTrailingNumber()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) 4345856 1");
- }
-
- @Test()
- public void testWriteHistory1800Seconds()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(1800 s) 4345856");
- assertEquals(1800L, (long) descriptor.getWriteHistory().
- getIntervalLength());
- }
-
- @Test()
- public void testReadHistory() throws DescriptorParseException {
- String readHistoryLine = "read-history 2012-01-01 03:51:44 (900 s) "
- + "4268032,139264,7797760,1415168";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithReadHistoryLine(readHistoryLine);
- assertNotNull(descriptor.getReadHistory());
- BandwidthHistory parsedReadHistory = descriptor.getReadHistory();
- assertEquals(readHistoryLine, parsedReadHistory.getLine());
- assertEquals(1325389904000L, (long) parsedReadHistory.
- getHistoryEndMillis());
- assertEquals(900L, (long) parsedReadHistory.getIntervalLength());
- SortedMap<Long, Long> bandwidthValues = parsedReadHistory.
- getBandwidthValues();
- assertEquals(4268032L, (long) bandwidthValues.remove(1325387204000L));
- assertEquals(139264L, (long) bandwidthValues.remove(1325388104000L));
- assertEquals(7797760L, (long) bandwidthValues.remove(1325389004000L));
- assertEquals(1415168L, (long) bandwidthValues.remove(1325389904000L));
- assertTrue(bandwidthValues.isEmpty());
- }
-
- @Test()
- public void testReadHistoryTwoSpaces() throws DescriptorParseException {
- /* There are some server descriptors from older Tor versions that
- * contain "opt read-history " lines. */
- String readHistoryLine = "opt read-history 2012-01-01 03:51:44 "
- + "(900 s) 4268032,139264,7797760,1415168";
- DescriptorBuilder.createWithReadHistoryLine(readHistoryLine);
- }
-
- @Test()
- public void testEventdnsOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("opt eventdns 1");
- assertTrue(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test()
- public void testEventdns1() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("eventdns 1");
- assertTrue(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test()
- public void testEventdns0() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("eventdns 0");
- assertFalse(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEventdnsTrue() throws DescriptorParseException {
- DescriptorBuilder.createWithEventdnsLine("eventdns true");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEventdnsNo() throws DescriptorParseException {
- DescriptorBuilder.createWithEventdnsLine("eventdns no");
- }
-
- @Test()
- public void testCachesExtraInfoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithCachesExtraInfoLine("opt caches-extra-info");
- assertTrue(descriptor.getCachesExtraInfo());
- }
-
- @Test()
- public void testCachesExtraInfoNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithCachesExtraInfoLine("caches-extra-info");
- assertTrue(descriptor.getCachesExtraInfo());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCachesExtraInfoTrue() throws DescriptorParseException {
- DescriptorBuilder.createWithCachesExtraInfoLine("caches-extra-info "
- + "true");
- }
-
- @Test()
- public void testAllowSingleHopExitsOpt()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithAllowSingleHopExitsLine("opt allow-single-hop-exits");
- assertTrue(descriptor.getAllowSingleHopExits());
- }
-
- @Test()
- public void testAllowSingleHopExitsNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithAllowSingleHopExitsLine("allow-single-hop-exits");
- assertTrue(descriptor.getAllowSingleHopExits());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAllowSingleHopExitsTrue()
- throws DescriptorParseException {
- DescriptorBuilder.createWithAllowSingleHopExitsLine(
- "allow-single-hop-exits true");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAllowSingleHopExitsNonAsciiKeyword()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
- 0x14, (byte) 0xfe, 0x18, // non-ascii chars
- 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2d, // "allow-"
- 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2d, // "single-"
- 0x68, 0x6f, 0x70, 0x2d, // "hop-"
- 0x65, 0x78, 0x69, 0x74, 0x73 }, // "exits" (no newline)
- false);
- }
-
- @Test()
- public void testIpv6PolicyLine() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithIpv6PolicyLine("ipv6-policy accept 80,1194,1220,1293");
- assertEquals("accept", descriptor.getIpv6DefaultPolicy());
- assertEquals("80,1194,1220,1293", descriptor.getIpv6PortList());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPolicy()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPorts()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy accept");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPolicyNoPorts()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineProject()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy project 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoIpv6PolicyLines() throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine(
- "ipv6-policy accept 80,1194,1220,1293\n"
- + "ipv6-policy accept 80,1194,1220,1293");
- }
-
- @Test()
- public void testNtorOnionKeyLine() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY=");
- assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
- descriptor.getNtorOnionKey());
- }
-
- @Test()
- public void testNtorOnionKeyLineNoPadding()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
- assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
- descriptor.getNtorOnionKey());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyLineNoKey()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyLineTwoKeys()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoNtorOnionKeyLines() throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\nntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\n");
- }
-
- @Test()
- public void testTunnelledDirServerTrue()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder
- .createWithTunnelledDirServerLine("tunnelled-dir-server");
- assertTrue(descriptor.getTunnelledDirServer());
- }
-
- @Test()
- public void testTunnelledDirServerFalse()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder
- .createWithTunnelledDirServerLine(null);
- assertFalse(descriptor.getTunnelledDirServer());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerTypo()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunneled-dir-server");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerTwice()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunnelled-dir-server\ntunnelled-dir-server");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerArgs()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunnelled-dir-server 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- @Test()
- public void testSomeOtherKey() throws DescriptorParseException {
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add("some-other-key");
- unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
- unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
- + "1U4V9SeiKooSo5BpPL");
- unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
- + "IynCI4QryfCEuC3cTF");
- unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
- + "facOkpAgMBAAE=");
- unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
- StringBuilder sb = new StringBuilder();
- for (String line : unrecognizedLines) {
- sb.append("\n").append(line);
- }
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(sb.toString().substring(1), false);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- @Test()
- public void testUnrecognizedCryptoBlockNoKeyword()
- throws DescriptorParseException {
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
- unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
- + "1U4V9SeiKooSo5BpPL");
- unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
- + "IynCI4QryfCEuC3cTF");
- unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
- + "facOkpAgMBAAE=");
- unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
- StringBuilder sb = new StringBuilder();
- for (String line : unrecognizedLines) {
- sb.append("\n").append(line);
- }
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(sb.toString().substring(1), false);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- private static final String IDENTITY_ED25519_LINES =
- "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
- + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
- + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
- + "\n"
- + "-----END ED25519 CERT-----";
-
- private static final String MASTER_KEY_ED25519_LINE =
- "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
-
- private static final String ROUTER_SIG_ED25519_LINE =
- "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
- + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
-
- @Test()
- public void testEd25519() throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- assertEquals(IDENTITY_ED25519_LINES.substring(
- IDENTITY_ED25519_LINES.indexOf("\n") + 1),
- descriptor.getIdentityEd25519());
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- assertEquals(ROUTER_SIG_ED25519_LINE.substring(
- ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getRouterSignatureEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityMasterKeyMismatch()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519IdentityMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(null,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
- + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityEmptyCrypto()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519MasterKeyMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- null, ROUTER_SIG_ED25519_LINE);
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519MasterKeyDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519RouterSigMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519RouterSigDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
- + ROUTER_SIG_ED25519_LINE);
- }
-
- private static final String ONION_KEY_CROSSCERT_LINES =
- "onion-key-crosscert\n"
- + "-----BEGIN CROSSCERT-----\n"
- + "gVWpiNgG2FekW1uonr4KKoqykjr4bqUBKGZfu6s9rvsV1TThnquZNP6ZhX2IPdQA"
- + "\nlfKtzFggGu/4BiJ5oTSDj2sK2DMjY3rjrMQZ3I/wJ25yhc9gxjqYqUYO9MmJwA"
- + "Lp\nfYkqp/t4WchJpyva/4hK8vITsI6eT2BfY/DWMy/suIE=\n"
- + "-----END CROSSCERT-----";
-
- private static final String NTOR_ONION_KEY_CROSSCERT_LINES =
- "ntor-onion-key-crosscert 1\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQoABiUeAdauu1MxYGMmGLTCPaoes0RvW7udeLc1t8LZ4P3CDo5bAN4nrRfbCfOt"
- + "\nz2Nwqn8tER1a+Ry6Vs+ilMZA55Rag4+f6Zdb1fmHWknCxbQlLHpqHACMtemPda"
- + "Ka\nErPtMuiEqAc=\n"
- + "-----END ED25519 CERT-----";
-
- @Test()
- public void testOnionKeyCrosscert() throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- ONION_KEY_CROSSCERT_LINES);
- assertEquals(ONION_KEY_CROSSCERT_LINES.substring(
- ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
- descriptor.getOnionKeyCrosscert());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOnionKeyCrosscertDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- ONION_KEY_CROSSCERT_LINES + "\n" + ONION_KEY_CROSSCERT_LINES);
- }
-
- @Test()
- public void testNtorOnionKeyCrosscert()
- throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithNtorOnionKeyCrosscertLines(
- NTOR_ONION_KEY_CROSSCERT_LINES);
- assertEquals(NTOR_ONION_KEY_CROSSCERT_LINES.substring(
- NTOR_ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
- descriptor.getNtorOnionKeyCrosscert());
- assertEquals(1, descriptor.getNtorOnionKeyCrosscertSign());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyCrosscertDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- NTOR_ONION_KEY_CROSSCERT_LINES + "\n"
- + NTOR_ONION_KEY_CROSSCERT_LINES);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/TorperfResultImplTest.java b/test/org/torproject/descriptor/impl/TorperfResultImplTest.java
deleted file mode 100644
index b5cde0a..0000000
--- a/test/org/torproject/descriptor/impl/TorperfResultImplTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.Descriptor;
-
-public class TorperfResultImplTest {
-
- @Test()
- public void testAnnotatedInput() throws Exception{
- TorperfResultImpl result = (TorperfResultImpl)
- (TorperfResultImpl.parseTorperfResults((torperfAnnotation + input)
- .getBytes("US-ASCII"), false).get(0));
- assertEquals("Expected one annotation.", 1,
- result.getAnnotations().size());
- assertEquals(torperfAnnotation.substring(0, 17),
- result.getAnnotations().get(0));
- int count = 0;
- for (Long l: result.getDataPercentiles().values()) {
- assertNotNull(l);
- assertEquals(l.longValue(), deciles[count++]);
- }
- }
-
- @Test()
- public void testPartiallyAnnotatedInput() throws Exception{
- byte[] asciiBytes = (torperfAnnotation
- + input + input + input).getBytes("US-ASCII");
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- asciiBytes, false);
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
- assertEquals(3, result.size());
- assertEquals("Expected zero annotations.", 0,
- ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
- assertEquals("Expected zero annotations.", 0,
- ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
- }
-
- @Test()
- public void testAllAnnotatedInput() throws Exception {
- byte[] asciiBytes = (torperfAnnotation + input
- + torperfAnnotation + input
- + torperfAnnotation + input).getBytes("US-ASCII");
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- asciiBytes, false);
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
- assertEquals(3, result.size());
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
- }
-
- private static long[] deciles = new long[] {
- 1441065602980L, 1441065603030L, 1441065603090L, 1441065603120L,
- 1441065603230L, 1441065603250L, 1441065603310L, 1441065603370L,
- 1441065603370L };
-
- private static final String torperfAnnotation = "@type torperf 1.0\n";
-
- private static final String input =
- "BUILDTIMES=0.872834920883,1.09103679657,1.49180984497 "
- + "CIRC_ID=1228 CONNECT=1441065601.86 DATACOMPLETE=1441065603.39 "
- + "DATAPERC10=1441065602.98 DATAPERC20=1441065603.03 "
- + "DATAPERC30=1441065603.09 DATAPERC40=1441065603.12 "
- + "DATAPERC50=1441065603.23 DATAPERC60=1441065603.25 "
- + "DATAPERC70=1441065603.31 DATAPERC80=1441065603.37 "
- + "DATAPERC90=1441065603.37 DATAREQUEST=1441065602.38 "
- + "DATARESPONSE=1441065602.84 DIDTIMEOUT=0 FILESIZE=51200 "
- + "LAUNCH=1441065361.30 NEGOTIATE=1441065601.86 "
- + "PATH=$C4C9C332D25B3546BEF4E1250CF410E97EF996E6,"
- + "$C43FA6474A9F071E9120DF63ED6EB8FDBA105234,"
- + "$7C0AA4E3B73E407E9F5FEB1912F8BE26D8AA124D QUANTILE=0.800000 "
- + "READBYTES=51416 REQUEST=1441065601.86 RESPONSE=1441065602.38 "
- + "SOCKET=1441065601.86 SOURCE=moria START=1441065601.86 "
- + "TIMEOUT=1500 USED_AT=1441065603.40 USED_BY=2475 WRITEBYTES=75\n";
-
- @Test()
- public void testDatapercNonNumeric() throws Exception {
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- ("DATAPERMILLE=2.0 " + input).getBytes(), false);
- assertEquals(1, result.size());
- TorperfResultImpl torperfResult = (TorperfResultImpl) result.get(0);
- assertEquals(1, torperfResult.getUnrecognizedKeys().size());
- assertEquals("DATAPERMILLE",
- torperfResult.getUnrecognizedKeys().firstKey());
- }
-}
-
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits