commit 75844d046cf6df8162599ce4de4794cc2de09af0 Author: Karsten Loesing karsten.loesing@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();