commit df6bdc8d9bdd0ee0bf66b6d5587751bc3f86aa46 Author: iwakeh iwakeh@torproject.org Date: Fri May 26 08:52:00 2017 +0000
Implements task-19607. Use enums for keywords as well as enum sets and maps. Use constants for repeated strings. --- CHANGELOG.md | 5 + .../impl/BridgeExtraInfoDescriptorImpl.java | 2 +- .../descriptor/impl/BridgeNetworkStatusImpl.java | 10 +- .../descriptor/impl/BridgePoolAssignmentImpl.java | 20 +- .../impl/BridgeServerDescriptorImpl.java | 2 +- .../torproject/descriptor/impl/DescriptorImpl.java | 185 +++++++++-------- .../descriptor/impl/DirSourceEntryImpl.java | 82 ++++---- .../impl/DirectoryKeyCertificateImpl.java | 95 +++++---- .../descriptor/impl/DirectorySignatureImpl.java | 23 ++- .../descriptor/impl/ExtraInfoDescriptorImpl.java | 218 +++++++++++---------- .../java/org/torproject/descriptor/impl/Key.java | 177 +++++++++++++++++ .../descriptor/impl/MicrodescriptorImpl.java | 56 +++--- .../descriptor/impl/NetworkStatusEntryImpl.java | 70 ++++--- .../descriptor/impl/NetworkStatusImpl.java | 29 +-- .../descriptor/impl/RelayDirectoryImpl.java | 137 ++++++------- .../impl/RelayExtraInfoDescriptorImpl.java | 2 +- .../impl/RelayNetworkStatusConsensusImpl.java | 90 ++++----- .../descriptor/impl/RelayNetworkStatusImpl.java | 102 +++++----- .../impl/RelayNetworkStatusVoteImpl.java | 168 ++++++++-------- .../descriptor/impl/RelayServerDescriptorImpl.java | 2 +- .../descriptor/impl/ServerDescriptorImpl.java | 192 +++++++++--------- 21 files changed, 930 insertions(+), 737 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index c76bbc1..0e39147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ descriptor lines, as permitted by dir-spec.txt. - Streamline digest method names.
+ * Minor changes + - Turn keyword strings into enums and use the appropriate enum sets + and maps to avoid repeating string literals and to use more speedy + collection types. +
# Changes in version 1.6.0 - 2017-02-17
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java index 080dde1..0518392 100644 --- a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java @@ -19,7 +19,7 @@ public class BridgeExtraInfoDescriptorImpl List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "extra-info "); + Key.EXTRA_INFO.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { ExtraInfoDescriptor parsedDescriptor = new BridgeExtraInfoDescriptorImpl(descriptorBytes, diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java index 80aba01..88411e6 100644 --- a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java @@ -71,16 +71,16 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl this.enoughMtbfInfo = -1; this.ignoringAdvertisedBws = -1;
- Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter("\n"); + Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "published": + Key key = Key.get(parts[0]); + switch (key) { + case PUBLISHED: this.parsePublishedLine(line, parts); break; - case "flag-thresholds": + case FLAG_THRESHOLDS: this.parseFlagThresholdsLine(line, parts); break; default: diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java index e2579e5..609797e 100644 --- a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java @@ -7,11 +7,9 @@ import org.torproject.descriptor.BridgePoolAssignment; import org.torproject.descriptor.DescriptorParseException;
import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; -import java.util.Set; import java.util.SortedMap; import java.util.TreeMap;
@@ -24,7 +22,7 @@ public class BridgePoolAssignmentImpl extends DescriptorImpl List<BridgePoolAssignment> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "bridge-pool-assignment "); + Key.BRIDGE_POOL_ASSIGNMENT.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { BridgePoolAssignment parsedDescriptor = new BridgePoolAssignmentImpl(descriptorBytes, @@ -39,20 +37,18 @@ public class BridgePoolAssignmentImpl extends DescriptorImpl 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(); + this.checkExactlyOnceKeys(EnumSet.of(Key.BRIDGE_POOL_ASSIGNMENT)); + this.checkFirstKey(Key.BRIDGE_POOL_ASSIGNMENT); + this.clearParsedKeys(); return; }
private void parseDescriptorBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.rawDescriptorBytes)) - .useDelimiter("\n"); + .useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); - if (line.startsWith("bridge-pool-assignment ")) { + if (line.startsWith(Key.BRIDGE_POOL_ASSIGNMENT.keyword + SP)) { this.parseBridgePoolAssignmentLine(line); } else { this.parseBridgeLine(line); @@ -80,7 +76,7 @@ public class BridgePoolAssignmentImpl extends DescriptorImpl } String fingerprint = ParseHelper.parseTwentyByteHexString(line, parts[0]); - String poolAndDetails = line.substring(line.indexOf(" ") + 1); + String poolAndDetails = line.substring(line.indexOf(SP) + 1); this.entries.put(fingerprint, poolAndDetails); }
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java index 43548fc..900f6cd 100644 --- a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java @@ -19,7 +19,7 @@ public class BridgeServerDescriptorImpl extends ServerDescriptorImpl List<ServerDescriptor> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "router "); + Key.ROUTER.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { ServerDescriptor parsedDescriptor = new BridgeServerDescriptorImpl(descriptorBytes, diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java index 49f4a2e..f963fef 100644 --- a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java @@ -8,7 +8,7 @@ import org.torproject.descriptor.DescriptorParseException;
import java.io.UnsupportedEncodingException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Scanner; @@ -16,6 +16,10 @@ import java.util.Set;
public abstract class DescriptorImpl implements Descriptor {
+ public static final String NL = "\n"; + + public static final String SP = " "; + protected static List<Descriptor> parseDescriptors( byte[] rawDescriptorBytes, String fileName, boolean failUnrecognizedDescriptorLines) @@ -30,33 +34,38 @@ public abstract class DescriptorImpl implements Descriptor { 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"))) { + || firstLines.startsWith( + "@type network-status-microdesc-consensus-3 1.") + || ((firstLines.startsWith( + Key.NETWORK_STATUS_VERSION.keyword + SP + "3") + || firstLines.contains( + NL + Key.NETWORK_STATUS_VERSION.keyword + SP + "3")) + && firstLines.contains( + NL + Key.VOTE_STATUS.keyword + SP + "consensus" + NL))) { 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"))) { + || ((firstLines.startsWith( + Key.NETWORK_STATUS_VERSION.keyword + SP + "3" + NL) + || firstLines.contains( + NL + Key.NETWORK_STATUS_VERSION.keyword + SP + "3" + NL)) + && firstLines.contains( + NL + Key.VOTE_STATUS.keyword + SP + "vote" + NL))) { parsedDescriptors.addAll(RelayNetworkStatusVoteImpl .parseVotes(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type bridge-network-status 1.") - || firstLines.startsWith("r ")) { + || firstLines.startsWith(Key.R.keyword + SP)) { parsedDescriptors.add(new BridgeNetworkStatusImpl( rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines)); - } else if (firstLines.startsWith( - "@type bridge-server-descriptor 1.")) { + } 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 ")) { + || firstLines.startsWith(Key.ROUTER.keyword + SP) + || firstLines.contains(NL + Key.ROUTER.keyword + SP)) { parsedDescriptors.addAll(RelayServerDescriptorImpl .parseDescriptors(rawDescriptorBytes, failUnrecognizedDescriptorLines)); @@ -65,42 +74,45 @@ public abstract class DescriptorImpl implements Descriptor { .parseDescriptors(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type extra-info 1.") - || firstLines.startsWith("extra-info ") - || firstLines.contains("\nextra-info ")) { + || firstLines.startsWith(Key.EXTRA_INFO.keyword + SP) + || firstLines.contains(NL + Key.EXTRA_INFO.keyword + SP)) { parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl .parseDescriptors(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type microdescriptor 1.") - || firstLines.startsWith("onion-key\n") - || firstLines.contains("\nonion-key\n")) { + || firstLines.startsWith(Key.ONION_KEY.keyword + NL) + || firstLines.contains(NL + Key.ONION_KEY.keyword + NL)) { 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 ")) { + || firstLines.startsWith(Key.BRIDGE_POOL_ASSIGNMENT.keyword + SP) + || firstLines.contains(NL + Key.BRIDGE_POOL_ASSIGNMENT.keyword + SP)) { 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 ")) { + || firstLines.startsWith(Key.DIR_KEY_CERTIFICATE_VERSION.keyword + SP) + || firstLines.contains( + NL + Key.DIR_KEY_CERTIFICATE_VERSION.keyword + SP)) { parsedDescriptors.addAll(DirectoryKeyCertificateImpl .parseDescriptors(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type tordnsel 1.") - || firstLines.startsWith("ExitNode ") - || firstLines.contains("\nExitNode ")) { + || firstLines.startsWith("ExitNode" + SP) + || firstLines.contains(NL + "ExitNode" + SP)) { 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")) { + || firstLines.startsWith( + Key.NETWORK_STATUS_VERSION.keyword + SP + "2" + NL) + || firstLines.contains( + NL + Key.NETWORK_STATUS_VERSION.keyword + SP + "2" + NL)) { parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type directory 1.") - || firstLines.startsWith("signed-directory\n") - || firstLines.contains("\nsigned-directory\n")) { + || firstLines.startsWith(Key.SIGNED_DIRECTORY.keyword + NL) + || firstLines.contains(NL + Key.SIGNED_DIRECTORY.keyword + NL)) { parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes, failUnrecognizedDescriptorLines)); } else if (firstLines.startsWith("@type torperf 1.")) { @@ -116,7 +128,7 @@ public abstract class DescriptorImpl implements Descriptor { protected static List<byte[]> splitRawDescriptorBytes( byte[] rawDescriptorBytes, String startToken) { List<byte[]> rawDescriptors = new ArrayList<>(); - String splitToken = "\n" + startToken; + String splitToken = NL + startToken; String ascii; try { ascii = new String(rawDescriptorBytes, "US-ASCII"); @@ -126,7 +138,7 @@ public abstract class DescriptorImpl implements Descriptor { int endAllDescriptors = rawDescriptorBytes.length; int startAnnotations = 0; boolean containsAnnotations = ascii.startsWith("@") - || ascii.contains("\n@"); + || ascii.contains(NL + "@"); while (startAnnotations < endAllDescriptors) { int startDescriptor; if (ascii.indexOf(startToken, startAnnotations) == 0) { @@ -141,7 +153,7 @@ public abstract class DescriptorImpl implements Descriptor { } int endDescriptor = -1; if (containsAnnotations) { - endDescriptor = ascii.indexOf("\n@", startDescriptor); + endDescriptor = ascii.indexOf(NL + "@", startDescriptor); } if (endDescriptor < 0) { endDescriptor = ascii.indexOf(splitToken, startDescriptor); @@ -183,7 +195,7 @@ public abstract class DescriptorImpl implements Descriptor { this.failUnrecognizedDescriptorLines = failUnrecognizedDescriptorLines; this.cutOffAnnotations(rawDescriptorBytes); - this.countKeywords(rawDescriptorBytes, blankLinesAllowed); + this.countKeys(rawDescriptorBytes, blankLinesAllowed); }
/* Parse annotation lines from the descriptor bytes. */ @@ -194,8 +206,8 @@ public abstract class DescriptorImpl implements Descriptor { 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); + || (start > 0 && ascii.indexOf(NL + "@", start - 1) >= 0)) { + int end = ascii.indexOf(NL, start); if (end < 0) { throw new DescriptorParseException("Annotation line does not " + "contain a newline."); @@ -217,130 +229,129 @@ public abstract class DescriptorImpl implements Descriptor { return new ArrayList<>(this.annotations); }
- private String firstKeyword; + private Key firstKey = Key.EMPTY;
- private String lastKeyword; + private Key lastKey = Key.EMPTY;
- private Map<String, Integer> parsedKeywords = new HashMap<>(); + private Map<Key, Integer> parsedKeys = new EnumMap<>(Key.class);
/* Count parsed keywords for consistency checks by subclasses. */ - private void countKeywords(byte[] rawDescriptorBytes, + private void countKeys(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"))) { + if (!blankLinesAllowed && (descriptorString.startsWith(NL) + || descriptorString.contains(NL + NL))) { throw new DescriptorParseException("Blank lines are not allowed."); } boolean skipCrypto = false; - Scanner scanner = new Scanner(descriptorString).useDelimiter("\n"); + Scanner scanner = new Scanner(descriptorString).useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); - if (line.startsWith("-----BEGIN")) { + if (line.startsWith(Key.CRYPTO_BEGIN.keyword)) { skipCrypto = true; - } else if (line.startsWith("-----END")) { + } else if (line.startsWith(Key.CRYPTO_END.keyword)) { 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]; + String lineNoOpt = line.startsWith(Key.OPT.keyword + SP) + ? line.substring(Key.OPT.keyword.length() + 1) : line; + String keyword = lineNoOpt.split(SP, -1)[0]; if (keyword.equals("")) { throw new DescriptorParseException("Illegal keyword in line '" + line + "'."); } - if (this.firstKeyword == null) { - this.firstKeyword = keyword; + Key key = Key.get(keyword); + if (Key.EMPTY == this.firstKey) { + this.firstKey = key; } - lastKeyword = keyword; - if (parsedKeywords.containsKey(keyword)) { - parsedKeywords.put(keyword, parsedKeywords.get(keyword) + 1); + lastKey = key; + if (parsedKeys.containsKey(key)) { + parsedKeys.put(key, parsedKeys.get(key) + 1); } else { - parsedKeywords.put(keyword, 1); + parsedKeys.put(key, 1); } } } }
- protected void checkFirstKeyword(String keyword) + protected void checkFirstKey(Key key) throws DescriptorParseException { - if (this.firstKeyword == null - || !this.firstKeyword.equals(keyword)) { - throw new DescriptorParseException("Keyword '" + keyword + "' must " + if (this.firstKey != key) { + throw new DescriptorParseException("Keyword '" + key.keyword + "' must " + "be contained in the first line."); } }
- protected void checkLastKeyword(String keyword) + protected void checkLastKey(Key key) throws DescriptorParseException { - if (this.lastKeyword == null - || !this.lastKeyword.equals(keyword)) { - throw new DescriptorParseException("Keyword '" + keyword + "' must " + if (this.lastKey != key) { + throw new DescriptorParseException("Keyword '" + key.keyword + "' must " + "be contained in the last line."); } }
- protected void checkExactlyOnceKeywords(Set<String> keywords) + protected void checkExactlyOnceKeys(Set<Key> keys) throws DescriptorParseException { - for (String keyword : keywords) { + for (Key key : keys) { int contained = 0; - if (this.parsedKeywords.containsKey(keyword)) { - contained = this.parsedKeywords.get(keyword); + if (this.parsedKeys.containsKey(key)) { + contained = this.parsedKeys.get(key); } if (contained != 1) { - throw new DescriptorParseException("Keyword '" + keyword + "' is " + throw new DescriptorParseException("Keyword '" + key.keyword + "' is " + "contained " + contained + " times, but must be contained " + "exactly once."); } } }
- protected void checkAtLeastOnceKeywords(Set<String> keywords) + protected void checkAtLeastOnceKeys(Set<Key> keys) throws DescriptorParseException { - for (String keyword : keywords) { - if (!this.parsedKeywords.containsKey(keyword)) { - throw new DescriptorParseException("Keyword '" + keyword + "' is " + for (Key key : keys) { + if (!this.parsedKeys.containsKey(key)) { + throw new DescriptorParseException("Keyword '" + key.keyword + "' is " + "contained 0 times, but must be contained at least once."); } } }
- protected void checkAtMostOnceKeywords(Set<String> keywords) + protected void checkAtMostOnceKeys(Set<Key> keys) 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, " + for (Key key : keys) { + if (this.parsedKeys.containsKey(key) + && this.parsedKeys.get(key) > 1) { + throw new DescriptorParseException("Keyword '" + key.keyword + "' is " + + "contained " + this.parsedKeys.get(key) + " 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 " + protected void checkKeysDependOn(Set<Key> dependentKeys, + Key dependingKey) throws DescriptorParseException { + for (Key dependentKey : dependentKeys) { + if (this.parsedKeys.containsKey(dependentKey) + && !this.parsedKeys.containsKey(dependingKey)) { + throw new DescriptorParseException("Keyword '" + dependentKey.keyword + + "' is contained, but keyword '" + dependingKey.keyword + "' is " + "not."); } } }
- protected int getKeywordCount(String keyword) { - if (!this.parsedKeywords.containsKey(keyword)) { + protected int getKeyCount(Key key) { + if (!this.parsedKeys.containsKey(key)) { return 0; } else { - return this.parsedKeywords.get(keyword); + return this.parsedKeys.get(key); } }
- protected void clearParsedKeywords() { - this.parsedKeywords = null; + protected void clearParsedKeys() { + this.parsedKeys = null; } }
diff --git a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java index 539d211..fd2c783 100644 --- a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java @@ -3,14 +3,17 @@
package org.torproject.descriptor.impl;
+import static org.torproject.descriptor.impl.DescriptorImpl.NL; +import static org.torproject.descriptor.impl.DescriptorImpl.SP; + import org.torproject.descriptor.DescriptorParseException; import org.torproject.descriptor.DirSourceEntry;
import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.Set;
public class DirSourceEntryImpl implements DirSourceEntry {
@@ -37,72 +40,67 @@ public class DirSourceEntryImpl implements DirSourceEntry { this.dirSourceEntryBytes = dirSourceEntryBytes; this.failUnrecognizedDescriptorLines = failUnrecognizedDescriptorLines; - this.initializeKeywords(); this.parseDirSourceEntryBytes(); - this.checkAndClearKeywords(); + this.checkAndClearKeys(); }
- private SortedSet<String> exactlyOnceKeywords; - - private SortedSet<String> atMostOnceKeywords; + private Set<Key> exactlyOnceKeys = EnumSet.of( + Key.DIR_SOURCE, Key.VOTE_DIGEST);
- 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 Set<Key> atMostOnceKeys = EnumSet.of(Key.CONTACT);
- private void parsedExactlyOnceKeyword(String keyword) + private void parsedExactlyOnceKey(Key key) throws DescriptorParseException { - if (!this.exactlyOnceKeywords.contains(keyword)) { - throw new DescriptorParseException("Duplicate '" + keyword + if (!this.exactlyOnceKeys.contains(key)) { + throw new DescriptorParseException("Duplicate '" + key.keyword + "' line in dir-source."); } - this.exactlyOnceKeywords.remove(keyword); + this.exactlyOnceKeys.remove(key); }
- private void parsedAtMostOnceKeyword(String keyword) + private void parsedAtMostOnceKey(Key key) throws DescriptorParseException { - if (!this.atMostOnceKeywords.contains(keyword)) { - throw new DescriptorParseException("Duplicate " + keyword + "line " + if (!this.atMostOnceKeys.contains(key)) { + throw new DescriptorParseException("Duplicate " + key.keyword + "line " + "in dir-source."); } - this.atMostOnceKeywords.remove(keyword); + this.atMostOnceKeys.remove(key); }
- private void checkAndClearKeywords() throws DescriptorParseException { - if (!this.exactlyOnceKeywords.isEmpty()) { - throw new DescriptorParseException("dir-source does not contain a '" - + this.exactlyOnceKeywords.first() + "' line."); + private void checkAndClearKeys() throws DescriptorParseException { + if (!this.exactlyOnceKeys.isEmpty()) { + for (Key key : this.exactlyOnceKeys) { + throw new DescriptorParseException("dir-source does not contain a '" + + key.keyword + "' line."); + } } - this.exactlyOnceKeywords = null; - this.atMostOnceKeywords = null; + this.exactlyOnceKeys = null; + this.atMostOnceKeys = null; }
private void parseDirSourceEntryBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.dirSourceEntryBytes)) - .useDelimiter("\n"); + .useDelimiter(NL); boolean skipCrypto = false; while (scanner.hasNext()) { String line = scanner.next(); - String[] parts = line.split(" "); - switch (parts[0]) { - case "dir-source": + String[] parts = line.split(SP); + Key key = Key.get(parts[0]); + switch (key) { + case DIR_SOURCE: this.parseDirSourceLine(line); break; - case "contact": + case CONTACT: this.parseContactLine(line); break; - case "vote-digest": + case VOTE_DIGEST: this.parseVoteDigestLine(line); break; - case "-----BEGIN": + case CRYPTO_BEGIN: skipCrypto = true; break; - case "-----END": + case CRYPTO_END: skipCrypto = false; break; default: @@ -123,7 +121,7 @@ public class DirSourceEntryImpl implements DirSourceEntry {
private void parseDirSourceLine(String line) throws DescriptorParseException { - this.parsedExactlyOnceKeyword("dir-source"); + this.parsedExactlyOnceKey(Key.DIR_SOURCE); String[] parts = line.split("[ \t]+"); if (parts.length != 7) { throw new DescriptorParseException("Invalid line '" + line + "'."); @@ -133,7 +131,7 @@ public class DirSourceEntryImpl implements DirSourceEntry { nickname = nickname.substring(0, nickname.length() - "-legacy".length()); this.isLegacy = true; - this.parsedExactlyOnceKeyword("vote-digest"); + this.parsedExactlyOnceKey(Key.VOTE_DIGEST); } this.nickname = ParseHelper.parseNickname(line, nickname); this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]); @@ -149,9 +147,9 @@ public class DirSourceEntryImpl implements DirSourceEntry {
private void parseContactLine(String line) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("contact"); - if (line.length() > "contact ".length()) { - this.contactLine = line.substring("contact ".length()); + this.parsedAtMostOnceKey(Key.CONTACT); + if (line.length() > Key.CONTACT.keyword.length() + 1) { + this.contactLine = line.substring(Key.CONTACT.keyword.length() + 1); } else { this.contactLine = ""; } @@ -159,7 +157,7 @@ public class DirSourceEntryImpl implements DirSourceEntry {
private void parseVoteDigestLine(String line) throws DescriptorParseException { - this.parsedExactlyOnceKeyword("vote-digest"); + this.parsedExactlyOnceKey(Key.VOTE_DIGEST); String[] parts = line.split("[ \t]+"); if (parts.length != 2) { throw new DescriptorParseException("Invalid line '" + line + "'."); diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java index 64df7aa..0bb08fd 100644 --- a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java @@ -10,8 +10,7 @@ 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.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -27,7 +26,7 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl List<DirectoryKeyCertificate> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DirectoryKeyCertificateImpl.splitRawDescriptorBytes( - descriptorsBytes, "dir-key-certificate-version "); + descriptorsBytes, Key.DIR_KEY_CERTIFICATE_VERSION.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { DirectoryKeyCertificate parsedDescriptor = new DirectoryKeyCertificateImpl(descriptorBytes, @@ -43,90 +42,90 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl 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(); + Set<Key> exactlyOnceKeys = EnumSet.of( + Key.DIR_KEY_CERTIFICATE_VERSION, Key.FINGERPRINT, Key.DIR_IDENTITY_KEY, + Key.DIR_KEY_PUBLISHED, Key.DIR_KEY_EXPIRES, Key.DIR_SIGNING_KEY, + Key.DIR_KEY_CERTIFICATION); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.DIR_ADDRESS, Key.DIR_KEY_CROSSCERT); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkFirstKey(Key.DIR_KEY_CERTIFICATE_VERSION); + this.checkLastKey(Key.DIR_KEY_CERTIFICATION); + this.clearParsedKeys(); }
private void parseDescriptorBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.rawDescriptorBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "dir-key-certificate-version": + Key key = Key.get(parts[0]); + switch (key) { + case DIR_KEY_CERTIFICATE_VERSION: this.parseDirKeyCertificateVersionLine(line, parts); break; - case "dir-address": + case DIR_ADDRESS: this.parseDirAddressLine(line, parts); break; - case "fingerprint": + case FINGERPRINT: this.parseFingerprintLine(line, parts); break; - case "dir-identity-key": + case DIR_IDENTITY_KEY: this.parseDirIdentityKeyLine(line, parts); - nextCrypto = "dir-identity-key"; + nextCrypto = key; break; - case "dir-key-published": + case DIR_KEY_PUBLISHED: this.parseDirKeyPublishedLine(line, parts); break; - case "dir-key-expires": + case DIR_KEY_EXPIRES: this.parseDirKeyExpiresLine(line, parts); break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.parseDirSigningKeyLine(line, parts); - nextCrypto = "dir-signing-key"; + nextCrypto = key; break; - case "dir-key-crosscert": + case DIR_KEY_CROSSCERT: this.parseDirKeyCrosscertLine(line, parts); - nextCrypto = "dir-key-crosscert"; + nextCrypto = key; break; - case "dir-key-certification": + case DIR_KEY_CERTIFICATION: this.parseDirKeyCertificationLine(line, parts); - nextCrypto = "dir-key-certification"; + nextCrypto = key; break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; switch (nextCrypto) { - case "dir-identity-key": + case DIR_IDENTITY_KEY: this.dirIdentityKey = cryptoString; break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.dirSigningKey = cryptoString; break; - case "dir-key-crosscert": + case DIR_KEY_CROSSCERT: this.dirKeyCrosscert = cryptoString; break; - case "dir-key-certification": + case DIR_KEY_CERTIFICATION: this.dirKeyCertification = cryptoString; break; default: throw new DescriptorParseException("Unrecognized crypto " + "block in directory key certificate."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else { if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" @@ -144,7 +143,7 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
private void parseDirKeyCertificateVersionLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-key-certificate-version 3")) { + if (!line.equals(Key.DIR_KEY_CERTIFICATE_VERSION.keyword + SP + "3")) { throw new DescriptorParseException("Illegal directory key " + "certificate version number in line '" + line + "'."); } @@ -174,7 +173,7 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
private void parseDirIdentityKeyLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-identity-key")) { + if (!line.equals(Key.DIR_IDENTITY_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } } @@ -193,21 +192,21 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
private void parseDirSigningKeyLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-signing-key")) { + if (!line.equals(Key.DIR_SIGNING_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseDirKeyCrosscertLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-key-crosscert")) { + if (!line.equals(Key.DIR_KEY_CROSSCERT.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseDirKeyCertificationLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-key-certification")) { + if (!line.equals(Key.DIR_KEY_CERTIFICATION.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } } @@ -215,8 +214,8 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl 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"; + String startToken = Key.DIR_KEY_CERTIFICATE_VERSION.keyword + SP; + String sigToken = NL + Key.DIR_KEY_CERTIFICATION.keyword + NL; int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); if (start >= 0 && sig >= 0 && sig > start) { diff --git a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java index 7cf427a..674b634 100644 --- a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java @@ -3,6 +3,9 @@
package org.torproject.descriptor.impl;
+import static org.torproject.descriptor.impl.DescriptorImpl.NL; +import static org.torproject.descriptor.impl.DescriptorImpl.SP; + import org.torproject.descriptor.DescriptorParseException; import org.torproject.descriptor.DirectorySignature;
@@ -36,14 +39,14 @@ public class DirectorySignatureImpl implements DirectorySignature { private void parseDirectorySignatureBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.directorySignatureBytes)) - .useDelimiter("\n"); + .useDelimiter(NL); StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); - String[] parts = line.split(" ", -1); - String keyword = parts[0]; - switch (keyword) { - case "directory-signature": + String[] parts = line.split(SP, -1); + Key key = Key.get(parts[0]); + switch (key) { + case DIRECTORY_SIGNATURE: int algorithmOffset = 0; switch (parts.length) { case 4: @@ -61,19 +64,19 @@ public class DirectorySignatureImpl implements DirectorySignature { this.signingKeyDigest = ParseHelper.parseHexString( line, parts[2 + algorithmOffset]); break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; this.signature = cryptoString; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else { if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java index dd1bc07..fd55925 100644 --- a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java @@ -14,8 +14,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Scanner; @@ -28,6 +28,16 @@ import javax.xml.bind.DatatypeConverter; public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl implements ExtraInfoDescriptor {
+ private Set<Key> exactlyOnceKeys = EnumSet.of( + Key.EXTRA_INFO, Key.PUBLISHED); + + private static final Set<Key> atMostOnceKeys = EnumSet.of( + Key.IDENTITY_ED25519, Key.MASTER_KEY_ED25519, Key.READ_HISTORY, + Key.WRITE_HISTORY, Key.DIRREQ_READ_HISTORY, Key.DIRREQ_WRITE_HISTORY, + Key.GEOIP_DB_DIGEST, Key.GEOIP6_DB_DIGEST, Key.ROUTER_SIG_ED25519, + Key.ROUTER_SIGNATURE, Key.ROUTER_DIGEST_SHA256, Key.ROUTER_DIGEST, + Key.PADDING_COUNTS); + protected ExtraInfoDescriptorImpl(byte[] descriptorBytes, boolean failUnrecognizedDescriptorLines) throws DescriptorParseException { @@ -35,231 +45,223 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl 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); - atMostOnceKeywords.add("padding-counts"); - 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(); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> dirreqStatsKeys = EnumSet.of( + Key.DIRREQ_STATS_END, Key.DIRREQ_V2_IPS, Key.DIRREQ_V3_IPS, + Key.DIRREQ_V2_REQS, Key.DIRREQ_V3_REQS, Key.DIRREQ_V2_SHARE, + Key.DIRREQ_V3_SHARE, Key.DIRREQ_V2_RESP, Key.DIRREQ_V3_RESP, + Key.DIRREQ_V2_DIRECT_DL, Key.DIRREQ_V3_DIRECT_DL, + Key.DIRREQ_V2_TUNNELED_DL, Key.DIRREQ_V3_TUNNELED_DL); + Set<Key> entryStatsKeys = EnumSet.of( + Key.ENTRY_STATS_END, Key.ENTRY_IPS); + Set<Key> cellStatsKeys = EnumSet.of( + Key.CELL_STATS_END, Key.CELL_PROCESSED_CELLS, Key.CELL_QUEUED_CELLS, + Key.CELL_TIME_IN_QUEUE, Key.CELL_CIRCUITS_PER_DECILE); + Set<Key> connBiDirectStatsKeys = EnumSet.of(Key.CONN_BI_DIRECT); + Set<Key> exitStatsKeys = EnumSet.of( + Key.EXIT_STATS_END, Key.EXIT_KIBIBYTES_WRITTEN, Key.EXIT_KIBIBYTES_READ, + Key.EXIT_STREAMS_OPENED); + Set<Key> bridgeStatsKeys = EnumSet.of( + Key.BRIDGE_STATS_END, Key.BRIDGE_IPS); + atMostOnceKeys.addAll(dirreqStatsKeys); + atMostOnceKeys.addAll(entryStatsKeys); + atMostOnceKeys.addAll(cellStatsKeys); + atMostOnceKeys.addAll(connBiDirectStatsKeys); + atMostOnceKeys.addAll(exitStatsKeys); + atMostOnceKeys.addAll(bridgeStatsKeys); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkKeysDependOn(dirreqStatsKeys, Key.DIRREQ_STATS_END); + this.checkKeysDependOn(entryStatsKeys, Key.ENTRY_STATS_END); + this.checkKeysDependOn(cellStatsKeys, Key.CELL_STATS_END); + this.checkKeysDependOn(exitStatsKeys, Key.EXIT_STATS_END); + this.checkKeysDependOn(bridgeStatsKeys, Key.BRIDGE_STATS_END); + this.checkFirstKey(Key.EXTRA_INFO); + this.clearParsedKeys(); return; }
private void parseDescriptorBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.rawDescriptorBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; List<String> cryptoLines = null; while (scanner.hasNext()) { String line = scanner.next(); - String lineNoOpt = line.startsWith("opt ") - ? line.substring("opt ".length()) : line; + String lineNoOpt = line.startsWith(Key.OPT.keyword + SP) + ? line.substring(Key.OPT.keyword.length() + 1) : line; String[] partsNoOpt = lineNoOpt.split("[ \t]+"); - String keyword = partsNoOpt[0]; - switch (keyword) { - case "extra-info": + Key key = Key.get(partsNoOpt[0]); + switch (key) { + case EXTRA_INFO: this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt); break; - case "published": + case PUBLISHED: this.parsePublishedLine(line, lineNoOpt, partsNoOpt); break; - case "read-history": + case READ_HISTORY: this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "write-history": + case WRITE_HISTORY: this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "geoip-db-digest": + case GEOIP_DB_DIGEST: this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt); break; - case "geoip6-db-digest": + case GEOIP6_DB_DIGEST: this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt); break; - case "geoip-start-time": + case GEOIP_START_TIME: this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt); break; - case "geoip-client-origins": + case GEOIP_CLIENT_ORIGINS: this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-stats-end": + case DIRREQ_STATS_END: this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-ips": + case DIRREQ_V2_IPS: this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-ips": + case DIRREQ_V3_IPS: this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-reqs": + case DIRREQ_V2_REQS: this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-reqs": + case DIRREQ_V3_REQS: this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-share": + case DIRREQ_V2_SHARE: this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-share": + case DIRREQ_V3_SHARE: this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-resp": + case DIRREQ_V2_RESP: this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-resp": + case DIRREQ_V3_RESP: this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-direct-dl": + case DIRREQ_V2_DIRECT_DL: this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-direct-dl": + case DIRREQ_V3_DIRECT_DL: this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v2-tunneled-dl": + case DIRREQ_V2_TUNNELED_DL: this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-v3-tunneled-dl": + case DIRREQ_V3_TUNNELED_DL: this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-read-history": + case DIRREQ_READ_HISTORY: this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "dirreq-write-history": + case DIRREQ_WRITE_HISTORY: this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "entry-stats-end": + case ENTRY_STATS_END: this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "entry-ips": + case ENTRY_IPS: this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt); break; - case "cell-stats-end": + case CELL_STATS_END: this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "cell-processed-cells": + case CELL_PROCESSED_CELLS: this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt); break; - case "cell-queued-cells": + case CELL_QUEUED_CELLS: this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt); break; - case "cell-time-in-queue": + case CELL_TIME_IN_QUEUE: this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt); break; - case "cell-circuits-per-decile": + case CELL_CIRCUITS_PER_DECILE: this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt); break; - case "conn-bi-direct": + case CONN_BI_DIRECT: this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt); break; - case "exit-stats-end": + case EXIT_STATS_END: this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "exit-kibibytes-written": + case EXIT_KIBIBYTES_WRITTEN: this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt); break; - case "exit-kibibytes-read": + case EXIT_KIBIBYTES_READ: this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt); break; - case "exit-streams-opened": + case EXIT_STREAMS_OPENED: this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt); break; - case "bridge-stats-end": + case BRIDGE_STATS_END: this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "bridge-ips": + case BRIDGE_IPS: this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt); break; - case "bridge-ip-versions": + case BRIDGE_IP_VERSIONS: this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt); break; - case "bridge-ip-transports": + case BRIDGE_IP_TRANSPORTS: this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt); break; - case "transport": + case TRANSPORT: this.parseTransportLine(line, lineNoOpt, partsNoOpt); break; - case "hidserv-stats-end": + case HIDSERV_STATS_END: this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt); break; - case "hidserv-rend-relayed-cells": + case HIDSERV_REND_RELAYED_CELLS: this.parseHidservRendRelayedCellsLine(line, lineNoOpt, partsNoOpt); break; - case "hidserv-dir-onions-seen": + case HIDSERV_DIR_ONIONS_SEEN: this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt); break; - case "padding-counts": + case PADDING_COUNTS: this.parsePaddingCountsLine(line, lineNoOpt, partsNoOpt); break; - case "identity-ed25519": + case IDENTITY_ED25519: this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt); - nextCrypto = "identity-ed25519"; + nextCrypto = key; break; - case "master-key-ed25519": + case MASTER_KEY_ED25519: this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt); break; - case "router-sig-ed25519": + case ROUTER_SIG_ED25519: this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt); break; - case "router-signature": + case ROUTER_SIGNATURE: this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "router-signature"; + nextCrypto = key; break; - case "router-digest": + case ROUTER_DIGEST: this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt); break; - case "router-digest-sha256": + case ROUTER_DIGEST_SHA256: this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt); break; - case "-----BEGIN": + case CRYPTO_BEGIN: cryptoLines = new ArrayList<>(); cryptoLines.add(line); break; - case "-----END": + case CRYPTO_END: cryptoLines.add(line); StringBuilder sb = new StringBuilder(); for (String cryptoLine : cryptoLines) { - sb.append("\n").append(cryptoLine); + sb.append(NL).append(cryptoLine); } String cryptoString = sb.toString().substring(1); switch (nextCrypto) { - case "router-signature": + case ROUTER_SIGNATURE: this.routerSignature = cryptoString; break; - case "identity-ed25519": + case IDENTITY_ED25519: this.identityEd25519 = cryptoString; this.parseIdentityEd25519CryptoBlock(cryptoString); break; @@ -276,7 +278,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl } } cryptoLines = null; - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (cryptoLines != null) { @@ -845,8 +847,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl } try { String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII"); - String startToken = "extra-info "; - String sigToken = "\nrouter-signature\n"; + String startToken = Key.EXTRA_INFO.keyword + SP; + String sigToken = NL + Key.ROUTER_SIGNATURE.keyword + NL; int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); if (start >= 0 && sig >= 0 && sig > start) { @@ -876,7 +878,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl } try { String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII"); - String startToken = "extra-info "; + String startToken = Key.EXTRA_INFO.keyword + SP; String sigToken = "\n-----END SIGNATURE-----\n"; int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); diff --git a/src/main/java/org/torproject/descriptor/impl/Key.java b/src/main/java/org/torproject/descriptor/impl/Key.java new file mode 100644 index 0000000..763ecbf --- /dev/null +++ b/src/main/java/org/torproject/descriptor/impl/Key.java @@ -0,0 +1,177 @@ +package org.torproject.descriptor.impl; + +import java.util.HashMap; +import java.util.Map; + +public enum Key { + + EMPTY("the-empty-key"), + INVALID("the-invalid-key"), + + /* crypto keys */ + CRYPTO_BEGIN("-----BEGIN"), + CRYPTO_END("-----END"), + + /* descriptor keys (in alphabetic order) */ + A("a"), + ACCEPT("accept"), + ALLOW_SINGLE_HOP_EXITS("allow-single-hop-exits"), + BANDWIDTH("bandwidth"), + BANDWIDTH_WEIGHTS("bandwidth-weights"), + BRIDGE_IPS("bridge-ips"), + BRIDGE_IP_TRANSPORTS("bridge-ip-transports"), + BRIDGE_IP_VERSIONS("bridge-ip-versions"), + BRIDGE_POOL_ASSIGNMENT("bridge-pool-assignment"), + BRIDGE_STATS_END("bridge-stats-end"), + CACHES_EXTRA_INFO("caches-extra-info"), + CELL_CIRCUITS_PER_DECILE("cell-circuits-per-decile"), + CELL_PROCESSED_CELLS("cell-processed-cells"), + CELL_QUEUED_CELLS("cell-queued-cells"), + CELL_STATS_END("cell-stats-end"), + CELL_TIME_IN_QUEUE("cell-time-in-queue"), + CLIENT_VERSIONS("client-versions"), + CONN_BI_DIRECT("conn-bi-direct"), + CONSENSUS_METHOD("consensus-method"), + CONSENSUS_METHODS("consensus-methods"), + CONTACT("contact"), + DIRCACHEPORT("dircacheport"), + DIRECTORY_FOOTER("directory-footer"), + DIRECTORY_SIGNATURE("directory-signature"), + DIRREQ_READ_HISTORY("dirreq-read-history"), + DIRREQ_STATS_END("dirreq-stats-end"), + DIRREQ_V2_DIRECT_DL("dirreq-v2-direct-dl"), + DIRREQ_V2_IPS("dirreq-v2-ips"), + DIRREQ_V2_REQS("dirreq-v2-reqs"), + DIRREQ_V2_RESP("dirreq-v2-resp"), + DIRREQ_V2_SHARE("dirreq-v2-share"), + DIRREQ_V2_TUNNELED_DL("dirreq-v2-tunneled-dl"), + DIRREQ_V3_DIRECT_DL("dirreq-v3-direct-dl"), + DIRREQ_V3_IPS("dirreq-v3-ips"), + DIRREQ_V3_REQS("dirreq-v3-reqs"), + DIRREQ_V3_RESP("dirreq-v3-resp"), + DIRREQ_V3_SHARE("dirreq-v3-share"), + DIRREQ_V3_TUNNELED_DL("dirreq-v3-tunneled-dl"), + DIRREQ_WRITE_HISTORY("dirreq-write-history"), + DIR_ADDRESS("dir-address"), + DIR_IDENTITY_KEY("dir-identity-key"), + DIR_KEY_CERTIFICATE_VERSION("dir-key-certificate-version"), + DIR_KEY_CERTIFICATION("dir-key-certification"), + DIR_KEY_CROSSCERT("dir-key-crosscert"), + DIR_KEY_EXPIRES("dir-key-expires"), + DIR_KEY_PUBLISHED("dir-key-published"), + DIR_OPTIONS("dir-options"), + DIR_SIGNING_KEY("dir-signing-key"), + DIR_SOURCE("dir-source"), + ENTRY_IPS("entry-ips"), + ENTRY_STATS_END("entry-stats-end"), + EVENTDNS("eventdns"), + EXIT_KIBIBYTES_READ("exit-kibibytes-read"), + EXIT_KIBIBYTES_WRITTEN("exit-kibibytes-written"), + EXIT_STATS_END("exit-stats-end"), + EXIT_STREAMS_OPENED("exit-streams-opened"), + EXTRA_INFO("extra-info"), + EXTRA_INFO_DIGEST("extra-info-digest"), + FAMILY("family"), + FINGERPRINT("fingerprint"), + FLAG_THRESHOLDS("flag-thresholds"), + FRESH_UNTIL("fresh-until"), + GEOIP6_DB_DIGEST("geoip6-db-digest"), + GEOIP_CLIENT_ORIGINS("geoip-client-origins"), + GEOIP_DB_DIGEST("geoip-db-digest"), + GEOIP_START_TIME("geoip-start-time"), + HIBERNATING("hibernating"), + HIDDEN_SERVICE_DIR("hidden-service-dir"), + HIDSERV_DIR_ONIONS_SEEN("hidserv-dir-onions-seen"), + HIDSERV_REND_RELAYED_CELLS("hidserv-rend-relayed-cells"), + HIDSERV_STATS_END("hidserv-stats-end"), + ID("id"), + IDENTITY_ED25519("identity-ed25519"), + IPV6_POLICY("ipv6-policy"), + KNOWN_FLAGS("known-flags"), + LEGACY_DIR_KEY("legacy-dir-key"), + LEGACY_KEY("legacy-key"), + M("m"), + MASTER_KEY_ED25519("master-key-ed25519"), + NETWORK_STATUS_VERSION("network-status-version"), + NTOR_ONION_KEY("ntor-onion-key"), + NTOR_ONION_KEY_CROSSCERT("ntor-onion-key-crosscert"), + ONION_KEY("onion-key"), + ONION_KEY_CROSSCERT("onion-key-crosscert"), + OPT("opt"), + OR_ADDRESS("or-address"), + P("p"), + P6("p6"), + PACKAGE("package"), + PADDING_COUNTS("padding-counts"), + PARAMS("params"), + PLATFORM("platform"), + PR("pr"), + PROTO("proto"), + PROTOCOLS("protocols"), + PUBLISHED("published"), + R("r"), + READ_HISTORY("read-history"), + RECOMMENDED_CLIENT_PROTOCOLS("recommended-client-protocols"), + RECOMMENDED_RELAY_PROTOCOLS("recommended-relay-protocols"), + RECOMMENDED_SOFTWARE("recommended-software"), + REJECT("reject"), + REQUIRED_CLIENT_PROTOCOLS("required-client-protocols"), + REQUIRED_RELAY_PROTOCOLS("required-relay-protocols"), + ROUTER("router"), + ROUTER_DIGEST("router-digest"), + ROUTER_DIGEST_SHA256("router-digest-sha256"), + ROUTER_SIGNATURE("router-signature"), + ROUTER_SIG_ED25519("router-sig-ed25519"), + ROUTER_STATUS("router-status"), + RUNNING_ROUTERS("running-routers"), + S("s"), + SERVER_VERSIONS("server-versions"), + SHARED_RAND_COMMIT("shared-rand-commit"), + SHARED_RAND_CURRENT_VALUE("shared-rand-current-value"), + SHARED_RAND_PARTICIPATE("shared-rand-participate"), + SHARED_RAND_PREVIOUS_VALUE("shared-rand-previous-value"), + SIGNED_DIRECTORY("signed-directory"), + SIGNING_KEY("signing-key"), + TRANSPORT("transport"), + TUNNELLED_DIR_SERVER("tunnelled-dir-server"), + UPTIME("uptime"), + V("v"), + VALID_AFTER("valid-after"), + VALID_UNTIL("valid-until"), + VOTE_DIGEST("vote-digest"), + VOTE_STATUS("vote-status"), + VOTING_DELAY("voting-delay"), + W("w"), + WRITE_HISTORY("write-history"); + + /** The keyword as it appears in descriptors. */ + public final String keyword; + + private static final Map<String, Key> keywordMap = new HashMap<>(); + static { + for (Key key : values()) { + keywordMap.put(key.keyword, key); + } + keywordMap.remove(INVALID.keyword); + keywordMap.remove(EMPTY.keyword); + } + + private Key(String keyword) { + this.keyword = keyword; + } + + /** Retrieve a Key for a keyword. + * Returns Key.INVALID for non-existing keywords. */ + public static Key get(String keyword) { + Key res = INVALID; + try { + res = keywordMap.get(keyword); + } catch (Throwable th) { + res = INVALID; + } + if (null == res) { + res = INVALID; + } + return res; + } +} diff --git a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java index 5c94371..e8329cc 100644 --- a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java @@ -11,7 +11,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -28,7 +28,7 @@ public class MicrodescriptorImpl extends DescriptorImpl List<Microdescriptor> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "onion-key\n"); + Key.ONION_KEY + NL); for (byte[] descriptorBytes : splitDescriptorsBytes) { Microdescriptor parsedDescriptor = new MicrodescriptorImpl(descriptorBytes, @@ -44,21 +44,19 @@ public class MicrodescriptorImpl extends DescriptorImpl 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(); + this.checkExactlyOnceKeys(EnumSet.of(Key.ONION_KEY)); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.NTOR_ONION_KEY, Key.FAMILY, Key.P, Key.P6, Key.ID); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkFirstKey(Key.ONION_KEY); + this.clearParsedKeys(); return; }
private void parseDescriptorBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.rawDescriptorBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); @@ -66,49 +64,49 @@ public class MicrodescriptorImpl extends DescriptorImpl continue; } String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "onion-key": + Key key = Key.get(parts[0]); + switch (key) { + case ONION_KEY: this.parseOnionKeyLine(line, parts); - nextCrypto = "onion-key"; + nextCrypto = key; break; - case "ntor-onion-key": + case NTOR_ONION_KEY: this.parseNtorOnionKeyLine(line, parts); break; - case "a": + case A: this.parseALine(line, parts); break; - case "family": + case FAMILY: this.parseFamilyLine(line, parts); break; - case "p": + case P: this.parsePLine(line, parts); break; - case "p6": + case P6: this.parseP6Line(line, parts); break; - case "id": + case ID: this.parseIdLine(line, parts); break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; - if (nextCrypto.equals("onion-key")) { + if (nextCrypto.equals(Key.ONION_KEY)) { this.onionKey = cryptoString; } else { throw new DescriptorParseException("Unrecognized crypto " + "block in microdescriptor."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else { ParseHelper.parseKeyword(line, parts[0]); if (this.failUnrecognizedDescriptorLines) { diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java index 152546a..cb4eca8 100644 --- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java @@ -3,11 +3,15 @@
package org.torproject.descriptor.impl;
+import static org.torproject.descriptor.impl.DescriptorImpl.NL; +import static org.torproject.descriptor.impl.DescriptorImpl.SP; + import org.torproject.descriptor.DescriptorParseException; import org.torproject.descriptor.NetworkStatusEntry;
import java.util.ArrayList; import java.util.BitSet; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -46,34 +50,25 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry { this.microdescConsensus = microdescConsensus; this.failUnrecognizedDescriptorLines = failUnrecognizedDescriptorLines; - this.initializeKeywords(); this.parseStatusEntryBytes(); - this.clearAtMostOnceKeywords(); + this.clearAtMostOnceKeys(); }
- private SortedSet<String> atMostOnceKeywords; - - private void initializeKeywords() { - this.atMostOnceKeywords = new TreeSet<>(); - this.atMostOnceKeywords.add("s"); - this.atMostOnceKeywords.add("v"); - this.atMostOnceKeywords.add("pr"); - this.atMostOnceKeywords.add("w"); - this.atMostOnceKeywords.add("p"); - } + private Set<Key> atMostOnceKeys = EnumSet.of( + Key.S, Key.V, Key.PR, Key.W, Key.P);
- private void parsedAtMostOnceKeyword(String keyword) + private void parsedAtMostOnceKey(Key key) throws DescriptorParseException { - if (!this.atMostOnceKeywords.contains(keyword)) { - throw new DescriptorParseException("Duplicate '" + keyword + if (!this.atMostOnceKeys.contains(key)) { + throw new DescriptorParseException("Duplicate '" + key.keyword + "' line in status entry."); } - this.atMostOnceKeywords.remove(keyword); + this.atMostOnceKeys.remove(key); }
private void parseStatusEntryBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.statusEntryBytes)) - .useDelimiter("\n"); + .useDelimiter(NL); String line = null; if (!scanner.hasNext() || !(line = scanner.next()).startsWith("r ")) { throw new DescriptorParseException("Status entry must start with " @@ -83,32 +78,33 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry { this.parseRLine(line, rlineParts); while (scanner.hasNext()) { line = scanner.next(); - String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") - : line.substring("opt ".length()).split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "a": + String[] parts = !line.startsWith(Key.OPT.keyword + SP) + ? line.split("[ \t]+") + : line.substring(Key.OPT.keyword.length() + 1).split("[ \t]+"); + Key key = Key.get(parts[0]); + switch (key) { + case A: this.parseALine(line, parts); break; - case "s": + case S: this.parseSLine(line, parts); break; - case "v": + case V: this.parseVLine(line, parts); break; - case "pr": + case PR: this.parsePrLine(line, parts); break; - case "w": + case W: this.parseWLine(line, parts); break; - case "p": + case P: this.parsePLine(line, parts); break; - case "m": + case M: this.parseMLine(line, parts); break; - case "id": + case ID: this.parseIdLine(line, parts); break; default: @@ -167,7 +163,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parseSLine(String line, String[] parts) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("s"); + this.parsedAtMostOnceKey(Key.S); BitSet flags = new BitSet(flagIndexes.size()); for (int i = 1; i < parts.length; i++) { String flag = parts[i]; @@ -182,9 +178,9 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parseVLine(String line, String[] parts) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("v"); + this.parsedAtMostOnceKey(Key.V); String noOptLine = line; - if (noOptLine.startsWith("opt ")) { + if (noOptLine.startsWith(Key.OPT.keyword + SP)) { noOptLine = noOptLine.substring(4); } if (noOptLine.length() < 3) { @@ -197,13 +193,13 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parsePrLine(String line, String[] parts) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("pr"); + this.parsedAtMostOnceKey(Key.PR); this.protocols = ParseHelper.parseProtocolVersions(line, line, parts); }
private void parseWLine(String line, String[] parts) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("w"); + this.parsedAtMostOnceKey(Key.W); SortedMap<String, Integer> pairs = ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "="); if (pairs.isEmpty()) { @@ -225,7 +221,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private void parsePLine(String line, String[] parts) throws DescriptorParseException { - this.parsedAtMostOnceKeyword("p"); + this.parsedAtMostOnceKey(Key.P); boolean isValid = true; if (parts.length != 3) { isValid = false; @@ -280,8 +276,8 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry { } }
- private void clearAtMostOnceKeywords() { - this.atMostOnceKeywords = null; + private void clearAtMostOnceKeys() { + this.atMostOnceKeys = null; }
private String nickname; diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java index e3891a6..f67c32a 100644 --- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java @@ -34,15 +34,16 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { throw new DescriptorParseException("Descriptor is empty."); } String descriptorString = new String(rawDescriptorBytes); - int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r"); + int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, + Key.R.keyword); int endIndex = descriptorString.length(); int firstDirectorySignatureIndex = this.findFirstIndexOfKeyword( - descriptorString, "directory-signature"); + descriptorString, Key.DIRECTORY_SIGNATURE.keyword); if (firstDirectorySignatureIndex < 0) { firstDirectorySignatureIndex = endIndex; } int directoryFooterIndex = this.findFirstIndexOfKeyword( - descriptorString, "directory-footer"); + descriptorString, Key.DIRECTORY_FOOTER.keyword); if (directoryFooterIndex < 0) { directoryFooterIndex = firstDirectorySignatureIndex; } @@ -50,7 +51,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { firstRIndex = directoryFooterIndex; } int firstDirSourceIndex = !containsDirSourceEntries ? -1 - : this.findFirstIndexOfKeyword(descriptorString, "dir-source"); + : this.findFirstIndexOfKeyword(descriptorString, + Key.DIR_SOURCE.keyword); if (firstDirSourceIndex < 0) { firstDirSourceIndex = firstRIndex; } @@ -79,10 +81,10 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { 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 if (descriptorString.contains(NL + keyword + SP)) { + return descriptorString.indexOf(NL + keyword + SP) + 1; + } else if (descriptorString.contains(NL + keyword + NL)) { + return descriptorString.indexOf(NL + keyword + NL) + 1; } else { return -1; } @@ -99,7 +101,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { private void parseDirSourceBytes(String descriptorString, int start, int end) throws DescriptorParseException { List<byte[]> splitDirSourceBytes = - this.splitByKeyword(descriptorString, "dir-source", start, end); + this.splitByKeyword( + descriptorString, Key.DIR_SOURCE.keyword, start, end); for (byte[] dirSourceBytes : splitDirSourceBytes) { this.parseDirSource(dirSourceBytes); } @@ -108,7 +111,7 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { private void parseStatusEntryBytes(String descriptorString, int start, int end) throws DescriptorParseException { List<byte[]> splitStatusEntryBytes = - this.splitByKeyword(descriptorString, "r", start, end); + this.splitByKeyword(descriptorString, Key.R.keyword, start, end); for (byte[] statusEntryBytes : splitStatusEntryBytes) { this.parseStatusEntry(statusEntryBytes); } @@ -125,7 +128,7 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { private void parseDirectorySignatureBytes(String descriptorString, int start, int end) throws DescriptorParseException { List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword( - descriptorString, "directory-signature", start, end); + descriptorString, Key.DIRECTORY_SIGNATURE.keyword, start, end); for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) { this.parseDirectorySignature(directorySignatureBytes); } @@ -136,9 +139,9 @@ public abstract class NetworkStatusImpl extends DescriptorImpl { List<byte[]> splitParts = new ArrayList<>(); int from = start; while (from < end) { - int to = descriptorString.indexOf("\n" + keyword + " ", from); + int to = descriptorString.indexOf(NL + keyword + SP, from); if (to < 0) { - to = descriptorString.indexOf("\n" + keyword + "\n", from); + to = descriptorString.indexOf(NL + keyword + NL, from); } if (to < 0) { to = end; diff --git a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java index fc8f07b..fae54f4 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java @@ -12,8 +12,7 @@ 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.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -29,7 +28,7 @@ public class RelayDirectoryImpl extends DescriptorImpl List<RelayDirectory> parsedDirectories = new ArrayList<>(); List<byte[]> splitDirectoriesBytes = DescriptorImpl.splitRawDescriptorBytes(directoriesBytes, - "signed-directory\n"); + Key.SIGNED_DIRECTORY.keyword + NL); for (byte[] directoryBytes : splitDirectoriesBytes) { RelayDirectory parsedDirectory = new RelayDirectoryImpl(directoryBytes, @@ -45,28 +44,28 @@ public class RelayDirectoryImpl extends DescriptorImpl 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(); + Set<Key> exactlyOnceKeys = EnumSet.of( + Key.SIGNED_DIRECTORY, Key.RECOMMENDED_SOFTWARE, + Key.DIRECTORY_SIGNATURE); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.DIR_SIGNING_KEY, Key.RUNNING_ROUTERS, Key.ROUTER_STATUS); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkFirstKey(Key.SIGNED_DIRECTORY); + this.clearParsedKeys(); }
private void calculateDigest() throws DescriptorParseException { try { String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII"); - String startToken = "signed-directory\n"; - String sigToken = "\ndirectory-signature "; + String startToken = Key.SIGNED_DIRECTORY.keyword + NL; + String sigToken = NL + Key.DIRECTORY_SIGNATURE.keyword + SP; if (!ascii.contains(sigToken)) { return; } int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); - sig = ascii.indexOf("\n", sig) + 1; + sig = ascii.indexOf(NL, sig) + 1; if (start >= 0 && sig >= 0 && sig > start) { byte[] forDigest = new byte[sig - start]; System.arraycopy(this.getRawDescriptorBytes(), start, @@ -94,9 +93,9 @@ public class RelayDirectoryImpl extends DescriptorImpl String descriptorString = new String(rawDescriptorBytes); int startIndex = 0; int firstRouterIndex = this.findFirstIndexOfKeyword(descriptorString, - "router"); + Key.ROUTER.keyword); int directorySignatureIndex = this.findFirstIndexOfKeyword( - descriptorString, "directory-signature"); + descriptorString, Key.DIRECTORY_SIGNATURE.keyword); int endIndex = descriptorString.length(); if (directorySignatureIndex < 0) { directorySignatureIndex = endIndex; @@ -122,10 +121,10 @@ public class RelayDirectoryImpl extends DescriptorImpl 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 if (descriptorString.contains(NL + keyword + SP)) { + return descriptorString.indexOf(NL + keyword + SP) + 1; + } else if (descriptorString.contains(NL + keyword + NL)) { + return descriptorString.indexOf(NL + keyword + NL) + 1; } else { return -1; } @@ -142,7 +141,7 @@ public class RelayDirectoryImpl extends DescriptorImpl private void parseServerDescriptorBytes(String descriptorString, int start, int end) throws DescriptorParseException { List<byte[]> splitServerDescriptorBytes = - this.splitByKeyword(descriptorString, "router", start, end); + this.splitByKeyword(descriptorString, Key.ROUTER.keyword, start, end); for (byte[] statusEntryBytes : splitServerDescriptorBytes) { this.parseServerDescriptor(statusEntryBytes); } @@ -162,9 +161,9 @@ public class RelayDirectoryImpl extends DescriptorImpl List<byte[]> splitParts = new ArrayList<>(); int from = start; while (from < end) { - int to = descriptorString.indexOf("\n" + keyword + " ", from); + int to = descriptorString.indexOf(NL + keyword + SP, from); if (to < 0) { - to = descriptorString.indexOf("\n" + keyword + "\n", from); + to = descriptorString.indexOf(NL + keyword + NL, from); } if (to < 0) { to = end; @@ -187,9 +186,9 @@ public class RelayDirectoryImpl extends DescriptorImpl
private void parseHeader(byte[] headerBytes) throws DescriptorParseException { - Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter("\n"); + Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter(NL); String publishedLine = null; - String nextCrypto = ""; + Key nextCrypto = Key.EMPTY; String runningRoutersLine = null; String routerStatusLine = null; StringBuilder crypto = null; @@ -198,15 +197,15 @@ public class RelayDirectoryImpl extends DescriptorImpl if (line.isEmpty() || line.startsWith("@")) { continue; } - String lineNoOpt = line.startsWith("opt ") - ? line.substring("opt ".length()) : line; + String lineNoOpt = line.startsWith(Key.OPT.keyword + SP) + ? line.substring(Key.OPT.keyword.length() + 1) : line; String[] partsNoOpt = lineNoOpt.split("[ \t]+"); - String keyword = partsNoOpt[0]; - switch (keyword) { - case "signed-directory": + Key key = Key.get(partsNoOpt[0]); + switch (key) { + case SIGNED_DIRECTORY: this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt); break; - case "published": + case PUBLISHED: if (publishedLine != null) { throw new DescriptorParseException("Keyword 'published' is " + "contained more than once, but must be contained " @@ -215,39 +214,39 @@ public class RelayDirectoryImpl extends DescriptorImpl publishedLine = line; } break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "dir-signing-key"; + nextCrypto = key; break; - case "recommended-software": + case RECOMMENDED_SOFTWARE: this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt); break; - case "running-routers": + case RUNNING_ROUTERS: runningRoutersLine = line; break; - case "router-status": + case ROUTER_STATUS: routerStatusLine = line; break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; - if (nextCrypto.equals("dir-signing-key") + if (nextCrypto.equals(Key.DIR_SIGNING_KEY) && this.dirSigningKey == null) { this.dirSigningKey = cryptoString; } else { throw new DescriptorParseException("Unrecognized crypto " + "block in v1 directory."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else { if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" @@ -265,15 +264,17 @@ public class RelayDirectoryImpl extends DescriptorImpl 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 publishedLineNoOpt = publishedLine.startsWith(Key.OPT.keyword + SP) + ? publishedLine.substring(Key.OPT.keyword.length() + 1) + : publishedLine; String[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+"); this.parsePublishedLine(publishedLine, publishedLineNoOpt, publishedPartsNoOpt); } if (routerStatusLine != null) { - String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") - ? routerStatusLine.substring("opt ".length()) + String routerStatusLineNoOpt = + routerStatusLine.startsWith(Key.OPT.keyword + SP) + ? routerStatusLine.substring(Key.OPT.keyword.length() + 1) : routerStatusLine; String[] routerStatusPartsNoOpt = routerStatusLineNoOpt.split("[ \t]+"); @@ -281,8 +282,8 @@ public class RelayDirectoryImpl extends DescriptorImpl routerStatusPartsNoOpt); } else if (runningRoutersLine != null) { String runningRoutersLineNoOpt = - runningRoutersLine.startsWith("opt ") - ? runningRoutersLine.substring("opt ".length()) + runningRoutersLine.startsWith(Key.OPT.keyword + SP) + ? runningRoutersLine.substring(Key.OPT.keyword.length() + 1) : runningRoutersLine; String[] runningRoutersPartsNoOpt = runningRoutersLineNoOpt.split("[ \t]+"); @@ -308,39 +309,39 @@ public class RelayDirectoryImpl extends DescriptorImpl private void parseDirectorySignature(byte[] directorySignatureBytes) throws DescriptorParseException { Scanner scanner = new Scanner(new String(directorySignatureBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); - String lineNoOpt = line.startsWith("opt ") - ? line.substring("opt ".length()) : line; + String lineNoOpt = line.startsWith(Key.OPT.keyword + SP) + ? line.substring(Key.OPT.keyword.length() + 1) : line; String[] partsNoOpt = lineNoOpt.split("[ \t]+"); - String keyword = partsNoOpt[0]; - switch (keyword) { - case "directory-signature": + Key key = Key.get(partsNoOpt[0]); + switch (key) { + case DIRECTORY_SIGNATURE: this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "directory-signature"; + nextCrypto = key; break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; - if (nextCrypto.equals("directory-signature")) { + if (nextCrypto.equals(Key.DIRECTORY_SIGNATURE)) { this.directorySignature = cryptoString; } else { throw new DescriptorParseException("Unrecognized crypto " + "block in v2 network status."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" + line + "' in v2 network status."); @@ -356,7 +357,7 @@ public class RelayDirectoryImpl extends DescriptorImpl
private void parseSignedDirectoryLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("signed-directory")) { + if (!lineNoOpt.equals(Key.SIGNED_DIRECTORY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } } @@ -379,11 +380,11 @@ public class RelayDirectoryImpl extends DescriptorImpl sb.append("-----BEGIN RSA PUBLIC KEY-----\n"); String keyString = partsNoOpt[1]; while (keyString.length() > 64) { - sb.append(keyString.substring(0, 64)).append("\n"); + sb.append(keyString.substring(0, 64)).append(NL); keyString = keyString.substring(64); } if (keyString.length() > 0) { - sb.append(keyString).append("\n"); + sb.append(keyString).append(NL); } sb.append("-----END RSA PUBLIC KEY-----\n"); this.dirSigningKey = sb.toString(); diff --git a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java index 3ec4869..6ee86b1 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java @@ -19,7 +19,7 @@ public class RelayExtraInfoDescriptorImpl List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "extra-info "); + Key.EXTRA_INFO.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { ExtraInfoDescriptor parsedDescriptor = new RelayExtraInfoDescriptorImpl(descriptorBytes, diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java index ef135d4..4570931 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java @@ -11,7 +11,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -33,7 +33,7 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl new ArrayList<>(); List<byte[]> splitConsensusBytes = DescriptorImpl.splitRawDescriptorBytes(consensusesBytes, - "network-status-version 3"); + Key.NETWORK_STATUS_VERSION.keyword + SP + "3"); for (byte[] consensusBytes : splitConsensusBytes) { RelayNetworkStatusConsensus parsedConsensus = new RelayNetworkStatusConsensusImpl(consensusBytes, @@ -47,27 +47,27 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl 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,recommended-client-protocols," - + "recommended-relay-protocols,required-client-protocols," - + "required-relay-protocols,params,shared-rand-previous-value," - + "shared-rand-current-value,directory-footer,bandwidth-weights") - .split(","))); - this.checkAtMostOnceKeywords(atMostOnceKeywords); - this.checkFirstKeyword("network-status-version"); - this.clearParsedKeywords(); + Set<Key> exactlyOnceKeys = EnumSet.of( + Key.VOTE_STATUS, Key.CONSENSUS_METHOD, Key.VALID_AFTER, Key.FRESH_UNTIL, + Key.VALID_UNTIL, Key.VOTING_DELAY, Key.KNOWN_FLAGS); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.CLIENT_VERSIONS, Key.SERVER_VERSIONS, + Key.RECOMMENDED_CLIENT_PROTOCOLS, Key.RECOMMENDED_RELAY_PROTOCOLS, + Key.REQUIRED_CLIENT_PROTOCOLS, Key.REQUIRED_RELAY_PROTOCOLS, Key.PARAMS, + Key.SHARED_RAND_PREVIOUS_VALUE, Key.SHARED_RAND_CURRENT_VALUE, + Key.DIRECTORY_FOOTER, Key.BANDWIDTH_WEIGHTS); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkFirstKey(Key.NETWORK_STATUS_VERSION); + this.clearParsedKeys(); 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 "; + String startToken = Key.NETWORK_STATUS_VERSION.keyword + SP; + String sigToken = NL + Key.DIRECTORY_SIGNATURE.keyword + SP; if (!ascii.contains(sigToken)) { return; } @@ -94,64 +94,64 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
protected void parseHeader(byte[] headerBytes) throws DescriptorParseException { - Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter("\n"); + Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "network-status-version": + Key key = Key.get(parts[0]); + switch (key) { + case NETWORK_STATUS_VERSION: this.parseNetworkStatusVersionLine(line, parts); break; - case "vote-status": + case VOTE_STATUS: this.parseVoteStatusLine(line, parts); break; - case "consensus-method": + case CONSENSUS_METHOD: this.parseConsensusMethodLine(line, parts); break; - case "valid-after": + case VALID_AFTER: this.parseValidAfterLine(line, parts); break; - case "fresh-until": + case FRESH_UNTIL: this.parseFreshUntilLine(line, parts); break; - case "valid-until": + case VALID_UNTIL: this.parseValidUntilLine(line, parts); break; - case "voting-delay": + case VOTING_DELAY: this.parseVotingDelayLine(line, parts); break; - case "client-versions": + case CLIENT_VERSIONS: this.parseClientVersionsLine(line, parts); break; - case "server-versions": + case SERVER_VERSIONS: this.parseServerVersionsLine(line, parts); break; - case "recommended-client-protocols": + case RECOMMENDED_CLIENT_PROTOCOLS: this.parseRecommendedClientProtocolsLine(line, parts); break; - case "recommended-relay-protocols": + case RECOMMENDED_RELAY_PROTOCOLS: this.parseRecommendedRelayProtocolsLine(line, parts); break; - case "required-client-protocols": + case REQUIRED_CLIENT_PROTOCOLS: this.parseRequiredClientProtocolsLine(line, parts); break; - case "required-relay-protocols": + case REQUIRED_RELAY_PROTOCOLS: this.parseRequiredRelayProtocolsLine(line, parts); break; - case "package": + case PACKAGE: this.parsePackageLine(line, parts); break; - case "known-flags": + case KNOWN_FLAGS: this.parseKnownFlagsLine(line, parts); break; - case "params": + case PARAMS: this.parseParamsLine(line, parts); break; - case "shared-rand-previous-value": + case SHARED_RAND_PREVIOUS_VALUE: this.parseSharedRandPreviousValueLine(line, parts); break; - case "shared-rand-current-value": + case SHARED_RAND_CURRENT_VALUE: this.parseSharedRandCurrentValueLine(line, parts); break; default: @@ -188,15 +188,15 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
protected void parseFooter(byte[] footerBytes) throws DescriptorParseException { - Scanner scanner = new Scanner(new String(footerBytes)).useDelimiter("\n"); + Scanner scanner = new Scanner(new String(footerBytes)).useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "directory-footer": + Key key = Key.get(parts[0]); + switch (key) { + case DIRECTORY_FOOTER: break; - case "bandwidth-weights": + case BANDWIDTH_WEIGHTS: this.parseBandwidthWeightsLine(line, parts); break; default: @@ -215,7 +215,7 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
private void parseNetworkStatusVersionLine(String line, String[] parts) throws DescriptorParseException { - if (!line.startsWith("network-status-version 3")) { + if (!line.startsWith(Key.NETWORK_STATUS_VERSION.keyword + SP + "3")) { throw new DescriptorParseException("Illegal network status version " + "number in line '" + line + "'."); } @@ -335,7 +335,7 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl if (this.packageLines == null) { this.packageLines = new ArrayList<>(); } - this.packageLines.add(line.substring("package ".length())); + this.packageLines.add(line.substring(Key.PACKAGE.keyword.length() + 1)); }
private void parseKnownFlagsLine(String line, String[] parts) diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java index 143540c..121cdc9 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java @@ -11,7 +11,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -42,29 +42,29 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl 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(); + Set<Key> exactlyOnceKeys = EnumSet.of( + Key.NETWORK_STATUS_VERSION, Key.DIR_SOURCE, Key.FINGERPRINT, + Key.CONTACT, Key.DIR_SIGNING_KEY, Key.PUBLISHED); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.DIR_OPTIONS, Key.CLIENT_VERSIONS, Key.SERVER_VERSIONS); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkFirstKey(Key.NETWORK_STATUS_VERSION); + this.clearParsedKeys(); 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 "; + String startToken = Key.NETWORK_STATUS_VERSION.keyword + SP; + String sigToken = NL + Key.DIRECTORY_SIGNATURE.keyword + SP; if (!ascii.contains(sigToken)) { return; } int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); - sig = ascii.indexOf("\n", sig) + 1; + sig = ascii.indexOf(NL, sig) + 1; if (start >= 0 && sig >= 0 && sig > start) { byte[] forDigest = new byte[sig - start]; System.arraycopy(this.getRawDescriptorBytes(), start, @@ -86,8 +86,8 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
protected void parseHeader(byte[] headerBytes) throws DescriptorParseException { - Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter("\n"); - String nextCrypto = ""; + Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); @@ -95,55 +95,55 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl continue; } String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "network-status-version": + Key key = Key.get(parts[0]); + switch (key) { + case NETWORK_STATUS_VERSION: this.parseNetworkStatusVersionLine(line, parts); break; - case "dir-source": + case DIR_SOURCE: this.parseDirSourceLine(line, parts); break; - case "fingerprint": + case FINGERPRINT: this.parseFingerprintLine(line, parts); break; - case "contact": + case CONTACT: this.parseContactLine(line, parts); break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.parseDirSigningKeyLine(line, parts); - nextCrypto = "dir-signing-key"; + nextCrypto = key; break; - case "client-versions": + case CLIENT_VERSIONS: this.parseClientVersionsLine(line, parts); break; - case "server-versions": + case SERVER_VERSIONS: this.parseServerVersionsLine(line, parts); break; - case "published": + case PUBLISHED: this.parsePublishedLine(line, parts); break; - case "dir-options": + case DIR_OPTIONS: this.parseDirOptionsLine(line, parts); break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; - if (nextCrypto.equals("dir-signing-key")) { + if (nextCrypto.equals(Key.DIR_SIGNING_KEY)) { this.dirSigningKey = cryptoString; } else { throw new DescriptorParseException("Unrecognized crypto " + "block in v2 network status."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" + line + "' in v2 network status."); @@ -166,37 +166,37 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl protected void parseDirectorySignature(byte[] directorySignatureBytes) throws DescriptorParseException { Scanner scanner = new Scanner(new String(directorySignatureBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "directory-signature": + Key key = Key.get(parts[0]); + switch (key) { + case DIRECTORY_SIGNATURE: this.parseDirectorySignatureLine(line, parts); - nextCrypto = "directory-signature"; + nextCrypto = key; break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; - if (nextCrypto.equals("directory-signature")) { + if (nextCrypto.equals(Key.DIRECTORY_SIGNATURE)) { this.directorySignature = cryptoString; } else { throw new DescriptorParseException("Unrecognized crypto " + "block in v2 network status."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" + line + "' in v2 network status."); @@ -212,7 +212,7 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
private void parseNetworkStatusVersionLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("network-status-version 2")) { + if (!line.equals(Key.NETWORK_STATUS_VERSION.keyword + SP + "2")) { throw new DescriptorParseException("Illegal network status version " + "number in line '" + line + "'."); } @@ -246,8 +246,8 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
private void parseContactLine(String line, String[] parts) throws DescriptorParseException { - if (line.length() > "contact ".length()) { - this.contactLine = line.substring("contact ".length()); + if (line.length() > Key.CONTACT.keyword.length() + 1) { + this.contactLine = line.substring(Key.CONTACT.keyword.length() + 1); } else { this.contactLine = ""; } @@ -255,7 +255,7 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
private void parseDirSigningKeyLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-signing-key")) { + if (!line.equals(Key.DIR_SIGNING_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } } diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java index 05f5cc2..c645928 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java @@ -12,7 +12,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Scanner; @@ -33,7 +33,7 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl List<RelayNetworkStatusVote> parsedVotes = new ArrayList<>(); List<byte[]> splitVotesBytes = DescriptorImpl.splitRawDescriptorBytes(votesBytes, - "network-status-version 3"); + Key.NETWORK_STATUS_VERSION.keyword + SP + "3"); for (byte[] voteBytes : splitVotesBytes) { RelayNetworkStatusVote parsedVote = new RelayNetworkStatusVoteImpl(voteBytes, @@ -47,27 +47,25 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl 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," - + "recommended-client-protocols,recommended-relay-protocols," - + "required-client-protocols,required-relay-protocols," - + "flag-thresholds,params,contact,shared-rand-participate," - + "shared-rand-previous-value,shared-rand-current-value," - + "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(); + Set<Key> exactlyOnceKeys = EnumSet.of( + Key.VOTE_STATUS, Key.PUBLISHED, Key.VALID_AFTER, Key.FRESH_UNTIL, + Key.VALID_UNTIL, Key.VOTING_DELAY, Key.KNOWN_FLAGS, Key.DIR_SOURCE, + Key.DIR_KEY_CERTIFICATE_VERSION, Key.FINGERPRINT, Key.DIR_KEY_PUBLISHED, + Key.DIR_KEY_EXPIRES, Key.DIR_IDENTITY_KEY, Key.DIR_SIGNING_KEY, + Key.DIR_KEY_CERTIFICATION); + this.checkExactlyOnceKeys(exactlyOnceKeys); + Set<Key> atMostOnceKeys = EnumSet.of( + Key.CONSENSUS_METHODS, Key.CLIENT_VERSIONS, Key.SERVER_VERSIONS, + Key.RECOMMENDED_CLIENT_PROTOCOLS, Key.RECOMMENDED_RELAY_PROTOCOLS, + Key.REQUIRED_CLIENT_PROTOCOLS, Key.REQUIRED_RELAY_PROTOCOLS, + Key.FLAG_THRESHOLDS, Key.PARAMS, Key.CONTACT, + Key.SHARED_RAND_PARTICIPATE, Key.SHARED_RAND_PREVIOUS_VALUE, + Key.SHARED_RAND_CURRENT_VALUE, Key.LEGACY_KEY, Key.DIR_KEY_CROSSCERT, + Key.DIR_ADDRESS, Key.DIRECTORY_FOOTER); + this.checkAtMostOnceKeys(atMostOnceKeys); + this.checkAtLeastOnceKeys(EnumSet.of(Key.DIRECTORY_SIGNATURE)); + this.checkFirstKey(Key.NETWORK_STATUS_VERSION); + this.clearParsedKeys(); this.calculateDigest(); }
@@ -116,150 +114,150 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl this.enoughMtbfInfo = -1; this.ignoringAdvertisedBws = -1;
- Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter("\n"); - String nextCrypto = ""; + Scanner scanner = new Scanner(new String(headerBytes)).useDelimiter(NL); + Key nextCrypto = Key.EMPTY; StringBuilder crypto = null; while (scanner.hasNext()) { String line = scanner.next(); String[] parts = line.split("[ \t]+"); - String keyword = parts[0]; - switch (keyword) { - case "network-status-version": + Key key = Key.get(parts[0]); + switch (key) { + case NETWORK_STATUS_VERSION: this.parseNetworkStatusVersionLine(line, parts); break; - case "vote-status": + case VOTE_STATUS: this.parseVoteStatusLine(line, parts); break; - case "consensus-methods": + case CONSENSUS_METHODS: this.parseConsensusMethodsLine(line, parts); break; - case "published": + case PUBLISHED: this.parsePublishedLine(line, parts); break; - case "valid-after": + case VALID_AFTER: this.parseValidAfterLine(line, parts); break; - case "fresh-until": + case FRESH_UNTIL: this.parseFreshUntilLine(line, parts); break; - case "valid-until": + case VALID_UNTIL: this.parseValidUntilLine(line, parts); break; - case "voting-delay": + case VOTING_DELAY: this.parseVotingDelayLine(line, parts); break; - case "client-versions": + case CLIENT_VERSIONS: this.parseClientVersionsLine(line, parts); break; - case "server-versions": + case SERVER_VERSIONS: this.parseServerVersionsLine(line, parts); break; - case "recommended-client-protocols": + case RECOMMENDED_CLIENT_PROTOCOLS: this.parseRecommendedClientProtocolsLine(line, parts); break; - case "recommended-relay-protocols": + case RECOMMENDED_RELAY_PROTOCOLS: this.parseRecommendedRelayProtocolsLine(line, parts); break; - case "required-client-protocols": + case REQUIRED_CLIENT_PROTOCOLS: this.parseRequiredClientProtocolsLine(line, parts); break; - case "required-relay-protocols": + case REQUIRED_RELAY_PROTOCOLS: this.parseRequiredRelayProtocolsLine(line, parts); break; - case "package": + case PACKAGE: this.parsePackageLine(line, parts); break; - case "known-flags": + case KNOWN_FLAGS: this.parseKnownFlagsLine(line, parts); break; - case "flag-thresholds": + case FLAG_THRESHOLDS: this.parseFlagThresholdsLine(line, parts); break; - case "params": + case PARAMS: this.parseParamsLine(line, parts); break; - case "dir-source": + case DIR_SOURCE: this.parseDirSourceLine(line, parts); break; - case "contact": + case CONTACT: this.parseContactLine(line, parts); break; - case "shared-rand-participate": + case SHARED_RAND_PARTICIPATE: this.parseSharedRandParticipateLine(line, parts); break; - case "shared-rand-commit": + case SHARED_RAND_COMMIT: this.parseSharedRandCommitLine(line, parts); break; - case "shared-rand-previous-value": + case SHARED_RAND_PREVIOUS_VALUE: this.parseSharedRandPreviousValueLine(line, parts); break; - case "shared-rand-current-value": + case SHARED_RAND_CURRENT_VALUE: this.parseSharedRandCurrentValueLine(line, parts); break; - case "dir-key-certificate-version": + case DIR_KEY_CERTIFICATE_VERSION: this.parseDirKeyCertificateVersionLine(line, parts); break; - case "dir-address": + case DIR_ADDRESS: this.parseDirAddressLine(line, parts); break; - case "fingerprint": + case FINGERPRINT: this.parseFingerprintLine(line, parts); break; - case "legacy-dir-key": + case LEGACY_DIR_KEY: this.parseLegacyDirKeyLine(line, parts); break; - case "dir-key-published": + case DIR_KEY_PUBLISHED: this.parseDirKeyPublished(line, parts); break; - case "dir-key-expires": + case DIR_KEY_EXPIRES: this.parseDirKeyExpiresLine(line, parts); break; - case "dir-identity-key": + case DIR_IDENTITY_KEY: this.parseDirIdentityKeyLine(line, parts); - nextCrypto = "dir-identity-key"; + nextCrypto = key; break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.parseDirSigningKeyLine(line, parts); - nextCrypto = "dir-signing-key"; + nextCrypto = key; break; - case "dir-key-crosscert": + case DIR_KEY_CROSSCERT: this.parseDirKeyCrosscertLine(line, parts); - nextCrypto = "dir-key-crosscert"; + nextCrypto = key; break; - case "dir-key-certification": + case DIR_KEY_CERTIFICATION: this.parseDirKeyCertificationLine(line, parts); - nextCrypto = "dir-key-certification"; + nextCrypto = key; break; - case "-----BEGIN": + case CRYPTO_BEGIN: crypto = new StringBuilder(); - crypto.append(line).append("\n"); + crypto.append(line).append(NL); break; - case "-----END": - crypto.append(line).append("\n"); + case CRYPTO_END: + crypto.append(line).append(NL); String cryptoString = crypto.toString(); crypto = null; switch (nextCrypto) { - case "dir-identity-key": + case DIR_IDENTITY_KEY: this.dirIdentityKey = cryptoString; break; - case "dir-signing-key": + case DIR_SIGNING_KEY: this.dirSigningKey = cryptoString; break; - case "dir-key-crosscert": + case DIR_KEY_CROSSCERT: this.dirKeyCrosscert = cryptoString; break; - case "dir-key-certification": + case DIR_KEY_CERTIFICATION: this.dirKeyCertification = cryptoString; break; default: throw new DescriptorParseException("Unrecognized crypto " + "block in vote."); } - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; default: if (crypto != null) { - crypto.append(line).append("\n"); + crypto.append(line).append(NL); } else { if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" @@ -277,7 +275,7 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
private void parseNetworkStatusVersionLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("network-status-version 3")) { + if (!line.equals(Key.NETWORK_STATUS_VERSION.keyword + SP + "3")) { throw new DescriptorParseException("Illegal network status version " + "number in line '" + line + "'."); } @@ -399,7 +397,7 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl if (this.packageLines == null) { this.packageLines = new ArrayList<>(); } - this.packageLines.add(line.substring("package ".length())); + this.packageLines.add(line.substring(Key.PACKAGE.keyword.length() + 1)); }
private void parseKnownFlagsLine(String line, String[] parts) @@ -492,8 +490,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
private void parseContactLine(String line, String[] parts) throws DescriptorParseException { - if (line.length() > "contact ".length()) { - this.contactLine = line.substring("contact ".length()); + if (line.length() > Key.CONTACT.keyword.length() + 1) { + this.contactLine = line.substring(Key.CONTACT.keyword.length() + 1); } else { this.contactLine = ""; } @@ -604,38 +602,38 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
private void parseDirIdentityKeyLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-identity-key")) { + if (!line.equals(Key.DIR_IDENTITY_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseDirSigningKeyLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-signing-key")) { + if (!line.equals(Key.DIR_SIGNING_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseDirKeyCrosscertLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-key-crosscert")) { + if (!line.equals(Key.DIR_KEY_CROSSCERT.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseDirKeyCertificationLine(String line, String[] parts) throws DescriptorParseException { - if (!line.equals("dir-key-certification")) { + if (!line.equals(Key.DIR_KEY_CERTIFICATION.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
protected void parseFooter(byte[] footerBytes) throws DescriptorParseException { - Scanner scanner = new Scanner(new String(footerBytes)).useDelimiter("\n"); + Scanner scanner = new Scanner(new String(footerBytes)).useDelimiter(NL); while (scanner.hasNext()) { String line = scanner.next(); - if (!line.equals("directory-footer")) { + if (!line.equals(Key.DIRECTORY_FOOTER.keyword)) { if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" + line + "' in vote."); diff --git a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java index 3353ec1..eefa24f 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java @@ -19,7 +19,7 @@ public class RelayServerDescriptorImpl extends ServerDescriptorImpl List<ServerDescriptor> parsedDescriptors = new ArrayList<>(); List<byte[]> splitDescriptorsBytes = DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes, - "router "); + Key.ROUTER.keyword + SP); for (byte[] descriptorBytes : splitDescriptorsBytes) { ServerDescriptor parsedDescriptor = new RelayServerDescriptorImpl(descriptorBytes, diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java index ac113d2..70bd42c 100644 --- a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java @@ -12,7 +12,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Scanner; import java.util.Set; @@ -25,6 +25,20 @@ import javax.xml.bind.DatatypeConverter; public abstract class ServerDescriptorImpl extends DescriptorImpl implements ServerDescriptor {
+ private static final Set<Key> atMostOnce = EnumSet.of( + Key.IDENTITY_ED25519, Key.MASTER_KEY_ED25519, Key.PLATFORM, Key.PROTO, + Key.FINGERPRINT, Key.HIBERNATING, Key.UPTIME, Key.CONTACT, Key.FAMILY, + Key.READ_HISTORY, Key.WRITE_HISTORY, Key.EVENTDNS, Key.CACHES_EXTRA_INFO, + Key.EXTRA_INFO_DIGEST, Key.HIDDEN_SERVICE_DIR, Key.PROTOCOLS, + Key.ALLOW_SINGLE_HOP_EXITS, Key.ONION_KEY, Key.SIGNING_KEY, + Key.IPV6_POLICY, Key.NTOR_ONION_KEY, Key.ONION_KEY_CROSSCERT, + Key.NTOR_ONION_KEY_CROSSCERT, Key.TUNNELLED_DIR_SERVER, + Key.ROUTER_SIG_ED25519, Key.ROUTER_SIGNATURE, Key.ROUTER_DIGEST_SHA256, + Key.ROUTER_DIGEST); + + private static final Set<Key> exactlyOnce = EnumSet.of( + Key.ROUTER, Key.BANDWIDTH, Key.PUBLISHED); + protected ServerDescriptorImpl(byte[] descriptorBytes, boolean failUnrecognizedDescriptorLines) throws DescriptorParseException { @@ -32,184 +46,173 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl 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,proto,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) { + this.checkExactlyOnceKeys(exactlyOnce); + this.checkAtMostOnceKeys(atMostOnce); + this.checkFirstKey(Key.ROUTER); + if (this.getKeyCount(Key.ACCEPT) == 0 + && this.getKeyCount(Key.REJECT) == 0) { throw new DescriptorParseException("Either keyword 'accept' or " + "'reject' must be contained at least once."); } - this.clearParsedKeywords(); + this.clearParsedKeys(); return; }
private void parseDescriptorBytes() throws DescriptorParseException { Scanner scanner = new Scanner(new String(this.rawDescriptorBytes)) - .useDelimiter("\n"); - String nextCrypto = ""; + .useDelimiter(NL); + Key nextCrypto = Key.EMPTY; List<String> cryptoLines = null; while (scanner.hasNext()) { String line = scanner.next(); if (line.startsWith("@")) { continue; } - String lineNoOpt = line.startsWith("opt ") - ? line.substring("opt ".length()) : line; + String lineNoOpt = line.startsWith(Key.OPT.keyword + SP) + ? line.substring(Key.OPT.keyword.length() + 1) : line; String[] partsNoOpt = lineNoOpt.split("[ \t]+"); - String keyword = partsNoOpt[0]; - switch (keyword) { - case "router": + Key key = Key.get(partsNoOpt[0]); + switch (key) { + case ROUTER: this.parseRouterLine(line, lineNoOpt, partsNoOpt); break; - case "or-address": + case OR_ADDRESS: this.parseOrAddressLine(line, lineNoOpt, partsNoOpt); break; - case "bandwidth": + case BANDWIDTH: this.parseBandwidthLine(line, lineNoOpt, partsNoOpt); break; - case "platform": + case PLATFORM: this.parsePlatformLine(line, lineNoOpt, partsNoOpt); break; - case "proto": + case PROTO: this.parseProtoLine(line, lineNoOpt, partsNoOpt); break; - case "published": + case PUBLISHED: this.parsePublishedLine(line, lineNoOpt, partsNoOpt); break; - case "fingerprint": + case FINGERPRINT: this.parseFingerprintLine(line, lineNoOpt, partsNoOpt); break; - case "hibernating": + case HIBERNATING: this.parseHibernatingLine(line, lineNoOpt, partsNoOpt); break; - case "uptime": + case UPTIME: this.parseUptimeLine(line, lineNoOpt, partsNoOpt); break; - case "onion-key": + case ONION_KEY: this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "onion-key"; + nextCrypto = key; break; - case "signing-key": + case SIGNING_KEY: this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "signing-key"; + nextCrypto = key; break; - case "accept": + case ACCEPT: this.parseAcceptLine(line, lineNoOpt, partsNoOpt); break; - case "reject": + case REJECT: this.parseRejectLine(line, lineNoOpt, partsNoOpt); break; - case "router-signature": + case ROUTER_SIGNATURE: this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt); - nextCrypto = "router-signature"; + nextCrypto = key; break; - case "contact": + case CONTACT: this.parseContactLine(line, lineNoOpt, partsNoOpt); break; - case "family": + case FAMILY: this.parseFamilyLine(line, lineNoOpt, partsNoOpt); break; - case "read-history": + case READ_HISTORY: this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "write-history": + case WRITE_HISTORY: this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt); break; - case "eventdns": + case EVENTDNS: this.parseEventdnsLine(line, lineNoOpt, partsNoOpt); break; - case "caches-extra-info": + case CACHES_EXTRA_INFO: this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt); break; - case "extra-info-digest": + case EXTRA_INFO_DIGEST: this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt); break; - case "hidden-service-dir": + case HIDDEN_SERVICE_DIR: this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt); break; - case "protocols": + case PROTOCOLS: this.parseProtocolsLine(line, lineNoOpt, partsNoOpt); break; - case "allow-single-hop-exits": + case ALLOW_SINGLE_HOP_EXITS: this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt); break; - case "dircacheport": + case DIRCACHEPORT: this.parseDircacheportLine(line, lineNoOpt, partsNoOpt); break; - case "router-digest": + case ROUTER_DIGEST: this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt); break; - case "router-digest-sha256": + case ROUTER_DIGEST_SHA256: this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt); break; - case "ipv6-policy": + case IPV6_POLICY: this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt); break; - case "ntor-onion-key": + case NTOR_ONION_KEY: this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt); break; - case "identity-ed25519": + case IDENTITY_ED25519: this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt); - nextCrypto = "identity-ed25519"; + nextCrypto = key; break; - case "master-key-ed25519": + case MASTER_KEY_ED25519: this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt); break; - case "router-sig-ed25519": + case ROUTER_SIG_ED25519: this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt); break; - case "onion-key-crosscert": + case ONION_KEY_CROSSCERT: this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt); - nextCrypto = "onion-key-crosscert"; + nextCrypto = key; break; - case "ntor-onion-key-crosscert": + case NTOR_ONION_KEY_CROSSCERT: this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt); - nextCrypto = "ntor-onion-key-crosscert"; + nextCrypto = key; break; - case "tunnelled-dir-server": + case TUNNELLED_DIR_SERVER: this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt); break; - case "-----BEGIN": + case CRYPTO_BEGIN: cryptoLines = new ArrayList<>(); cryptoLines.add(line); break; - case "-----END": + case CRYPTO_END: cryptoLines.add(line); StringBuilder sb = new StringBuilder(); for (String cryptoLine : cryptoLines) { - sb.append("\n").append(cryptoLine); + sb.append(NL).append(cryptoLine); } String cryptoString = sb.toString().substring(1); switch (nextCrypto) { - case "onion-key": + case ONION_KEY: this.onionKey = cryptoString; break; - case "signing-key": + case SIGNING_KEY: this.signingKey = cryptoString; break; - case "router-signature": + case ROUTER_SIGNATURE: this.routerSignature = cryptoString; break; - case "identity-ed25519": + case IDENTITY_ED25519: this.identityEd25519 = cryptoString; this.parseIdentityEd25519CryptoBlock(cryptoString); break; - case "onion-key-crosscert": + case ONION_KEY_CROSSCERT: this.onionKeyCrosscert = cryptoString; break; - case "ntor-onion-key-crosscert": + case NTOR_ONION_KEY_CROSSCERT: this.ntorOnionKeyCrosscert = cryptoString; break; default: @@ -224,8 +227,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } } cryptoLines = null; - nextCrypto = ""; + nextCrypto = Key.EMPTY; break; + case INVALID: default: if (cryptoLines != null) { cryptoLines.add(line); @@ -301,8 +305,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parsePlatformLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (lineNoOpt.length() > "platform ".length()) { - this.platform = lineNoOpt.substring("platform ".length()); + if (lineNoOpt.length() > Key.PLATFORM.keyword.length() + 1) { + this.platform = lineNoOpt.substring(Key.PLATFORM.keyword.length() + 1); } else { this.platform = ""; } @@ -322,11 +326,12 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseFingerprintLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (lineNoOpt.length() != "fingerprint".length() + 5 * 10) { + if (lineNoOpt.length() != Key.FINGERPRINT.keyword.length() + 5 * 10) { throw new DescriptorParseException("Illegal line '" + line + "'."); } this.fingerprint = ParseHelper.parseTwentyByteHexString(line, - lineNoOpt.substring("fingerprint ".length()).replaceAll(" ", "")); + lineNoOpt.substring(Key.FINGERPRINT.keyword.length() + 1) + .replaceAll(SP, "")); }
private void parseHibernatingLine(String line, String lineNoOpt, @@ -358,14 +363,14 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseOnionKeyLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("onion-key")) { + if (!lineNoOpt.equals(Key.ONION_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } }
private void parseSigningKeyLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("signing-key")) { + if (!lineNoOpt.equals(Key.SIGNING_KEY.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } } @@ -391,15 +396,15 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseRouterSignatureLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("router-signature")) { + if (!lineNoOpt.equals(Key.ROUTER_SIGNATURE.keyword)) { 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()); + if (lineNoOpt.length() > Key.CONTACT.keyword.length() + 1) { + this.contact = lineNoOpt.substring(Key.CONTACT.keyword.length() + 1); } else { this.contact = ""; } @@ -455,7 +460,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseCachesExtraInfoLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("caches-extra-info")) { + if (!lineNoOpt.equals(Key.CACHES_EXTRA_INFO.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } this.cachesExtraInfo = true; @@ -532,7 +537,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseAllowSingleHopExitsLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("allow-single-hop-exits")) { + if (!lineNoOpt.equals(Key.ALLOW_SINGLE_HOP_EXITS.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } this.allowSingleHopExits = true; @@ -568,9 +573,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl if (partsNoOpt.length != 3) { isValid = false; } else { - switch (partsNoOpt[1]) { - case "accept": - case "reject": + switch (Key.get(partsNoOpt[1])) { + case ACCEPT: + case REJECT: this.ipv6DefaultPolicy = partsNoOpt[1]; this.ipv6PortList = partsNoOpt[2]; String[] ports = partsNoOpt[2].split(",", -1); @@ -581,6 +586,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } } break; + case INVALID: default: isValid = false; } @@ -626,7 +632,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseTunnelledDirServerLine(String line, String lineNoOpt, String[] partsNoOpt) throws DescriptorParseException { - if (!lineNoOpt.equals("tunnelled-dir-server")) { + if (!lineNoOpt.equals(Key.TUNNELLED_DIR_SERVER.keyword)) { throw new DescriptorParseException("Illegal line '" + line + "'."); } this.tunnelledDirServer = true; @@ -684,8 +690,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } try { String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII"); - String startToken = "router "; - String sigToken = "\nrouter-signature\n"; + String startToken = Key.ROUTER.keyword + SP; + String sigToken = NL + Key.ROUTER_SIGNATURE.keyword + NL; int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length(); if (start >= 0 && sig >= 0 && sig > start) { @@ -715,7 +721,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } try { String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII"); - String startToken = "router "; + String startToken = Key.ROUTER.keyword + SP; String sigToken = "\n-----END SIGNATURE-----\n"; int start = ascii.indexOf(startToken); int sig = ascii.indexOf(sigToken) + sigToken.length();