[tor-commits] [metrics-lib/master] Parse new protocol versions lines.

karsten at torproject.org karsten at torproject.org
Tue Feb 14 14:34:14 UTC 2017


commit 2bcd6bb0e4cf8b535cf3b4c4ef0eed83dfc82676
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Mon Feb 13 12:05:47 2017 +0100

    Parse new protocol versions lines.
---
 CHANGELOG.md                                       |  3 +
 .../torproject/descriptor/NetworkStatusEntry.java  |  9 +++
 .../descriptor/RelayNetworkStatusConsensus.java    | 36 +++++++++
 .../descriptor/RelayNetworkStatusVote.java         | 32 ++++++++
 .../torproject/descriptor/ServerDescriptor.java    | 10 +++
 .../descriptor/impl/NetworkStatusEntryImpl.java    | 17 ++++
 .../torproject/descriptor/impl/ParseHelper.java    | 47 +++++++++++
 .../impl/RelayNetworkStatusConsensusImpl.java      | 68 +++++++++++++++-
 .../impl/RelayNetworkStatusVoteImpl.java           | 66 ++++++++++++++++
 .../descriptor/impl/ServerDescriptorImpl.java      | 20 ++++-
 .../descriptor/impl/ConsensusBuilder.java          | 60 ++++++++++++++
 .../impl/RelayNetworkStatusConsensusImplTest.java  | 79 +++++++++++++++++++
 .../impl/RelayNetworkStatusVoteImplTest.java       | 92 ++++++++++++++++++++++
 .../descriptor/impl/ServerDescriptorImplTest.java  | 49 ++++++++++--
 14 files changed, 581 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 755428d..6bcfea9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,9 @@
      the application can decide at any time to stop consuming
      descriptors without having to worry about the reader thread not
      being done.
+   - Parse "proto" lines in server descriptors, "pr" lines in status
+     entries, and "(recommended|required)-(client|relay)-protocols"
+     lines in consensuses and votes.
 
 
 # Changes in version 1.5.0 - 2016-10-19
diff --git a/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
index 68f6939..ba413bb 100644
--- a/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
+++ b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
@@ -5,6 +5,7 @@ package org.torproject.descriptor;
 
 import java.util.List;
 import java.util.Set;
+import java.util.SortedMap;
 import java.util.SortedSet;
 
 /**
@@ -123,6 +124,14 @@ public interface NetworkStatusEntry {
   public String getVersion();
 
   /**
+   * Return the version numbers of all protocols supported by this server, or
+   * null if the status entry does not specify supported protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getProtocols();
+
+  /**
    * Return the bandwidth weight of this server or -1 if the status entry
    * didn't contain a bandwidth line.
    *
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
index a2deba3..b004b66 100644
--- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -113,6 +113,42 @@ public interface RelayNetworkStatusConsensus extends Descriptor {
   public List<String> getRecommendedClientVersions();
 
   /**
+   * Return the version numbers of all protocols that clients should support,
+   * or null if the consensus does not contain an opinion about protocol
+   * versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRecommendedClientProtocols();
+
+  /**
+   * Return the version numbers of all protocols that relays should support,
+   * or null if the consensus does not contain an opinion about protocol
+   * versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRecommendedRelayProtocols();
+
+  /**
+   * Return the version numbers of all protocols that clients must support,
+   * or null if the consensus does not contain an opinion about protocol
+   * versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRequiredClientProtocols();
+
+  /**
+   * Return the version numbers of all protocols that relays must support,
+   * or null if the consensus does not contain an opinion about protocol
+   * versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRequiredRelayProtocols();
+
+  /**
    * Return a list of software packages and their versions together with a
    * URL and one or more digests in the format <code>PackageName Version
    * URL DIGESTS</code> that are known by at least three directory
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
index a3f8170..9ea804d 100644
--- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -105,6 +105,38 @@ public interface RelayNetworkStatusVote extends Descriptor {
   public List<String> getRecommendedClientVersions();
 
   /**
+   * Return the version numbers of all protocols that clients should support,
+   * or null if the vote does not contain an opinion about protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRecommendedClientProtocols();
+
+  /**
+   * Return the version numbers of all protocols that relays should support,
+   * or null if the vote does not contain an opinion about protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRecommendedRelayProtocols();
+
+  /**
+   * Return the version numbers of all protocols that clients must support,
+   * or null if the vote does not contain an opinion about protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRequiredClientProtocols();
+
+  /**
+   * Return the version numbers of all protocols that relays must support,
+   * or null if the vote does not contain an opinion about protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getRequiredRelayProtocols();
+
+  /**
    * Return a list of software packages and their versions together with a
    * URL and one or more digests in the format <code>PackageName Version
    * URL DIGESTS</code> that are known by this directory authority, or
diff --git a/src/main/java/org/torproject/descriptor/ServerDescriptor.java b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
index 418cb55..4ecade6 100644
--- a/src/main/java/org/torproject/descriptor/ServerDescriptor.java
+++ b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
@@ -4,6 +4,8 @@
 package org.torproject.descriptor;
 
 import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
 
 /**
  * Contains a relay or sanitized bridge server descriptor.
@@ -152,6 +154,14 @@ public interface ServerDescriptor extends Descriptor {
   public String getPlatform();
 
   /**
+   * Return the version numbers of all protocols supported by this server, or
+   * null if this descriptor does not specify supported protocol versions.
+   *
+   * @since 1.6.0
+   */
+  public SortedMap<String, SortedSet<Long>> getProtocols();
+
+  /**
    * Return the time in milliseconds since the epoch when this descriptor
    * and the corresponding extra-info descriptor were generated.
    *
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index d26eaeb..f67e6bd 100644
--- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -57,6 +57,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
     this.atMostOnceKeywords = new TreeSet<>();
     this.atMostOnceKeywords.add("s");
     this.atMostOnceKeywords.add("v");
+    this.atMostOnceKeywords.add("pr");
     this.atMostOnceKeywords.add("w");
     this.atMostOnceKeywords.add("p");
   }
@@ -95,6 +96,9 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
         case "v":
           this.parseVLine(line, parts);
           break;
+        case "pr":
+          this.parsePrLine(line, parts);
+          break;
         case "w":
           this.parseWLine(line, parts);
           break;
@@ -191,6 +195,12 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
     }
   }
 
+  private void parsePrLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.parsedAtMostOnceKeyword("pr");
+    this.protocols = ParseHelper.parseProtocolVersions(line, line, parts);
+  }
+
   private void parseWLine(String line, String[] parts)
       throws DescriptorParseException {
     this.parsedAtMostOnceKeyword("w");
@@ -359,6 +369,13 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
     return this.version;
   }
 
+  private SortedMap<String, SortedSet<Long>> protocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getProtocols() {
+    return this.protocols;
+  }
+
   private long bandwidth = -1L;
 
   @Override
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
index dfa9065..f73a591 100644
--- a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -8,13 +8,16 @@ import org.torproject.descriptor.DescriptorParseException;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.SortedMap;
+import java.util.SortedSet;
 import java.util.TimeZone;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.regex.Pattern;
 
 import javax.xml.bind.DatatypeConverter;
@@ -578,5 +581,49 @@ public class ParseHelper {
     throw new DescriptorParseException("Unable to locate "
         + "master-key-ed25519 in identity-ed25519.");
   }
+
+  private static Map<String, SortedMap<String, SortedSet<Long>>>
+      parsedProtocolVersions = new HashMap<>();
+
+  protected static SortedMap<String, SortedSet<Long>> parseProtocolVersions(
+      String line, String lineNoOpt, String[] partsNoOpt)
+      throws DescriptorParseException {
+    if (!parsedProtocolVersions.containsKey(lineNoOpt)) {
+      SortedMap<String, SortedSet<Long>> parsed = new TreeMap<>();
+      boolean invalid = false;
+      try {
+        for (int i = 1; i < partsNoOpt.length; i++) {
+          String[] part = partsNoOpt[i].split("=");
+          SortedSet<Long> versions = new TreeSet<>();
+          for (String val : part[1].split(",")) {
+            if (val.contains("-")) {
+              String[] fromTo = val.split("-");
+              long from = Long.parseLong(fromTo[0]);
+              long to = Long.parseLong(fromTo[1]);
+              if (from > to || to >= 0x1_0000_0000L) {
+                invalid = true;
+              } else {
+                for (long j = from;
+                    j <= to; j++) {
+                  versions.add(j);
+                }
+              }
+            } else {
+              versions.add(Long.parseLong(val));
+            }
+          }
+          parsed.put(part[0], Collections.unmodifiableSortedSet(versions));
+        }
+      } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+        throw new DescriptorParseException("Invalid line '" + line + "'.", e);
+      }
+      if (invalid) {
+        throw new DescriptorParseException("Invalid line '" + line + "'.");
+      }
+      parsedProtocolVersions.put(lineNoOpt,
+          Collections.unmodifiableSortedMap(parsed));
+    }
+    return parsedProtocolVersions.get(lineNoOpt);
+  }
 }
 
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index 8733729..fd4cf7e 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -52,7 +52,9 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         + "valid-until,voting-delay,known-flags").split(",")));
     this.checkExactlyOnceKeywords(exactlyOnceKeywords);
     Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
-        "client-versions,server-versions,params,directory-footer,"
+        "client-versions,server-versions,recommended-client-protocols,"
+        + "recommended-relay-protocols,required-client-protocols,"
+        + "required-relay-protocols,params,directory-footer,"
         + "bandwidth-weights").split(",")));
     this.checkAtMostOnceKeywords(atMostOnceKeywords);
     this.checkFirstKeyword("network-status-version");
@@ -124,6 +126,18 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         case "server-versions":
           this.parseServerVersionsLine(line, parts);
           break;
+        case "recommended-client-protocols":
+          this.parseRecommendedClientProtocolsLine(line, parts);
+          break;
+        case "recommended-relay-protocols":
+          this.parseRecommendedRelayProtocolsLine(line, parts);
+          break;
+        case "required-client-protocols":
+          this.parseRequiredClientProtocolsLine(line, parts);
+          break;
+        case "required-relay-protocols":
+          this.parseRequiredRelayProtocolsLine(line, parts);
+          break;
         case "package":
           this.parsePackageLine(line, parts);
           break;
@@ -281,6 +295,30 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         line, parts);
   }
 
+  private void parseRecommendedClientProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.recommendedClientProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRecommendedRelayProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.recommendedRelayProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRequiredClientProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.requiredClientProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRequiredRelayProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.requiredRelayProtocols = ParseHelper.parseProtocolVersions(line, line,
+        parts);
+  }
+
   private void parsePackageLine(String line, String[] parts)
       throws DescriptorParseException {
     if (parts.length < 5) {
@@ -397,6 +435,34 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         : Arrays.asList(this.recommendedServerVersions);
   }
 
+  private SortedMap<String, SortedSet<Long>> recommendedClientProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRecommendedClientProtocols() {
+    return this.recommendedClientProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> recommendedRelayProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRecommendedRelayProtocols() {
+    return this.recommendedRelayProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> requiredClientProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRequiredClientProtocols() {
+    return this.requiredClientProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> requiredRelayProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRequiredRelayProtocols() {
+    return this.requiredRelayProtocols;
+  }
+
   private List<String> packageLines;
 
   @Override
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index 02390de..619b2c1 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -52,6 +52,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
     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,"
         + "legacy-key,dir-key-crosscert,dir-address,directory-footer")
         .split(",")));
@@ -117,6 +119,18 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
         case "server-versions":
           this.parseServerVersionsLine(line, parts);
           break;
+        case "recommended-client-protocols":
+          this.parseRecommendedClientProtocolsLine(line, parts);
+          break;
+        case "recommended-relay-protocols":
+          this.parseRecommendedRelayProtocolsLine(line, parts);
+          break;
+        case "required-client-protocols":
+          this.parseRequiredClientProtocolsLine(line, parts);
+          break;
+        case "required-relay-protocols":
+          this.parseRequiredRelayProtocolsLine(line, parts);
+          break;
         case "package":
           this.parsePackageLine(line, parts);
           break;
@@ -305,6 +319,30 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
         line, parts);
   }
 
+  private void parseRecommendedClientProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.recommendedClientProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRecommendedRelayProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.recommendedRelayProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRequiredClientProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.requiredClientProtocols = ParseHelper.parseProtocolVersions(line,
+        line, parts);
+  }
+
+  private void parseRequiredRelayProtocolsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.requiredRelayProtocols = ParseHelper.parseProtocolVersions(line, line,
+        parts);
+  }
+
   private void parsePackageLine(String line, String[] parts)
       throws DescriptorParseException {
     if (parts.length < 5) {
@@ -710,6 +748,34 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
         : Arrays.asList(this.recommendedServerVersions);
   }
 
+  private SortedMap<String, SortedSet<Long>> recommendedClientProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRecommendedClientProtocols() {
+    return this.recommendedClientProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> recommendedRelayProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRecommendedRelayProtocols() {
+    return this.recommendedRelayProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> requiredClientProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRequiredClientProtocols() {
+    return this.requiredClientProtocols;
+  }
+
+  private SortedMap<String, SortedSet<Long>> requiredRelayProtocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getRequiredRelayProtocols() {
+    return this.requiredRelayProtocols;
+  }
+
   private List<String> packageLines;
 
   @Override
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
index 183959a..309cad4 100644
--- a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -16,6 +16,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Scanner;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
 
 import javax.xml.bind.DatatypeConverter;
 
@@ -34,7 +36,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
         "router,bandwidth,published".split(",")));
     this.checkExactlyOnceKeywords(exactlyOnceKeywords);
     Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
-        "identity-ed25519,master-key-ed25519,platform,fingerprint,"
+        "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,"
@@ -80,6 +82,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
         case "platform":
           this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
           break;
+        case "proto":
+          this.parseProtoLine(line, lineNoOpt, partsNoOpt);
+          break;
         case "published":
           this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
           break;
@@ -303,6 +308,12 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
     }
   }
 
+  private void parseProtoLine(String line, String lineNoOpt,
+      String[] partsNoOpt) throws DescriptorParseException {
+    this.protocols = ParseHelper.parseProtocolVersions(line, lineNoOpt,
+        partsNoOpt);
+  }
+
   private void parsePublishedLine(String line, String lineNoOpt,
       String[] partsNoOpt) throws DescriptorParseException {
     this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
@@ -812,6 +823,13 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
     return this.platform;
   }
 
+  private SortedMap<String, SortedSet<Long>> protocols;
+
+  @Override
+  public SortedMap<String, SortedSet<Long>> getProtocols() {
+    return this.protocols;
+  }
+
   private long publishedMillis;
 
   @Override
diff --git a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
index b765be6..24d5a02 100644
--- a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
+++ b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
@@ -126,6 +126,54 @@ public class ConsensusBuilder {
     return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
   }
 
+  private String recommendedClientProtocolsLine =
+      "recommended-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+      + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+  protected static RelayNetworkStatusConsensus
+      createWithRecommendedClientProtocolsLine(String line)
+      throws DescriptorParseException {
+    ConsensusBuilder cb = new ConsensusBuilder();
+    cb.recommendedClientProtocolsLine = line;
+    return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+  }
+
+  private String recommendedRelayProtocolsLine =
+      "recommended-relay-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+      + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+  protected static RelayNetworkStatusConsensus
+      createWithRecommendedRelayProtocolsLine(String line)
+      throws DescriptorParseException {
+    ConsensusBuilder cb = new ConsensusBuilder();
+    cb.recommendedRelayProtocolsLine = line;
+    return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+  }
+
+  private String requiredClientProtocolsLine =
+      "required-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+      + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+  protected static RelayNetworkStatusConsensus
+      createWithRequiredClientProtocolsLine(String line)
+      throws DescriptorParseException {
+    ConsensusBuilder cb = new ConsensusBuilder();
+    cb.requiredClientProtocolsLine = line;
+    return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+  }
+
+  private String requiredRelayProtocolsLine =
+      "required-relay-protocols Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 "
+      + "HSRend=1 Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2";
+
+  protected static RelayNetworkStatusConsensus
+      createWithRequiredRelayProtocolsLine(String line)
+      throws DescriptorParseException {
+    ConsensusBuilder cb = new ConsensusBuilder();
+    cb.requiredRelayProtocolsLine = line;
+    return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+  }
+
   private String paramsLine = "params "
       + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
       + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
@@ -322,6 +370,18 @@ public class ConsensusBuilder {
     if (this.knownFlagsLine != null) {
       sb.append(this.knownFlagsLine).append("\n");
     }
+    if (this.recommendedClientProtocolsLine != null) {
+      sb.append(this.recommendedClientProtocolsLine).append("\n");
+    }
+    if (this.recommendedRelayProtocolsLine != null) {
+      sb.append(this.recommendedRelayProtocolsLine).append("\n");
+    }
+    if (this.requiredClientProtocolsLine != null) {
+      sb.append(this.requiredClientProtocolsLine).append("\n");
+    }
+    if (this.requiredRelayProtocolsLine != null) {
+      sb.append(this.requiredRelayProtocolsLine).append("\n");
+    }
     if (this.paramsLine != null) {
       sb.append(this.paramsLine).append("\n");
     }
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
index ebcbd83..786ae54 100644
--- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
@@ -19,6 +19,7 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.TreeSet;
 
 /* TODO Add test cases for all lines starting with "opt ". */
 
@@ -242,6 +243,16 @@ public class RelayNetworkStatusConsensusImplTest {
       return createWithStatusEntry(seb.buildStatusEntry());
     }
 
+    private String prLine = "pr Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+        + "HSIntro=3 HSRend=1-2 Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2";
+
+    private static RelayNetworkStatusConsensus
+        createWithPrLine(String line) throws DescriptorParseException {
+      StatusEntryBuilder seb = new StatusEntryBuilder();
+      seb.prLine = line;
+      return createWithStatusEntry(seb.buildStatusEntry());
+    }
+
     @SuppressWarnings("checkstyle:membername")
     private String wLine = "w Bandwidth=1";
 
@@ -275,6 +286,9 @@ public class RelayNetworkStatusConsensusImplTest {
       if (this.vLine != null) {
         sb.append(this.vLine).append("\n");
       }
+      if (this.prLine != null) {
+        sb.append(this.prLine).append("\n");
+      }
       if (this.wLine != null) {
         sb.append(this.wLine).append("\n");
       }
@@ -353,6 +367,14 @@ public class RelayNetworkStatusConsensusImplTest {
     assertTrue(consensus.getRecommendedServerVersions().contains(
         "0.2.3.8-alpha"));
     assertTrue(consensus.getKnownFlags().contains("Running"));
+    assertTrue(consensus.getRecommendedClientProtocols().get("Cons")
+        .contains(1L));
+    assertFalse(consensus.getRecommendedRelayProtocols().get("Cons")
+        .contains(33L));
+    assertFalse(consensus.getRequiredClientProtocols().get("Relay")
+        .contains(1L));
+    assertTrue(consensus.getRequiredRelayProtocols().get("Relay")
+        .contains(1L));
     assertEquals(30000, (int) consensus.getConsensusParams().get(
         "CircuitPriorityHalflifeMsec"));
     assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
@@ -667,6 +689,54 @@ public class RelayNetworkStatusConsensusImplTest {
   }
 
   @Test()
+  public void testRecommendedClientProtocols123()
+      throws DescriptorParseException {
+    RelayNetworkStatusConsensus consensus = ConsensusBuilder
+        .createWithRecommendedClientProtocolsLine(
+        "recommended-client-protocols Cons=1,2,3");
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 1L, 2L, 3L })),
+        consensus.getRecommendedClientProtocols().get("Cons"));
+  }
+
+  @Test()
+  public void testRecommendedRelayProtocols134()
+      throws DescriptorParseException {
+    RelayNetworkStatusConsensus consensus = ConsensusBuilder
+        .createWithRecommendedRelayProtocolsLine(
+        "recommended-relay-protocols Cons=1,3-4");
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 1L, 3L, 4L })),
+        consensus.getRecommendedRelayProtocols().get("Cons"));
+  }
+
+  @Test()
+  public void testRequiredClientProtocols1425()
+      throws DescriptorParseException {
+    RelayNetworkStatusConsensus consensus = ConsensusBuilder
+        .createWithRequiredClientProtocolsLine(
+        "required-client-protocols Cons=1-3,2-4");
+    assertEquals(new TreeSet<Long>(Arrays.asList(
+        new Long[] { 1L, 2L, 3L, 4L })),
+        consensus.getRequiredClientProtocols().get("Cons"));
+  }
+
+  @Test()
+  public void testRequiredRelayProtocols1111()
+      throws DescriptorParseException {
+    RelayNetworkStatusConsensus consensus = ConsensusBuilder
+        .createWithRequiredRelayProtocolsLine(
+        "required-relay-protocols Cons=1-1,1-1");
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 1L })),
+        consensus.getRequiredRelayProtocols().get("Cons"));
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testRequiredRelayProtocolsTwice()
+      throws DescriptorParseException {
+    ConsensusBuilder.createWithRequiredRelayProtocolsLine(
+        "required-relay-protocols Cons=1\nrequired-relay-protocols Cons=1");
+  }
+
+  @Test()
   public void testPackageNone() throws DescriptorParseException {
     RelayNetworkStatusConsensus consensus =
         ConsensusBuilder.createWithPackageLines(null);
@@ -1022,6 +1092,15 @@ public class RelayNetworkStatusConsensusImplTest {
   }
 
   @Test(expected = DescriptorParseException.class)
+  public void testTwoPrLines() throws DescriptorParseException {
+    StatusEntryBuilder sb = new StatusEntryBuilder();
+    sb.prLine = sb.prLine + "\n" + sb.prLine;
+    ConsensusBuilder cb = new ConsensusBuilder();
+    cb.statusEntries.add(sb.buildStatusEntry());
+    new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+  }
+
+  @Test(expected = DescriptorParseException.class)
   public void testWLineNoSpace() throws DescriptorParseException {
     StatusEntryBuilder.createWithWLine("w");
   }
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
index 847c538..ea7b927 100644
--- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
@@ -16,6 +16,7 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.TreeSet;
 
 /* TODO Add test cases for all lines starting with "opt ". */
 
@@ -153,6 +154,54 @@ public class RelayNetworkStatusVoteImplTest {
       return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
     }
 
+    private String recommendedClientProtocolsLine =
+        "recommended-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+        + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+    private static RelayNetworkStatusVote
+        createWithRecommendedClientProtocolsLine(String line)
+        throws DescriptorParseException {
+      VoteBuilder vb = new VoteBuilder();
+      vb.recommendedClientProtocolsLine = line;
+      return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    }
+
+    private String recommendedRelayProtocolsLine =
+        "recommended-relay-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+        + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+    private static RelayNetworkStatusVote
+        createWithRecommendedRelayProtocolsLine(String line)
+        throws DescriptorParseException {
+      VoteBuilder vb = new VoteBuilder();
+      vb.recommendedRelayProtocolsLine = line;
+      return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    }
+
+    private String requiredClientProtocolsLine =
+        "required-client-protocols Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+        + "HSIntro=3 HSRend=1 Link=4 LinkAuth=1 Microdesc=1-2 Relay=2";
+
+    private static RelayNetworkStatusVote
+        createWithRequiredClientProtocolsLine(String line)
+        throws DescriptorParseException {
+      VoteBuilder vb = new VoteBuilder();
+      vb.requiredClientProtocolsLine = line;
+      return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    }
+
+    private String requiredRelayProtocolsLine =
+        "required-relay-protocols Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 "
+        + "HSRend=1 Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2";
+
+    private static RelayNetworkStatusVote
+        createWithRequiredRelayProtocolsLine(String line)
+        throws DescriptorParseException {
+      VoteBuilder vb = new VoteBuilder();
+      vb.requiredRelayProtocolsLine = line;
+      return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    }
+
     private String flagThresholdsLine = "flag-thresholds "
         + "stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 "
         + "guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 "
@@ -493,6 +542,18 @@ public class RelayNetworkStatusVoteImplTest {
       if (this.knownFlagsLine != null) {
         sb.append(this.knownFlagsLine).append("\n");
       }
+      if (this.recommendedClientProtocolsLine != null) {
+        sb.append(this.recommendedClientProtocolsLine).append("\n");
+      }
+      if (this.recommendedRelayProtocolsLine != null) {
+        sb.append(this.recommendedRelayProtocolsLine).append("\n");
+      }
+      if (this.requiredClientProtocolsLine != null) {
+        sb.append(this.requiredClientProtocolsLine).append("\n");
+      }
+      if (this.requiredRelayProtocolsLine != null) {
+        sb.append(this.requiredRelayProtocolsLine).append("\n");
+      }
       if (this.flagThresholdsLine != null) {
         sb.append(this.flagThresholdsLine).append("\n");
       }
@@ -875,6 +936,37 @@ public class RelayNetworkStatusVoteImplTest {
         "client-versions ,0.2.2.34");
   }
 
+  @Test(expected = DescriptorParseException.class)
+  public void testRecommendedClientProtocols21()
+      throws DescriptorParseException {
+    VoteBuilder.createWithRecommendedClientProtocolsLine(
+        "recommended-client-protocols Cons=2-1");
+  }
+
+  @Test()
+  public void testRecommendedRelayProtocols0()
+      throws DescriptorParseException {
+    RelayNetworkStatusVote vote =
+        VoteBuilder.createWithRecommendedRelayProtocolsLine(
+        "recommended-relay-protocols Cons=0");
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 0L })),
+        vote.getRecommendedRelayProtocols().get("Cons"));
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testRequiredClientProtocols1Max()
+      throws DescriptorParseException {
+    VoteBuilder.createWithRequiredClientProtocolsLine(
+        "recommended-client-protocols Cons=1-4294967296");
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testRequiredRelayProtocolsMinus1()
+      throws DescriptorParseException {
+    VoteBuilder.createWithRequiredRelayProtocolsLine(
+        "recommended-client-protocols Cons=-1");
+  }
+
   @Test()
   public void testPackageNone() throws DescriptorParseException {
     RelayNetworkStatusVote vote =
diff --git a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
index 3769ead..4370321 100644
--- a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.SortedMap;
+import java.util.TreeSet;
 
 /* Test parsing of relay server descriptors. */
 public class ServerDescriptorImplTest {
@@ -226,7 +227,7 @@ public class ServerDescriptorImplTest {
       return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
     }
 
-    private String protocolsLine = "opt protocols Link 1 2 Circuit 1";
+    private String protocolsLine = null;
 
     private static ServerDescriptor createWithProtocolsLine(
         String line) throws DescriptorParseException {
@@ -235,6 +236,16 @@ public class ServerDescriptorImplTest {
       return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
     }
 
+    private String protoLine = "proto Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 "
+        + "HSIntro=3 HSRend=1-2 Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2";
+
+    private static ServerDescriptor createWithProtoLine(
+        String line) throws DescriptorParseException {
+      DescriptorBuilder db = new DescriptorBuilder();
+      db.protoLine = line;
+      return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+    }
+
     private String allowSingleHopExitsLine = null;
 
     private static ServerDescriptor
@@ -395,6 +406,9 @@ public class ServerDescriptorImplTest {
       if (this.protocolsLine != null) {
         sb.append(this.protocolsLine).append("\n");
       }
+      if (this.protoLine != null) {
+        sb.append(this.protoLine).append("\n");
+      }
       if (this.allowSingleHopExitsLine != null) {
         sb.append(this.allowSingleHopExitsLine).append("\n");
       }
@@ -446,10 +460,10 @@ public class ServerDescriptorImplTest {
     assertEquals(0, (int) descriptor.getDirPort());
     assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
         descriptor.getPlatform());
-    assertEquals(Arrays.asList(new Integer[] {1, 2}),
-        descriptor.getLinkProtocolVersions());
-    assertEquals(Arrays.asList(new Integer[] {1}),
-        descriptor.getCircuitProtocolVersions());
+    assertEquals(new TreeSet<Long>(Arrays.asList(
+        new Long[] { 1L, 2L, 3L, 4L })), descriptor.getProtocols().get("Link"));
+    assertEquals(new TreeSet<Long>(Arrays.asList(
+        new Long[] { 1L })), descriptor.getProtocols().get("LinkAuth"));
     assertEquals(1325390599000L, descriptor.getPublishedMillis());
     assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
         descriptor.getFingerprint());
@@ -605,6 +619,16 @@ public class ServerDescriptorImplTest {
   }
 
   @Test()
+  public void testProtocolsOpt() throws DescriptorParseException {
+    ServerDescriptor descriptor = DescriptorBuilder
+        .createWithProtocolsLine("opt protocols Link 1 2 Circuit 1");
+    assertEquals(Arrays.asList(new Integer[] {1, 2}),
+        descriptor.getLinkProtocolVersions());
+    assertEquals(Arrays.asList(new Integer[] {1}),
+        descriptor.getCircuitProtocolVersions());
+  }
+
+  @Test()
   public void testProtocolsNoOpt() throws DescriptorParseException {
     ServerDescriptor descriptor = DescriptorBuilder
         .createWithProtocolsLine("protocols Link 1 2 Circuit 1");
@@ -626,6 +650,21 @@ public class ServerDescriptorImplTest {
     DescriptorBuilder.createWithProtocolsLine("opt protocols Link 1 2");
   }
 
+  @Test()
+  public void testProtoGreenPurple() throws DescriptorParseException {
+    ServerDescriptor descriptor = DescriptorBuilder
+        .createWithProtoLine("proto Green=23 Purple=42");
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 23L })),
+        descriptor.getProtocols().get("Green"));
+    assertEquals(new TreeSet<Long>(Arrays.asList(new Long[] { 42L })),
+        descriptor.getProtocols().get("Purple"));
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testProtoInvalid() throws DescriptorParseException {
+    DescriptorBuilder.createWithProtoLine("proto Invalid=1+2+3");
+  }
+
   @Test(expected = DescriptorParseException.class)
   public void testPublishedMissing() throws DescriptorParseException {
     DescriptorBuilder.createWithPublishedLine(null);





More information about the tor-commits mailing list