[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-lib/master] Implement parsing of network status votes.
commit 276e0ef72aae65f0e69cb55bb4b1d80c537691e9
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Tue Dec 13 10:17:34 2011 +0100
Implement parsing of network status votes.
---
.../descriptor/RelayNetworkStatusConsensus.java | 7 +
.../descriptor/RelayNetworkStatusVote.java | 80 +++++-
.../descriptor/impl/NetworkStatusEntryImpl.java | 7 +-
.../impl/RelayNetworkStatusConsensusImpl.java | 6 +
.../impl/RelayNetworkStatusVoteImpl.java | 315 +++++++++++++++++++-
5 files changed, 408 insertions(+), 7 deletions(-)
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
index 192c963..452a0ba 100755
--- a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -46,6 +46,13 @@ public interface RelayNetworkStatusConsensus extends Descriptor {
/* Return status entries, one for each contained relay. */
public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+ /* Return whether a status entry with the given fingerprint exists. */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /* Return a status entry by fingerprint or null if no such status entry
+ * exists. */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
/* Return directory signatures. */
public SortedMap<String, String> getDirectorySignatures();
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
index 62d1307..7d85a0c 100755
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -2,11 +2,87 @@
* See LICENSE for licensing information */
package org.torproject.descriptor;
+import java.util.List;
import java.util.SortedMap;
import java.util.SortedSet;
-/* Contains the unparsed string and parsed fields from a network status
- * vote. */
+/* Contains a network status vote. */
public interface RelayNetworkStatusVote extends Descriptor {
+
+ /* Return the network status version. */
+ public int getNetworkStatusVersion();
+
+ /* Return the consensus method. */
+ public List<Integer> getConsensusMethods();
+
+ /* Return the publication time in milliseconds. */
+ public long getPublishedMillis();
+
+ /* Return the valid-after time in milliseconds. */
+ public long getValidAfterMillis();
+
+ /* Return the fresh-until time in milliseconds. */
+ public long getFreshUntilMillis();
+
+ /* Return the valid-until time in milliseconds. */
+ public long getValidUntilMillis();
+
+ /* Return a list of the voting-delay times in seconds. */
+ public List<Long> getVotingDelay();
+
+ /* Return cecommended server versions. */
+ public SortedSet<String> getRecommendedServerVersions();
+
+ /* Return recommended client versions. */
+ public SortedSet<String> getRecommendedClientVersions();
+
+ /* Return known relay flags. */
+ public SortedSet<String> getKnownFlags();
+
+ /* Return consensus parameters. */
+ public SortedMap<String, String> getConsensusParams();
+
+ /* Return the directory nickname. */
+ public String getNickname();
+
+ /* Return the directory identity. */
+ public String getIdentity();
+
+ /* Return the IP address. */
+ public String getAddress();
+
+ /* Return the DiRPort. */
+ public int getDirport();
+
+ /* Return the ORPort. */
+ public int getOrport();
+
+ /* Return the contact line. */
+ public String getContactLine();
+
+ /* Return the directory key certificate version. */
+ public int getDirKeyCertificateVersion();
+
+ /* Return the directory key publication timestamp. */
+ public long getDirKeyPublishedMillis();
+
+ /* Return the directory key expiration timestamp. */
+ public long getDirKeyExpiresMillis();
+
+ /* Return the signing key digest. */
+ public String getSigningKeyDigest();
+
+ /* Return status entries, one for each contained relay. */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /* Return whether a status entry with the given fingerprint exists. */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /* Return a status entry by fingerprint or null if no such status entry
+ * exists. */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /* Return directory signatures. */
+ public SortedMap<String, String> getDirectorySignatures();
}
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index d0a511d..610fb1a 100755
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -57,12 +57,15 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
} else if (line.startsWith("s ")) {
this.flags.addAll(Arrays.asList(line.substring("s ".length()).
split(" ")));
- } else if (line.startsWith("v ")) {
- this.version = line.substring("v ".length());
+ } else if (line.startsWith("v ") || line.startsWith("opt v")) {
+ this.version = line.substring(
+ line.startsWith("v ") ? "v ".length() : "opt v".length());
} else if (line.startsWith("w ")) {
this.bandwidth = line.substring("w ".length());
} else if (line.startsWith("p ")) {
this.ports = line.substring(2);
+ } else if (line.startsWith("m ")) {
+ /* TODO Parse m lines in votes. */
} else {
throw new RuntimeException("Unknown line '" + line + "' in "
+ "status entry.");
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index a9579a6..3d2b0a2 100755
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -291,6 +291,12 @@ public class RelayNetworkStatusConsensusImpl
public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
return new TreeMap<String, NetworkStatusEntry>(this.statusEntries);
}
+ public boolean containsStatusEntry(String fingerprint) {
+ return this.statusEntries.containsKey(fingerprint);
+ }
+ public NetworkStatusEntry getStatusEntry(String fingerprint) {
+ return this.statusEntries.get(fingerprint);
+ }
private SortedMap<String, String> directorySignatures =
new TreeMap<String, String>();
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index 84a81e2..fa5b4d5 100755
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -2,14 +2,26 @@
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.NetworkStatusEntry;
import org.torproject.descriptor.RelayNetworkStatusVote;
/* Contains a network status vote. */
-/* TODO This class doesn't contain any parsing code yet, and it would be
- * sharing a lot of that code with the consensus class. Should there be
- * an abstract super class for the two? */
+/* TODO This class is sharing a lot of parsing code with the consensus
+ * class. Should there be an abstract super class for the two? */
public class RelayNetworkStatusVoteImpl
implements RelayNetworkStatusVote {
@@ -40,11 +52,308 @@ public class RelayNetworkStatusVoteImpl
protected RelayNetworkStatusVoteImpl(byte[] voteBytes) {
this.voteBytes = voteBytes;
+ this.parseVoteBytes();
+ this.checkConsistency();
+ /* TODO Find a way to handle parse and consistency-check problems. */
+ }
+
+ private void parseVoteBytes() {
+ String line = null;
+ try {
+ BufferedReader br = new BufferedReader(new StringReader(
+ new String(this.voteBytes)));
+ SimpleDateFormat dateTimeFormat = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss");
+ dateTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ StringBuilder dirSourceEntryLines = null, statusEntryLines = null;
+ boolean skipCrypto = false;
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("network-status-version ")) {
+ this.networkStatusVersion = Integer.parseInt(line.substring(
+ "network-status-version ".length()));
+ } else if (line.startsWith("vote-status ")) {
+ if (!line.equals("vote-status vote")) {
+ throw new RuntimeException("Line '" + line + "' indicates "
+ + "that this string is not a vote. Aborting parsing.");
+ }
+ } else if (line.startsWith("consensus-methods ")) {
+ for (String consensusMethodString : line.substring(
+ "consensus-methods ".length()).split(" ")) {
+ this.consensusMethods.add(Integer.parseInt(
+ consensusMethodString));
+ }
+ } else if (line.startsWith("published ")) {
+ this.publishedMillis = dateTimeFormat.parse(
+ line.substring("published ".length())).getTime();
+ } else if (line.startsWith("valid-after ")) {
+ this.validAfterMillis = dateTimeFormat.parse(
+ line.substring("valid-after ".length())).getTime();
+ } else if (line.startsWith("fresh-until ")) {
+ this.freshUntilMillis = dateTimeFormat.parse(
+ line.substring("fresh-until ".length())).getTime();
+ } else if (line.startsWith("valid-until ")) {
+ this.validUntilMillis = dateTimeFormat.parse(
+ line.substring("valid-until ".length())).getTime();
+ } else if (line.startsWith("voting-delay ")) {
+ for (String votingDelayString : line.substring(
+ "voting-delay ".length()).split(" ")) {
+ this.votingDelay.add(Long.parseLong(votingDelayString));
+ }
+ } else if (line.startsWith("client-versions ")) {
+ this.recommendedClientVersions.addAll(
+ Arrays.asList(line.split(" ")[1].split(",")));
+ } else if (line.startsWith("server-versions ")) {
+ this.recommendedServerVersions.addAll(
+ Arrays.asList(line.split(" ")[1].split(",")));
+ } else if (line.startsWith("known-flags ")) {
+ for (String flag : line.substring("known-flags ".length()).
+ split(" ")) {
+ this.knownFlags.add(flag);
+ }
+ } else if (line.startsWith("params ")) {
+ if (line.length() > "params ".length()) {
+ for (String param :
+ line.substring("params ".length()).split(" ")) {
+ String paramName = param.split("=")[0];
+ String paramValue = param.split("=")[1];
+ this.consensusParams.put(paramName, paramValue);
+ }
+ }
+ } else if (line.startsWith("dir-source ")) {
+ String[] parts = line.split(" ");
+ this.nickname = parts[1];
+ this.identity = parts[2];
+ this.address = parts[4];
+ this.dirPort = Integer.parseInt(parts[5]);
+ this.orPort = Integer.parseInt(parts[6]);
+ /* TODO Add code for parsing legacy dir sources. */
+ } else if (line.startsWith("contact ")) {
+ this.contactLine = line.substring("contact ".length());
+ } else if (line.startsWith("dir-key-certificate-version ")) {
+ this.dirKeyCertificateVersion = Integer.parseInt(line.substring(
+ "dir-key-certificate-version ".length()));
+ } else if (line.startsWith("fingerprint ")) {
+ /* Nothing new to learn here. We already know the fingerprint
+ * from the dir-source line. */
+ } else if (line.startsWith("dir-key-published ")) {
+ this.dirKeyPublishedMillis = dateTimeFormat.parse(
+ line.substring("dir-key-published ".length())).getTime();
+ } else if (line.startsWith("dir-key-expires ")) {
+ this.dirKeyExpiresMillis = dateTimeFormat.parse(
+ line.substring("dir-key-expires ".length())).getTime();
+ } else if (line.equals("dir-identity-key") ||
+ line.equals("dir-signing-key") ||
+ line.equals("dir-key-crosscert") ||
+ line.equals("dir-key-certification")) {
+ /* Ignore crypto parts for now. */
+ } else if (line.startsWith("r ") ||
+ line.equals("directory-footer")) {
+ if (statusEntryLines != null) {
+ NetworkStatusEntryImpl statusEntry =
+ new NetworkStatusEntryImpl(
+ statusEntryLines.toString().getBytes());
+ this.statusEntries.put(statusEntry.getFingerprint(),
+ statusEntry);
+ statusEntryLines = null;
+ }
+ if (line.startsWith("r ")) {
+ statusEntryLines = new StringBuilder();
+ statusEntryLines.append(line + "\n");
+ }
+ } else if (line.startsWith("s ") || line.equals("s") ||
+ line.startsWith("opt v ") || line.startsWith("w ") ||
+ line.startsWith("p ") || line.startsWith("m ")) {
+ statusEntryLines.append(line + "\n");
+ } else if (line.startsWith("directory-signature ")) {
+ String[] parts = line.split(" ");
+ String identity = parts[1];
+ String signingKeyDigest = parts[2];
+ this.directorySignatures.put(identity, signingKeyDigest);
+ } else if (line.startsWith("-----BEGIN")) {
+ skipCrypto = true;
+ } else if (line.startsWith("-----END")) {
+ skipCrypto = false;
+ } else if (!skipCrypto) {
+ throw new RuntimeException("Unrecognized line '" + line + "'.");
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Internal error: Ran into an "
+ + "IOException while parsing a String in memory. Something's "
+ + "really wrong.", e);
+ } catch (ParseException e) {
+ /* TODO Handle me correctly. */
+ throw new RuntimeException("Parse error in line '" + line + "'.");
+ } catch (NumberFormatException e) {
+ /* TODO Handle me. In theory, we shouldn't catch runtime
+ * exceptions, but in this case it keeps the parsing code small. */
+ } catch (ArrayIndexOutOfBoundsException e) {
+ /* TODO Handle me. In theory, we shouldn't catch runtime
+ * exceptions, but in this case it keeps the parsing code small. */
+ }
}
private byte[] voteBytes;
public byte[] getRawDescriptorBytes() {
return this.voteBytes;
}
+
+ private int networkStatusVersion;
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private List<Integer> consensusMethods = new ArrayList<Integer>();
+ public List<Integer> getConsensusMethods() {
+ return this.consensusMethods;
+ }
+
+ private long publishedMillis;
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long validAfterMillis;
+ public long getValidAfterMillis() {
+ return this.validAfterMillis;
+ }
+
+ private long freshUntilMillis;
+ public long getFreshUntilMillis() {
+ return this.freshUntilMillis;
+ }
+
+ private long validUntilMillis;
+ public long getValidUntilMillis() {
+ return this.validUntilMillis;
+ }
+
+ private List<Long> votingDelay = new ArrayList<Long>();
+ public List<Long> getVotingDelay() {
+ return new ArrayList<Long>(this.votingDelay);
+ }
+
+ private SortedSet<String> recommendedClientVersions =
+ new TreeSet<String>();
+ public SortedSet<String> getRecommendedClientVersions() {
+ return new TreeSet<String>(this.recommendedClientVersions);
+ }
+
+ private SortedSet<String> recommendedServerVersions =
+ new TreeSet<String>();
+ public SortedSet<String> getRecommendedServerVersions() {
+ return new TreeSet<String>(this.recommendedServerVersions);
+ }
+
+ private SortedSet<String> knownFlags = new TreeSet<String>();
+ public SortedSet<String> getKnownFlags() {
+ return new TreeSet<String>(this.knownFlags);
+ }
+
+ private SortedMap<String, String> consensusParams =
+ new TreeMap<String, String>();
+ public SortedMap<String, String> getConsensusParams() {
+ return new TreeMap<String, String>(this.consensusParams);
+ }
+
+ private String nickname;
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String identity;
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private String address;
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int dirPort;
+ public int getDirport() {
+ return this.dirPort;
+ }
+
+ private int orPort;
+ public int getOrport() {
+ return this.orPort;
+ }
+
+ private String contactLine;
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private int dirKeyCertificateVersion;
+ public int getDirKeyCertificateVersion() {
+ return this.dirKeyCertificateVersion;
+ }
+
+ private long dirKeyPublishedMillis;
+ public long getDirKeyPublishedMillis() {
+ return this.dirKeyPublishedMillis;
+ }
+
+ private long dirKeyExpiresMillis;
+ public long getDirKeyExpiresMillis() {
+ return this.dirKeyExpiresMillis;
+ }
+
+ private String signingKeyDigest;
+ public String getSigningKeyDigest() {
+ return this.signingKeyDigest;
+ }
+
+ private SortedMap<String, NetworkStatusEntry> statusEntries =
+ new TreeMap<String, NetworkStatusEntry>();
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
+ return new TreeMap<String, NetworkStatusEntry>(this.statusEntries);
+ }
+ public boolean containsStatusEntry(String fingerprint) {
+ return this.statusEntries.containsKey(fingerprint);
+ }
+ public NetworkStatusEntry getStatusEntry(String fingerprint) {
+ return this.statusEntries.get(fingerprint);
+ }
+
+
+ private SortedMap<String, String> directorySignatures =
+ new TreeMap<String, String>();
+ public SortedMap<String, String> getDirectorySignatures() {
+ return new TreeMap<String, String>(this.directorySignatures);
+ }
+
+ private void checkConsistency() {
+ if (this.networkStatusVersion == 0) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'network-status-version' line.");
+ }
+ if (this.validAfterMillis == 0L) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'valid-after' line.");
+ }
+ if (this.freshUntilMillis == 0L) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'fresh-until' line.");
+ }
+ if (this.validUntilMillis == 0L) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'valid-until' line.");
+ }
+ if (this.votingDelay.isEmpty()) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'voting-delay' line.");
+ }
+ if (this.knownFlags.isEmpty()) {
+ throw new RuntimeException("Consensus doesn't contain a "
+ + "'known-flags' line.");
+ }
+ if (this.statusEntries.isEmpty()) {
+ throw new RuntimeException("Consensus doesn't contain any 'r' "
+ + "lines.");
+ }
+ }
}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits