[tor-commits] [metrics-lib/master] Parse micodesc consensuses and microdescriptors.
karsten at torproject.org
karsten at torproject.org
Wed Jan 22 08:05:46 UTC 2014
commit 38c48ddd0c49978bbfa5e0a987cfd3a890692a5c
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Thu Jan 9 14:24:03 2014 +0100
Parse micodesc consensuses and microdescriptors.
Required for implementing #2785.
---
.../torproject/descriptor/DirectorySignature.java | 3 +
src/org/torproject/descriptor/Microdescriptor.java | 47 ++++
.../torproject/descriptor/NetworkStatusEntry.java | 11 +-
.../descriptor/RelayNetworkStatusConsensus.java | 5 +-
.../torproject/descriptor/impl/DescriptorImpl.java | 12 +-
.../descriptor/impl/DirectorySignatureImpl.java | 15 +-
.../descriptor/impl/MicrodescriptorImpl.java | 261 ++++++++++++++++++++
.../descriptor/impl/NetworkStatusEntryImpl.java | 51 +++-
.../descriptor/impl/NetworkStatusImpl.java | 8 +-
.../torproject/descriptor/impl/ParseHelper.java | 17 +-
.../impl/RelayNetworkStatusConsensusImpl.java | 37 ++-
.../descriptor/impl/ServerDescriptorImpl.java | 2 +-
12 files changed, 440 insertions(+), 29 deletions(-)
diff --git a/src/org/torproject/descriptor/DirectorySignature.java b/src/org/torproject/descriptor/DirectorySignature.java
index 29eb055..012b770 100644
--- a/src/org/torproject/descriptor/DirectorySignature.java
+++ b/src/org/torproject/descriptor/DirectorySignature.java
@@ -4,6 +4,9 @@ package org.torproject.descriptor;
public interface DirectorySignature {
+ /* Return the digest algorithm, which is "sha1" by default. */
+ public String getAlgorithm();
+
/* Return the directory identity fingerprint. */
public String getIdentity();
diff --git a/src/org/torproject/descriptor/Microdescriptor.java b/src/org/torproject/descriptor/Microdescriptor.java
new file mode 100644
index 0000000..f106f83
--- /dev/null
+++ b/src/org/torproject/descriptor/Microdescriptor.java
@@ -0,0 +1,47 @@
+/* Copyright 2014 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/* Contains a relay microdescriptor. */
+public interface Microdescriptor extends Descriptor {
+
+ /* Return the descriptor digest that is used to reference this
+ * microdescriptor in a network status. */
+ public String getMicrodescriptorDigest();
+
+ /* Return the onion key in PEM format. */
+ public String getOnionKey();
+
+ /* Return the ntor onion key base64 string with padding omitted, or null
+ * if the microdescriptor didn't contain an ntor onion key line. */
+ public String getNtorOnionKey();
+
+ /* Return the relay's additional OR addresses and ports contained in
+ * or-address lines, or an empty list if the microdescriptor doesn't
+ * contain such lines. */
+ public List<String> getOrAddresses();
+
+ /* Return nicknames, ($-prefixed) fingerprints, $fingerprint=nickname,
+ * or $fingerprint~nickname tuples contained in the family line of this
+ * relay, or null if the descriptor does not contain a family line. */
+ public List<String> getFamilyEntries();
+
+ /* Return the default policy of the port summary or null if the
+ * microdescriptor didn't contain a port summary line. */
+ public String getDefaultPolicy();
+
+ /* Return the port list of the port summary or null if the
+ * microdescriptor didn't contain a port summary line. */
+ public String getPortList();
+
+ /* Return the default policy of the IPv6 port summary or null if the
+ * microdescriptor didn't contain an IPv6 port summary line. */
+ public String getIpv6DefaultPolicy();
+
+ /* Return the port list of the IPv6 port summary or null if the
+ * microdescriptor didn't contain an IPv6 port summary line. */
+ public String getIpv6PortList();
+}
+
diff --git a/src/org/torproject/descriptor/NetworkStatusEntry.java b/src/org/torproject/descriptor/NetworkStatusEntry.java
index 2f0bb89..cb15ce2 100644
--- a/src/org/torproject/descriptor/NetworkStatusEntry.java
+++ b/src/org/torproject/descriptor/NetworkStatusEntry.java
@@ -1,8 +1,9 @@
-/* Copyright 2011, 2012 The Tor Project
+/* Copyright 2011--2014 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor;
import java.util.List;
+import java.util.Set;
import java.util.SortedSet;
/* Status entry contained in a network status with version 2 or higher or
@@ -18,7 +19,8 @@ public interface NetworkStatusEntry {
/* Return the relay fingerprint. */
public String getFingerprint();
- /* Return the descriptor identity. */
+ /* Return the descriptor identity or null if the containing status is a
+ * microdesc consensus. */
public String getDescriptor();
/* Return the publication timestamp. */
@@ -33,6 +35,11 @@ public interface NetworkStatusEntry {
/* Return the DirPort. */
public int getDirPort();
+ /* Return the (possibly empty) set of microdescriptor digest(s) if the
+ * containing status is a vote or microdesc consensus, or null
+ * otherwise. */
+ public Set<String> getMicrodescriptorDigests();
+
/* Return the relay's additional OR addresses and ports contained in
* or-address lines, or an empty list if the network status doesn't
* contain such lines. */
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
index 4a1634f..53eaf6b 100644
--- a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -1,4 +1,4 @@
-/* Copyright 2011, 2012 The Tor Project
+/* Copyright 2011--2014 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor;
@@ -12,6 +12,9 @@ public interface RelayNetworkStatusConsensus extends Descriptor {
/* Return the network status version. */
public int getNetworkStatusVersion();
+ /* Return the flavor name, or null if this consensus is unflavored. */
+ public String getConsensusFlavor();
+
/* Return the consensus method. */
public int getConsensusMethod();
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
index b1e4408..770ee4f 100644
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -27,8 +27,10 @@ public abstract class DescriptorImpl implements Descriptor {
first100Chars.length);
String firstLines = new String(first100Chars);
if (firstLines.startsWith("@type network-status-consensus-3 1.") ||
- ((firstLines.startsWith("network-status-version 3\n") ||
- firstLines.contains("\nnetwork-status-version 3\n")) &&
+ 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,
@@ -58,6 +60,12 @@ public abstract class DescriptorImpl implements Descriptor {
parsedDescriptors.addAll(ExtraInfoDescriptorImpl.
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 ")) {
diff --git a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
index d205345..d8548ec 100644
--- a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
+++ b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -41,14 +41,18 @@ public class DirectorySignatureImpl implements DirectorySignature {
String line = s.next();
if (line.startsWith("directory-signature ")) {
String[] parts = line.split(" ", -1);
- if (parts.length != 3) {
+ int algorithmOffset = 0;
+ if (parts.length == 4) {
+ this.algorithm = parts[1];
+ algorithmOffset = 1;
+ } else if (parts.length != 3) {
throw new DescriptorParseException("Illegal line '" + line
+ "'.");
}
this.identity = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
+ parts[1 + algorithmOffset]);
this.signingKeyDigest = ParseHelper.parseTwentyByteHexString(
- line, parts[2]);
+ line, parts[2 + algorithmOffset]);
} else if (line.startsWith("-----BEGIN")) {
crypto = new StringBuilder();
crypto.append(line + "\n");
@@ -73,6 +77,11 @@ public class DirectorySignatureImpl implements DirectorySignature {
}
}
+ private String algorithm = "sha1";
+ public String getAlgorithm() {
+ return this.algorithm;
+ }
+
private String identity;
public String getIdentity() {
return this.identity;
diff --git a/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
new file mode 100644
index 0000000..15fb91e
--- /dev/null
+++ b/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -0,0 +1,261 @@
+/* Copyright 2014 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+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 org.apache.commons.codec.digest.DigestUtils;
+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<Microdescriptor>();
+ 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<String>(Arrays.asList(
+ "onion-key".split(",")));
+ this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+ Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
+ "ntor-onion-key,family,p,p6").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("onion-key");
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = null;
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) {
+ continue;
+ }
+ String[] parts = line.split(" ");
+ String keyword = parts[0];
+ if (keyword.equals("onion-key")) {
+ this.parseOnionKeyLine(line, parts);
+ nextCrypto = "onion-key";
+ } else if (keyword.equals("ntor-onion-key")) {
+ this.parseNtorOnionKeyLine(line, parts);
+ } else if (keyword.equals("a")) {
+ this.parseALine(line, parts);
+ } else if (keyword.equals("family")) {
+ this.parseFamilyLine(line, parts);
+ } else if (keyword.equals("p")) {
+ this.parsePLine(line, parts);
+ } else if (keyword.equals("p6")) {
+ this.parseP6Line(line, parts);
+ } else if (line.startsWith("-----BEGIN")) {
+ crypto = new StringBuilder();
+ crypto.append(line + "\n");
+ } else if (line.startsWith("-----END")) {
+ crypto.append(line + "\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 = null;
+ } else if (crypto != null) {
+ crypto.append(line + "\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<String>();
+ }
+ 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 {
+ this.familyEntries = new ArrayList<String>();
+ 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));
+ this.familyEntries.add("$" + fingerprint + separator
+ + nickname);
+ } else {
+ this.familyEntries.add("$"
+ + ParseHelper.parseTwentyByteHexString(line,
+ parts[i].substring(1)));
+ }
+ } else {
+ this.familyEntries.add(ParseHelper.parseNickname(line, parts[i]));
+ }
+ }
+ }
+
+ 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 if (!parts[1].equals("accept") && !parts[1].equals("reject")) {
+ isValid = false;
+ } else {
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ }
+ if (!isValid) {
+ 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 = DigestUtils.sha256Hex(forDigest);
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ }
+ if (this.microdescriptorDigest == null) {
+ throw new DescriptorParseException("Could not calculate "
+ + "microdescriptor digest.");
+ }
+ }
+
+ private String microdescriptorDigest;
+ public String getMicrodescriptorDigest() {
+ return this.microdescriptorDigest;
+ }
+
+ private String onionKey;
+ public String getOnionKey() {
+ return this.onionKey;
+ }
+
+ private String ntorOnionKey;
+ public String getNtorOnionKey() {
+ return this.ntorOnionKey;
+ }
+
+ private List<String> orAddresses = new ArrayList<String>();
+ public List<String> getOrAddresses() {
+ return new ArrayList<String>(this.orAddresses);
+ }
+
+ private List<String> familyEntries;
+ public List<String> getFamilyEntries() {
+ return this.familyEntries == null ? null :
+ new ArrayList<String>(this.familyEntries);
+ }
+ private String defaultPolicy;
+ public String getDefaultPolicy() {
+ return this.defaultPolicy;
+ }
+
+ private String portList;
+ public String getPortList() {
+ return this.portList;
+ }
+
+ private String ipv6DefaultPolicy;
+ public String getIpv6DefaultPolicy() {
+ return this.ipv6DefaultPolicy;
+ }
+
+ private String ipv6PortList;
+ public String getIpv6PortList() {
+ return this.ipv6PortList;
+ }
+}
+
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index 337484e..19f951e 100644
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -1,10 +1,12 @@
-/* Copyright 2011, 2012 The Tor Project
+/* Copyright 2011--2014 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
import java.util.ArrayList;
+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.TreeSet;
@@ -18,6 +20,8 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
return this.statusEntryBytes;
}
+ private boolean microdescConsensus;
+
private boolean failUnrecognizedDescriptorLines;
private List<String> unrecognizedLines;
protected List<String> getAndClearUnrecognizedLines() {
@@ -27,9 +31,10 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
protected NetworkStatusEntryImpl(byte[] statusEntryBytes,
- boolean failUnrecognizedDescriptorLines)
+ boolean microdescConsensus, boolean failUnrecognizedDescriptorLines)
throws DescriptorParseException {
this.statusEntryBytes = statusEntryBytes;
+ this.microdescConsensus = microdescConsensus;
this.failUnrecognizedDescriptorLines =
failUnrecognizedDescriptorLines;
this.initializeKeywords();
@@ -95,20 +100,28 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parseRLine(String line, String[] parts)
throws DescriptorParseException {
- if (parts.length < 9) {
+ 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]);
- this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
- parts[3]);
+ int descriptorOffset = 0;
+ if (!this.microdescConsensus) {
+ this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
+ parts[3]);
+ descriptorOffset = 1;
+ }
this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 4, 5);
- this.address = ParseHelper.parseIpv4Address(line, parts[6]);
- this.orPort = ParseHelper.parsePort(line, parts[7]);
- this.dirPort = ParseHelper.parsePort(line, parts[8]);
+ 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)
@@ -194,8 +207,18 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parseMLine(String line, String[] parts)
throws DescriptorParseException {
- /* TODO Implement parsing of m lines in votes as specified in
- * dir-spec.txt. */
+ if (this.microdescriptorDigests == null) {
+ this.microdescriptorDigests = new HashSet<String>();
+ }
+ 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 String nickname;
@@ -233,6 +256,12 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
return this.dirPort;
}
+ private Set<String> microdescriptorDigests;
+ public Set<String> getMicrodescriptorDigests() {
+ return this.microdescriptorDigests == null ? null :
+ new HashSet<String>(this.microdescriptorDigests);
+ }
+
private List<String> orAddresses = new ArrayList<String>();
public List<String> getOrAddresses() {
return new ArrayList<String>(this.orAddresses);
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
index 94a76bf..6358ca3 100644
--- a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -176,7 +176,7 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
protected void parseStatusEntry(byte[] statusEntryBytes)
throws DescriptorParseException {
NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
- statusEntryBytes, this.failUnrecognizedDescriptorLines);
+ statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
List<String> unrecognizedStatusEntryLines = statusEntry.
getAndClearUnrecognizedLines();
@@ -210,13 +210,13 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
}
}
- private SortedMap<String, DirSourceEntry> dirSourceEntries =
+ protected SortedMap<String, DirSourceEntry> dirSourceEntries =
new TreeMap<String, DirSourceEntry>();
public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
return new TreeMap<String, DirSourceEntry>(this.dirSourceEntries);
}
- private SortedMap<String, NetworkStatusEntry> statusEntries =
+ protected SortedMap<String, NetworkStatusEntry> statusEntries =
new TreeMap<String, NetworkStatusEntry>();
public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
return new TreeMap<String, NetworkStatusEntry>(this.statusEntries);
@@ -228,7 +228,7 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
return this.statusEntries.get(fingerprint);
}
- private SortedMap<String, DirectorySignature> directorySignatures;
+ protected SortedMap<String, DirectorySignature> directorySignatures;
public SortedMap<String, DirectorySignature> getDirectorySignatures() {
return this.directorySignatures == null ? null :
new TreeMap<String, DirectorySignature>(this.directorySignatures);
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
index ebb8bc6..fd38eb0 100644
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/org/torproject/descriptor/impl/ParseHelper.java
@@ -239,11 +239,11 @@ public class ParseHelper {
return nickname;
}
- private static Pattern base64Pattern =
+ private static Pattern twentyByteBase64Pattern =
Pattern.compile("^[0-9a-zA-Z+/]{27}$");
public static String parseTwentyByteBase64String(String line,
String base64String) throws DescriptorParseException {
- if (!base64Pattern.matcher(base64String).matches()) {
+ if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
throw new DescriptorParseException("'" + base64String
+ "' in line '" + line + "' is not a valid base64-encoded "
+ "20-byte value.");
@@ -252,6 +252,19 @@ public class ParseHelper {
toUpperCase();
}
+ private static Pattern thirtyTwoByteBase64Pattern =
+ Pattern.compile("^[0-9a-zA-Z+/]{43}$");
+ public 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 Hex.encodeHexString(Base64.decodeBase64(base64String + "=")).
+ toUpperCase();
+ }
+
public static SortedMap<String, Integer>
parseCommaSeparatedKeyIntegerValueList(String line,
String[] partsNoOpt, int index, int keyLength)
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index dad5bc3..d6babee 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011, 2012 The Tor Project
+/* Copyright 2011--2014 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
@@ -17,7 +17,7 @@ import java.util.TreeSet;
import org.apache.commons.codec.digest.DigestUtils;
import org.torproject.descriptor.RelayNetworkStatusConsensus;
-/* Contains a network status consensus. */
+/* Contains a network status consensus or microdesc consensus. */
public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
implements RelayNetworkStatusConsensus {
@@ -120,6 +120,23 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
}
}
+ 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<String>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
protected void parseFooter(byte[] footerBytes)
throws DescriptorParseException {
Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
@@ -144,11 +161,20 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
private void parseNetworkStatusVersionLine(String line, String[] parts)
throws DescriptorParseException {
- if (!line.equals("network-status-version 3")) {
+ 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)
@@ -277,6 +303,11 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
return this.networkStatusVersion;
}
+ private String consensusFlavor;
+ public String getConsensusFlavor() {
+ return this.consensusFlavor;
+ }
+
private int consensusMethod;
public int getConsensusMethod() {
return this.consensusMethod;
diff --git a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
index baf4456..d1fcd28 100644
--- a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -508,7 +508,7 @@ public class ServerDescriptorImpl extends DescriptorImpl
if (partsNoOpt.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
- this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
+ this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
}
private void calculateDigest() throws DescriptorParseException {
More information about the tor-commits
mailing list