commit 75844d046cf6df8162599ce4de4794cc2de09af0
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Thu May 11 16:21:17 2017 +0200
Parse "padding-counts" lines in extra-info descriptors.
Implements #22217.
---
CHANGELOG.md | 1 +
.../torproject/descriptor/ExtraInfoDescriptor.java | 24 +++++++
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 36 +++++++++++
.../torproject/descriptor/impl/ParseHelper.java | 33 ++++++++++
.../impl/ExtraInfoDescriptorImplTest.java | 75 ++++++++++++++++++++++
5 files changed, 169 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9322ecd..36aa082 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
DescriptorParser and DescriptorReader and refer to
getUnrecognizedLines() in Descriptor if applications really need
to fail descriptors containing unrecognized lines.
+ - Parse "padding-counts" lines in extra-info descriptors.
* Minor changes
- Accept extra arguments in statistics-related extra-info
diff --git a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
index bb1cbe6..04a46d4 100644
--- a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
+++ b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
@@ -650,6 +650,30 @@ public interface ExtraInfoDescriptor extends Descriptor {
public Map<String, Double> getHidservDirOnionsSeenParameters();
/**
+ * Return the time in milliseconds since the epoch when the included
+ * padding-counts statistics ended, or -1 if no such statistics are included.
+ *
+ * @since 1.7.0
+ */
+ public long getPaddingCountsStatsEndMillis();
+
+ /**
+ * Return the interval length of the included padding-counts statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.7.0
+ */
+ public long getPaddingCountsStatsIntervalLength();
+
+ /**
+ * Return padding-counts statistics, or <code>null</code> if no such
+ * statistics are included.
+ *
+ * @since 1.7.0
+ */
+ public Map<String, Long> getPaddingCounts();
+
+ /**
* Return the RSA-1024 signature of the PKCS1-padded descriptor digest,
* taken from the beginning of the router line through the newline after
* the router-signature line, or null if the descriptor doesn't contain
diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
index e2b3827..dd1bc07 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -66,6 +66,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
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");
@@ -220,6 +221,9 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
case "hidserv-dir-onions-seen":
this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
break;
+ case "padding-counts":
+ this.parsePaddingCountsLine(line, lineNoOpt, partsNoOpt);
+ break;
case "identity-ed25519":
this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
nextCrypto = "identity-ed25519";
@@ -756,6 +760,16 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
partsNoOpt, 2);
}
+ private void parsePaddingCountsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 6);
+ this.paddingCountsStatsEndMillis = parsedStatsEndData[0];
+ this.paddingCountsStatsIntervalLength = parsedStatsEndData[1];
+ this.paddingCounts = ParseHelper.parseSpaceSeparatedStringKeyLongValueMap(
+ line, partsNoOpt, 5);
+ }
+
private void parseRouterSignatureLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
if (!lineNoOpt.equals("router-signature")) {
@@ -1341,6 +1355,28 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
: new HashMap<>(this.hidservDirOnionsSeenParameters);
}
+ private long paddingCountsStatsEndMillis = -1L;
+
+ @Override
+ public long getPaddingCountsStatsEndMillis() {
+ return this.paddingCountsStatsEndMillis;
+ }
+
+ private long paddingCountsStatsIntervalLength = -1L;
+
+ @Override
+ public long getPaddingCountsStatsIntervalLength() {
+ return this.paddingCountsStatsIntervalLength;
+ }
+
+ private Map<String, Long> paddingCounts;
+
+ @Override
+ public Map<String, Long> getPaddingCounts() {
+ return this.paddingCounts == null ? null
+ : new HashMap<>(this.paddingCounts);
+ }
+
private String routerSignature;
@Override
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
index fa6ca75..0d4a27a 100644
--- a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -493,6 +493,39 @@ public class ParseHelper {
return result;
}
+ protected static Map<String, Long>
+ parseSpaceSeparatedStringKeyLongValueMap(String line,
+ String[] partsNoOpt, int startIndex)
+ throws DescriptorParseException {
+ Map<String, Long> result = new LinkedHashMap<>();
+ if (partsNoOpt.length < startIndex) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list starting at index " + startIndex
+ + ".");
+ }
+ for (int i = startIndex; i < partsNoOpt.length; i++) {
+ String listElement = partsNoOpt[i];
+ String[] keyAndValue = listElement.split("=");
+ String key = null;
+ Long value = null;
+ if (keyAndValue.length == 2) {
+ try {
+ value = Long.parseLong(keyAndValue[1]);
+ key = keyAndValue[0];
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (key == null) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "an illegal key or value in list element '" + listElement
+ + "'.");
+ }
+ result.put(key, value);
+ }
+ return result;
+ }
+
protected static String
parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
String identityEd25519CryptoBlock) throws DescriptorParseException {
diff --git a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
index 76a2148..0afc344 100644
--- a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
@@ -180,6 +180,15 @@ public class ExtraInfoDescriptorImplTest {
return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
}
+ private String paddingCountsLine = null;
+
+ private static ExtraInfoDescriptor createWithPaddingCountsLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.paddingCountsLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+
private String unrecognizedLine = null;
private static ExtraInfoDescriptor createWithUnrecognizedLine(
@@ -288,6 +297,9 @@ public class ExtraInfoDescriptorImplTest {
if (this.hidservStatsLines != null) {
sb.append(this.hidservStatsLines).append("\n");
}
+ if (this.paddingCountsLine != null) {
+ sb.append(this.paddingCountsLine).append("\n");
+ }
if (this.unrecognizedLine != null) {
sb.append(this.unrecognizedLine).append("\n");
}
@@ -1727,6 +1739,69 @@ public class ExtraInfoDescriptorImplTest {
}
@Test()
+ public void testPaddingCountsValid()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder
+ .createWithPaddingCountsLine("padding-counts 2017-05-10 01:48:43 "
+ + "(86400 s) bin-size=10000 write-drop=10000 write-pad=10000 "
+ + "write-total=10000 read-drop=10000 read-pad=10000 read-total=70000 "
+ + "enabled-read-pad=0 enabled-read-total=0 enabled-write-pad=0 "
+ + "enabled-write-total=0 max-chanpad-timers=0");
+ assertEquals(1494380923000L,
+ descriptor.getPaddingCountsStatsEndMillis());
+ assertEquals(86400, descriptor.getPaddingCountsStatsIntervalLength());
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("bin-size"));
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("write-drop"));
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("write-pad"));
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("write-total"));
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("read-drop"));
+ assertEquals(10000L,
+ (long) descriptor.getPaddingCounts().get("read-pad"));
+ assertEquals(70000L,
+ (long) descriptor.getPaddingCounts().get("read-total"));
+ assertEquals(0L,
+ (long) descriptor.getPaddingCounts().get("enabled-read-pad"));
+ assertEquals(0L,
+ (long) descriptor.getPaddingCounts().get("enabled-read-total"));
+ assertEquals(0L,
+ (long) descriptor.getPaddingCounts().get("enabled-write-pad"));
+ assertEquals(0L,
+ (long) descriptor.getPaddingCounts().get("enabled-write-total"));
+ assertEquals(0L,
+ (long) descriptor.getPaddingCounts().get("max-chanpad-timers"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPaddingCountsNoTime() throws DescriptorParseException {
+ DescriptorBuilder.createWithPaddingCountsLine("padding-counts 2017-05-10 "
+ + "(86400 s) bin-size=10000 write-drop=10000");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPaddingCountsNoInterval() throws DescriptorParseException {
+ DescriptorBuilder.createWithPaddingCountsLine("padding-counts 2017-05-10 "
+ + "01:48:43 bin-size=10000 write-drop=10000");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPaddingCountsCommaSeparatedList()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithPaddingCountsLine("padding-counts 2017-05-10 "
+ + "(86400 s) bin-size=10000,write-drop=10000");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPaddingCountsNoList() throws DescriptorParseException {
+ DescriptorBuilder.createWithPaddingCountsLine("padding-counts 2017-05-10 "
+ + "(86400 s)");
+ }
+
+ @Test()
public void testHidservStatsValid() throws DescriptorParseException {
ExtraInfoDescriptor descriptor = HidservStatsBuilder
.createWithDefaultLines();