tor-commits
Threads by month
- ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2016
- 21 participants
- 1272 discussions
[tor/master] Avoid disclosing exit IP addresses in exit policies by default
by nickm@torproject.org 06 Jul '16
by nickm@torproject.org 06 Jul '16
06 Jul '16
commit 514f0041d190b9e142cc246e3ec7ac65342547bd
Author: teor (Tim Wilson-Brown) <teor2345(a)gmail.com>
Date: Fri Jul 1 15:37:13 2016 +1000
Avoid disclosing exit IP addresses in exit policies by default
From 0.2.7.2-alpha onwards, Exits would reject all the IP addresses
they knew about in their exit policy. But this may have disclosed
addresses that were otherwise unlisted.
Now, only advertised addresses are rejected by default by
ExitPolicyRejectPrivate. All known addresses are only rejected when
ExitPolicyRejectLocalInterfaces is explicitly set to 1.
---
changes/bug18456 | 6 ++++
doc/tor.1.txt | 28 +++++++++++-------
src/or/config.c | 3 ++
src/or/control.c | 2 +-
src/or/main.c | 8 +++---
src/or/or.h | 8 +++++-
src/or/policies.c | 78 ++++++++++++++++++++++++++++++++++++--------------
src/or/policies.h | 7 +++--
src/test/test_policy.c | 34 +++++++++++++++++++++-
9 files changed, 133 insertions(+), 41 deletions(-)
diff --git a/changes/bug18456 b/changes/bug18456
new file mode 100644
index 0000000..843c70a
--- /dev/null
+++ b/changes/bug18456
@@ -0,0 +1,6 @@
+ o Major bugfixes (exit policies):
+ - Avoid disclosing exit outbound bind addresses, configured port bind
+ addresses, and local interface addresses in relay descriptors by
+ default under ExitPolicyRejectPrivate. Instead, only reject these
+ (otherwise unlisted) addresses if ExitPolicyRejectLocalInterfaces is set.
+ Fixes bug 18456; bugfix on 0.2.7.2-alpha. Patch by teor.
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 64f0da0..c22f94e 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -1701,15 +1701,16 @@ is non-zero):
used with accept6/reject6.) +
+
Private addresses are rejected by default (at the beginning of your exit
- policy), along with any configured primary public IPv4 and IPv6 addresses,
- and any public IPv4 and IPv6 addresses on any interface on the relay.
+ policy), along with any configured primary public IPv4 and IPv6 addresses.
These private addresses are rejected unless you set the
ExitPolicyRejectPrivate config option to 0. For example, once you've done
that, you could allow HTTP to 127.0.0.1 and block all other connections to
internal networks with "accept 127.0.0.1:80,reject private:\*", though that
may also allow connections to your own computer that are addressed to its
public (external) IP address. See RFC 1918 and RFC 3330 for more details
- about internal and reserved IP address space. +
+ about internal and reserved IP address space. See
+ ExitPolicyRejectLocalInterfaces if you want to block every address on the
+ relay, even those that aren't advertised in the descriptor. +
+
This directive can be specified multiple times so you don't have to put it
all on one line. +
@@ -1739,16 +1740,23 @@ is non-zero):
IPv4 and IPv6 addresses.
[[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
- Reject all private (local) networks, along with any configured public
- IPv4 and IPv6 addresses, at the beginning of your exit policy. (This
- includes the IPv4 and IPv6 addresses advertised by the relay, any
- OutboundBindAddress, and the bind addresses of any port options, such as
- ORPort and DirPort.) This also rejects any public IPv4 and IPv6 addresses
- on any interface on the relay. (If IPv6Exit is not set, all IPv6 addresses
- will be rejected anyway.)
+ Reject all private (local) networks, along with the relay's advertised
+ public IPv4 and IPv6 addresses, at the beginning of your exit policy.
See above entry on ExitPolicy.
(Default: 1)
+[[ExitPolicyRejectLocalInterfaces]] **ExitPolicyRejectLocalInterfaces** **0**|**1**::
+ Reject all IPv4 and IPv6 addresses that the relay knows about, at the
+ beginning of your exit policy. This includes any OutboundBindAddress, the
+ bind addresses of any port options, such as ControlPort or DNSPort, and any
+ public IPv4 and IPv6 addresses on any interface on the relay. (If IPv6Exit
+ is not set, all IPv6 addresses will be rejected anyway.)
+ See above entry on ExitPolicy.
+ This option is off by default, because it lists all public relay IP
+ addresses in the ExitPolicy, even those relay operators might prefer not
+ to disclose.
+ (Default: 0)
+
[[IPv6Exit]] **IPv6Exit** **0**|**1**::
If set, and we are an exit node, allow clients to use us for IPv6
traffic. (Default: 0)
diff --git a/src/or/config.c b/src/or/config.c
index 45acd39..5643c8d 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -244,6 +244,7 @@ static config_var_t option_vars_[] = {
V(ExitNodes, ROUTERSET, NULL),
V(ExitPolicy, LINELIST, NULL),
V(ExitPolicyRejectPrivate, BOOL, "1"),
+ V(ExitPolicyRejectLocalInterfaces, BOOL, "0"),
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
V(ExitRelay, AUTOBOOL, "auto"),
@@ -4320,6 +4321,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
old_options->ExitRelay != new_options->ExitRelay ||
old_options->ExitPolicyRejectPrivate !=
new_options->ExitPolicyRejectPrivate ||
+ old_options->ExitPolicyRejectLocalInterfaces !=
+ new_options->ExitPolicyRejectLocalInterfaces ||
old_options->IPv6Exit != new_options->IPv6Exit ||
!config_lines_eq(old_options->ORPort_lines,
new_options->ORPort_lines) ||
diff --git a/src/or/control.c b/src/or/control.c
index d3613d8..ea7d7b7 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -3025,7 +3025,7 @@ static const getinfo_item_t getinfo_items[] = {
" ExitPolicyRejectPrivate."),
ITEM("exit-policy/reject-private/relay", policies,
"The relay-specific rules appended to the configured exit policy by"
- " ExitPolicyRejectPrivate."),
+ " ExitPolicyRejectPrivate and/or ExitPolicyRejectLocalInterfaces."),
ITEM("exit-policy/full", policies, "The entire exit policy of onion router"),
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
diff --git a/src/or/main.c b/src/or/main.c
index 65a67a9..2b9b085 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -2220,8 +2220,8 @@ ip_address_changed(int at_interface)
{
const or_options_t *options = get_options();
int server = server_mode(options);
- int exit_reject_private = (server && options->ExitRelay
- && options->ExitPolicyRejectPrivate);
+ int exit_reject_interfaces = (server && options->ExitRelay
+ && options->ExitPolicyRejectLocalInterfaces);
if (at_interface) {
if (! server) {
@@ -2239,8 +2239,8 @@ ip_address_changed(int at_interface)
}
/* Exit relays incorporate interface addresses in their exit policies when
- * ExitPolicyRejectPrivate is set */
- if (exit_reject_private || (server && !at_interface)) {
+ * ExitPolicyRejectLocalInterfaces is set */
+ if (exit_reject_interfaces || (server && !at_interface)) {
mark_my_descriptor_dirty("IP address changed");
}
diff --git a/src/or/or.h b/src/or/or.h
index a1a0810..98f5a00 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3573,7 +3573,13 @@ typedef struct {
/** Bitmask; derived from AllowInvalidNodes. */
invalid_router_usage_t AllowInvalid_;
config_line_t *ExitPolicy; /**< Lists of exit policy components. */
- int ExitPolicyRejectPrivate; /**< Should we not exit to local addresses? */
+ int ExitPolicyRejectPrivate; /**< Should we not exit to reserved private
+ * addresses, and our own published addresses?
+ */
+ int ExitPolicyRejectLocalInterfaces; /**< Should we not exit to local
+ * interface addresses?
+ * Includes OutboundBindAddresses and
+ * configured ports. */
config_line_t *SocksPolicy; /**< Lists of socks policy components */
config_line_t *DirPolicy; /**< Lists of dir policy components */
/** Addresses to bind for listening for SOCKS connections. */
diff --git a/src/or/policies.c b/src/or/policies.c
index 2703d7e..544c6b7 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -1843,10 +1843,18 @@ policies_log_first_redundant_entry(const smartlist_t *policy)
*
* If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
*
+ * If <b>configured_addresses</b> contains addresses:
+ * - prepend entries that reject the addresses in this list. These may be the
+ * advertised relay addresses and/or the outbound bind addresses,
+ * depending on the ExitPolicyRejectPrivate and
+ * ExitPolicyRejectLocalInterfaces settings.
* If <b>rejectprivate</b> is true:
* - prepend "reject private:*" to the policy.
- * - prepend entries that reject publicly routable addresses on this exit
- * relay by calling policies_parse_exit_policy_reject_private
+ * If <b>reject_interface_addresses</b> is true:
+ * - prepend entries that reject publicly routable interface addresses on
+ * this exit relay by calling policies_parse_exit_policy_reject_private
+ * If <b>reject_configured_port_addresses</b> is true:
+ * - prepend entries that reject all configured port addresses
*
* If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
@@ -1874,13 +1882,16 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
if (rejectprivate) {
/* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
- /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */
- policies_parse_exit_policy_reject_private(
- dest, ipv6_exit,
+ }
+
+ /* Consider rejecting IPv4 and IPv6 advertised relay addresses, outbound bind
+ * addresses, publicly routable addresses, and configured port addresses
+ * on this exit relay */
+ policies_parse_exit_policy_reject_private(dest, ipv6_exit,
configured_addresses,
reject_interface_addresses,
reject_configured_port_addresses);
- }
+
if (parse_addr_policy(cfg, dest, -1))
return -1;
@@ -1908,8 +1919,14 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
* - prepend an entry that rejects all destinations in all netblocks
* reserved for private use.
+ * - prepend entries that reject the advertised relay addresses in
+ * configured_addresses
+ * If <b>EXIT_POLICY_REJECT_LOCAL_INTERFACES</b> bit is set in <b>options</b>:
* - prepend entries that reject publicly routable addresses on this exit
* relay by calling policies_parse_exit_policy_internal
+ * - prepend entries that reject the outbound bind addresses in
+ * configured_addresses
+ * - prepend entries that reject all configured port addresses
*
* If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
@@ -1922,12 +1939,14 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0;
+ int reject_local_interfaces = (options &
+ EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0;
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
configured_addresses,
- reject_private,
- reject_private,
+ reject_local_interfaces,
+ reject_local_interfaces,
add_default);
}
@@ -1993,6 +2012,7 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
* add it to the list of configured addresses.
* - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
* to the list of configured addresses.
+ * If <b>or_options->ExitPolicyRejectLocalInterfaces</b> is true:
* - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
* it to the list of configured addresses.
* - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
@@ -2036,11 +2056,20 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
+ if (or_options->ExitPolicyRejectLocalInterfaces) {
+ parser_cfg |= EXIT_POLICY_REJECT_LOCAL_INTERFACES;
+ }
+
/* Copy the configured addresses into the tor_addr_t* list */
- policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
- policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
- policies_copy_outbound_addresses_to_smartlist(configured_addresses,
- or_options);
+ if (or_options->ExitPolicyRejectPrivate) {
+ policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
+ policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
+ }
+
+ if (or_options->ExitPolicyRejectLocalInterfaces) {
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ or_options);
+ }
rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg,
configured_addresses);
@@ -2820,7 +2849,8 @@ getinfo_helper_policies(control_connection_t *conn,
return -1;
}
- if (!options->ExitPolicyRejectPrivate) {
+ if (!options->ExitPolicyRejectPrivate &&
+ !options->ExitPolicyRejectLocalInterfaces) {
*answer = tor_strdup("");
return 0;
}
@@ -2829,16 +2859,22 @@ getinfo_helper_policies(control_connection_t *conn,
smartlist_t *configured_addresses = smartlist_new();
/* Copy the configured addresses into the tor_addr_t* list */
- policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
- policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
- policies_copy_outbound_addresses_to_smartlist(configured_addresses,
- options);
+ if (options->ExitPolicyRejectPrivate) {
+ policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
+ policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
+ }
+
+ if (options->ExitPolicyRejectLocalInterfaces) {
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ options);
+ }
policies_parse_exit_policy_reject_private(
- &private_policy_list,
- options->IPv6Exit,
- configured_addresses,
- 1, 1);
+ &private_policy_list,
+ options->IPv6Exit,
+ configured_addresses,
+ options->ExitPolicyRejectLocalInterfaces,
+ options->ExitPolicyRejectLocalInterfaces);
*answer = policy_dump_to_string(private_policy_list, 1, 1);
addr_policy_list_free(private_policy_list);
diff --git a/src/or/policies.h b/src/or/policies.h
index aaa6fa0..e134e68 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -18,9 +18,10 @@
*/
#define POLICY_BUF_LEN 72
-#define EXIT_POLICY_IPV6_ENABLED (1 << 0)
-#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
-#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+#define EXIT_POLICY_IPV6_ENABLED (1 << 0)
+#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
+#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+#define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3)
typedef enum firewall_connection_t {
FIREWALL_OR_CONNECTION = 0,
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 14182af..a972bd5 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -1082,10 +1082,32 @@ test_policies_getinfo_helper_policies(void *arg)
append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
mock_options.IPv6Exit = 1;
- mock_options.ExitPolicyRejectPrivate = 1;
tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
+ mock_options.ExitPolicyRejectPrivate = 1;
+ mock_options.ExitPolicyRejectLocalInterfaces = 1;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ mock_options.ExitPolicyRejectPrivate = 1;
+ mock_options.ExitPolicyRejectLocalInterfaces = 0;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ mock_options.ExitPolicyRejectPrivate = 0;
+ mock_options.ExitPolicyRejectLocalInterfaces = 1;
+
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
tt_assert(rv == 0);
@@ -1093,6 +1115,16 @@ test_policies_getinfo_helper_policies(void *arg)
tt_assert(strlen(answer) > 0);
tor_free(answer);
+ mock_options.ExitPolicyRejectPrivate = 0;
+ mock_options.ExitPolicyRejectLocalInterfaces = 0;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) == 0);
+ tor_free(answer);
+
rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
&errmsg);
tt_assert(rv == 0);
1
0
commit 3252550fc5869488058ea897bd2e8ec4a2e8b476
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Jul 6 10:07:02 2016 -0400
Fix sign in test-timers
---
src/test/test-timers.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/test/test-timers.c b/src/test/test-timers.c
index 0196ec1..2f8b205 100644
--- a/src/test/test-timers.c
+++ b/src/test/test-timers.c
@@ -97,8 +97,8 @@ main(int argc, char **argv)
total_square_difference += diff*diff;
}
const int64_t mean_diff = total_difference / n_active_timers;
- printf("mean difference: "U64_FORMAT" usec\n",
- U64_PRINTF_ARG(mean_diff));
+ printf("mean difference: "I64_FORMAT" usec\n",
+ I64_PRINTF_ARG(mean_diff));
const double mean_sq = ((double)total_square_difference)/ n_active_timers;
const double sq_mean = mean_diff * mean_diff;
1
0
commit dfb0ce449bb1eaa56abf49348274d5ae84c500e5
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Tue Jul 5 10:50:57 2016 +0200
Add checkstyle task.
Copied over from CollecTor.
---
build.xml | 25 ++++
src/test/resources/metrics_checks.xml | 217 ++++++++++++++++++++++++++++++++++
2 files changed, 242 insertions(+)
diff --git a/build.xml b/build.xml
index 0d6cf7d..f24c293 100644
--- a/build.xml
+++ b/build.xml
@@ -2,9 +2,11 @@
<property name="release.version" value="1.2.0-dev" />
<property name="sources" value="src/main/java"/>
<property name="resources" value="resources"/>
+ <property name="generated" value="generated/"/>
<property name="classes" value="classes"/>
<property name="docs" value="javadoc"/>
<property name="tests" value="src/test/java"/>
+ <property name="testresources" value="src/test/resources/"/>
<property name="libs" value="lib"/>
<property name="jarfile" value="descriptor-${release.version}.jar" />
<property name="jarsourcesfile"
@@ -24,6 +26,12 @@
</fileset>
</path>
+ <path id="checkstyle.classpath" >
+ <fileset dir="${libs}">
+ <include name="checkstyle-6.17-all.jar" />
+ </fileset>
+ </path>
+
<target name="clean" >
<delete includeEmptyDirs="true">
<fileset dir="${classes}" defaultexcludes="false" includes="**" />
@@ -37,6 +45,7 @@
<target name="init">
<mkdir dir="${classes}"/>
<mkdir dir="${docs}"/>
+ <mkdir dir="${generated}"/>
</target>
<target name="compile"
@@ -101,6 +110,22 @@
</java>
</target>
+ <taskdef resource="com/puppycrawl/tools/checkstyle/ant/checkstyle-ant-task.properties">
+ <classpath refid="checkstyle.classpath" />
+ </taskdef>
+ <target name="checks" depends="compile">
+ <checkstyle config="${testresources}/metrics_checks.xml">
+ <fileset dir="${sources}"
+ includes="**/*.java" />
+ <classpath>
+ <path refid="classpath" />
+ <path refid="checkstyle.classpath" />
+ </classpath>
+ <formatter type="plain"
+ toFile="${generated}/checkstyle_report.txt"/>
+ </checkstyle>
+ </target>
+
<target name="jar" depends="compile">
<exec executable="git" outputproperty="git.revision">
<arg value="rev-parse" />
diff --git a/src/test/resources/metrics_checks.xml b/src/test/resources/metrics_checks.xml
new file mode 100644
index 0000000..0777894
--- /dev/null
+++ b/src/test/resources/metrics_checks.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+ Checkstyle configuration that checks the Google coding conventions from Google Java Style
+ that can be found at https://google.github.io/styleguide/javaguide.html with the following
+ modifications:
+
+ - Replaced com.google with org.torproject in import statement ordering
+ [CustomImportOrder].
+
+ - Relaxed requirement that catch parameters must be at least two
+ characters long [CatchParameterName].
+
+ Checkstyle is very configurable. Be sure to read the documentation at
+ http://checkstyle.sf.net (or in your downloaded distribution).
+
+ To completely disable a check, just comment it out or delete it from the file.
+
+ Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
+ -->
+
+<module name = "Checker">
+ <property name="charset" value="UTF-8"/>
+
+ <property name="severity" value="warning"/>
+
+ <property name="fileExtensions" value="java, properties, xml"/>
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="FileTabCharacter">
+ <property name="eachLine" value="true"/>
+ </module>
+
+ <module name="TreeWalker">
+ <module name="OuterTypeFilename"/>
+ <module name="IllegalTokenText">
+ <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
+ <property name="format" value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
+ <property name="message" value="Avoid using corresponding octal or Unicode escape."/>
+ </module>
+ <module name="AvoidEscapedUnicodeCharacters">
+ <property name="allowEscapesForControlCharacters" value="true"/>
+ <property name="allowByTailComment" value="true"/>
+ <property name="allowNonPrintableEscapes" value="true"/>
+ </module>
+ <module name="LineLength">
+ <property name="max" value="100"/>
+ <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
+ </module>
+ <module name="AvoidStarImport"/>
+ <module name="OneTopLevelClass"/>
+ <module name="NoLineWrap"/>
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
+ <module name="NeedBraces"/>
+ <module name="LeftCurly">
+ <property name="maxLineLength" value="100"/>
+ </module>
+ <module name="RightCurly"/>
+ <module name="RightCurly">
+ <property name="option" value="alone"/>
+ <property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
+ </module>
+ <module name="WhitespaceAround">
+ <property name="allowEmptyConstructors" value="true"/>
+ <property name="allowEmptyMethods" value="true"/>
+ <property name="allowEmptyTypes" value="true"/>
+ <property name="allowEmptyLoops" value="true"/>
+ <message key="ws.notFollowed"
+ value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
+ <message key="ws.notPreceded"
+ value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="OneStatementPerLine"/>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="ArrayTypeStyle"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="FallThrough"/>
+ <module name="UpperEll"/>
+ <module name="ModifierOrder"/>
+ <module name="EmptyLineSeparator">
+ <property name="allowNoEmptyLineBetweenFields" value="true"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="tokens" value="DOT"/>
+ <property name="option" value="nl"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="tokens" value="COMMA"/>
+ <property name="option" value="EOL"/>
+ </module>
+ <module name="PackageName">
+ <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
+ <message key="name.invalidPattern"
+ value="Package name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="TypeName">
+ <message key="name.invalidPattern"
+ value="Type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MemberName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern"
+ value="Member name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ParameterName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern"
+ value="Parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="CatchParameterName">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern"
+ value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="LocalVariableName">
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
+ <property name="allowOneCharVarInForLoop" value="true"/>
+ <message key="name.invalidPattern"
+ value="Local variable name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="ClassTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Class type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MethodTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Method type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="InterfaceTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern"
+ value="Interface type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="NoFinalizer"/>
+ <module name="GenericWhitespace">
+ <message key="ws.followed"
+ value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.preceded"
+ value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.illegalFollow"
+ value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.notPreceded"
+ value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ </module>
+ <module name="Indentation">
+ <property name="basicOffset" value="2"/>
+ <property name="braceAdjustment" value="0"/>
+ <property name="caseIndent" value="2"/>
+ <property name="throwsIndent" value="4"/>
+ <property name="lineWrappingIndentation" value="4"/>
+ <property name="arrayInitIndent" value="2"/>
+ </module>
+ <module name="AbbreviationAsWordInName">
+ <property name="ignoreFinal" value="false"/>
+ <property name="allowedAbbreviationLength" value="1"/>
+ </module>
+ <module name="OverloadMethodsDeclarationOrder"/>
+ <module name="VariableDeclarationUsageDistance"/>
+ <module name="CustomImportOrder">
+ <property name="specialImportsRegExp" value="org.torproject"/>
+ <property name="sortImportsInGroupAlphabetically" value="true"/>
+ <property name="customImportOrderRules" value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
+ </module>
+ <module name="MethodParamPad"/>
+ <module name="OperatorWrap">
+ <property name="option" value="NL"/>
+ <property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
+ </module>
+ <module name="AnnotationLocation">
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="allowSamelineMultipleAnnotations" value="true"/>
+ </module>
+ <module name="NonEmptyAtclauseDescription"/>
+ <module name="JavadocTagContinuationIndentation"/>
+ <module name="SummaryJavadoc">
+ <property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
+ </module>
+ <module name="JavadocParagraph"/>
+ <module name="AtclauseOrder">
+ <property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
+ <property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
+ </module>
+ <module name="JavadocMethod">
+ <property name="scope" value="public"/>
+ <property name="allowMissingParamTags" value="true"/>
+ <property name="allowMissingThrowsTags" value="true"/>
+ <property name="allowMissingReturnTag" value="true"/>
+ <property name="minLineCount" value="2"/>
+ <property name="allowedAnnotations" value="Override, Test"/>
+ <property name="allowThrowsTagsForSubclasses" value="true"/>
+ </module>
+ <module name="MethodName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
+ <message key="name.invalidPattern"
+ value="Method name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="SingleLineJavadoc">
+ <property name="ignoreInlineTags" value="false"/>
+ </module>
+ <module name="EmptyCatchBlock">
+ <property name="exceptionVariableName" value="expected"/>
+ </module>
+ <module name="CommentsIndentation"/>
+ </module>
+</module>
1
0
[metrics-lib/master] Tweaked file structure and added cobertura.
by karsten@torproject.org 06 Jul '16
by karsten@torproject.org 06 Jul '16
06 Jul '16
commit 97cf26dc47b25477e2fc867336ae6ab200cdd31a
Author: iwakeh <iwakeh(a)torproject.org>
Date: Tue Jul 5 20:54:01 2016 +0000
Tweaked file structure and added cobertura.
---
build.xml | 104 +++++++++++++++++++++++++++++++++------
resources/overview.html | 69 --------------------------
src/main/resources/overview.html | 69 ++++++++++++++++++++++++++
3 files changed, 158 insertions(+), 84 deletions(-)
diff --git a/build.xml b/build.xml
index f24c293..3f62199 100644
--- a/build.xml
+++ b/build.xml
@@ -1,13 +1,18 @@
<project default="jar" name="descriptor" basedir=".">
<property name="release.version" value="1.2.0-dev" />
<property name="sources" value="src/main/java"/>
- <property name="resources" value="resources"/>
+ <property name="resources" value="src/main/resources"/>
<property name="generated" value="generated/"/>
- <property name="classes" value="classes"/>
- <property name="docs" value="javadoc"/>
+ <property name="classes" value="${generated}/classes"/>
+ <property name="docs" value="${generated}/javadoc"/>
<property name="tests" value="src/test/java"/>
<property name="testresources" value="src/test/resources/"/>
+ <property name="testresult" value="${generated}/test-results"/>
+ <property name="coverageresult" value="${generated}/coverage-report/"/>
+ <property name="instrument" value="${generated}/instrument/"/>
+ <property name="testclasses" value="${generated}/testclasses/"/>
<property name="libs" value="lib"/>
+ <property name="cobertura.ser.file" value="${basedir}/cobertura.ser" />
<property name="jarfile" value="descriptor-${release.version}.jar" />
<property name="jarsourcesfile"
value="descriptor-${release.version}-sources.jar" />
@@ -16,13 +21,15 @@
value="descriptor-${release.version}.tar.gz" />
<property file="build.properties" />
+ <patternset id="runtime" >
+ <include name="commons-compress-1.9.jar"/>
+ <include name="xz-1.5.jar"/>
+ </patternset>
+
<path id="classpath">
<pathelement path="${classes}"/>
<fileset dir="${libs}">
- <include name="commons-compress-1.4.1.jar"/>
- <include name="junit4-4.10.jar"/>
- <include name="hamcrest-core-1.2.jar"/>
- <include name="xz-1.0.jar"/>
+ <patternset refid="runtime" />
</fileset>
</path>
@@ -32,10 +39,44 @@
</fileset>
</path>
+ <path id="test.classpath">
+ <pathelement path="${classes}"/>
+ <pathelement path="${testclasses}"/>
+ <pathelement path="${testresources}"/>
+ <fileset dir="${libs}">
+ <patternset refid="runtime" />
+ </fileset>
+ <fileset dir="${libs}">
+ <include name="junit4-4.11.jar"/>
+ <include name="hamcrest-all-1.3.jar"/>
+ </fileset>
+ </path>
+
+ <path id="cobertura.classpath">
+ <fileset dir="${libs}">
+ <include name="cobertura-2.1.1.jar" />
+ <include name="slf4j-api-1.7.7.jar" />
+ <include name="commons-lang3-3.3.2.jar" />
+ <include name="asm4-5.0.3.jar" />
+ <include name="asm4-util-5.0.3.jar" />
+ <include name="asm4-tree-5.0.3.jar" />
+ <include name="asm4-commons-5.0.3.jar" />
+ <include name="asm4-analysis-5.0.3.jar" />
+ <include name="oro-2.0.8.jar" />
+ <include name="logback-core-1.1.2.jar" />
+ <include name="logback-classic-1.1.2.jar" />
+ </fileset>
+ </path>
+
+ <path id="cobertura.test.classpath">
+ <path location="${instrument}" />
+ <path refid="test.classpath" />
+ <path refid="cobertura.classpath" />
+ </path>
+
<target name="clean" >
- <delete includeEmptyDirs="true">
- <fileset dir="${classes}" defaultexcludes="false" includes="**" />
- <fileset dir="${docs}" defaultexcludes="false" includes="**" />
+ <delete includeEmptyDirs="true" quiet="true">
+ <fileset dir="${generated}" defaultexcludes="false" includes="**" />
</delete>
<delete file="${jarfile}"/>
<delete file="${jarsourcesfile}"/>
@@ -44,8 +85,10 @@
<target name="init">
<mkdir dir="${classes}"/>
+ <mkdir dir="${testclasses}"/>
<mkdir dir="${docs}"/>
- <mkdir dir="${generated}"/>
+ <mkdir dir="${testresult}"/>
+ <mkdir dir="${instrument}"/>
</target>
<target name="compile"
@@ -78,7 +121,7 @@
</target>
<target name="testcompile" depends="init">
- <javac destdir="${classes}"
+ <javac destdir="${testclasses}"
srcdir="${tests}"
source="${source-and-target-java-version}"
target="${source-and-target-java-version}"
@@ -87,16 +130,16 @@
optimize="false"
failonerror="true"
includeantruntime="false">
- <classpath refid="classpath"/>
+ <classpath refid="test.classpath"/>
</javac>
</target>
<target name="test" depends="compile,testcompile">
<junit fork="true" haltonfailure="true" printsummary="off">
- <classpath refid="classpath"/>
+ <classpath refid="test.classpath"/>
<formatter type="plain" usefile="false"/>
<batchtest>
- <fileset dir="${classes}"
+ <fileset dir="${testclasses}"
includes="**/*Test.class"/>
</batchtest>
</junit>
@@ -184,5 +227,36 @@
prefix="descriptor-${release.version}/${libs}" />
</tar>
</target>
+
+ <taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
+ <target name="coverage" depends="compile,testcompile">
+ <copy todir="${instrument}" >
+ <fileset dir="${classes}"/>
+ </copy>
+ <cobertura-instrument ignoreTrivial="true">
+ <fileset dir="${instrument}">
+ <include name="**/**/*.class" />
+ </fileset>
+ </cobertura-instrument>
+ <junit fork="true" haltonfailure="false" printsummary="on">
+ <sysproperty key="net.sourceforge.cobertura.datafile"
+ file="${cobertura.ser.file}" />
+ <classpath refid="cobertura.test.classpath" />
+ <formatter type="xml" />
+ <batchtest toDir="${testresult}" >
+ <fileset dir="${testclasses}"
+ includes="**/*Test.class"/>
+ </batchtest>
+ </junit>
+ <cobertura-report format="html" destdir="${coverageresult}" >
+ <fileset dir="${sources}">
+ <include name="**/*.java" />
+ </fileset>
+ </cobertura-report>
+ <cobertura-check branchrate="0" totallinerate="31" totalbranchrate="41" >
+ <regex pattern="org.torproject.descriptor.benchmark.*" branchrate="0" linerate="0"/>
+ </cobertura-check>
+ </target>
+
</project>
diff --git a/resources/overview.html b/resources/overview.html
deleted file mode 100644
index f5c6b51..0000000
--- a/resources/overview.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<html>
-<body>
-<p>DescripTor API, which is provided and supported by Tor's Metrics
-Team, is a library to obtain and process descriptors containing Tor
-network data. It is the main Java tool for processing Tor descriptors
-and provides a standard API consisting of interfaces and a reference
-implementation for all of them.</p>
-
-<p>Most Tor descriptors understood by this library are specified in the
-<a href="https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt">Tor
-directory protocol, version 3</a> or in the earlier
-<a href="https://gitweb.torproject.org/torspec.git/tree/attic/dir-spec-v2.txt">version 2</a> or
-<a href="https://gitweb.torproject.org/torspec.git/tree/attic/dir-spec-v1.txt">version 1</a>
-of that document.
-Other descriptors are specified on the
-<a href="https://collector.torproject.org/">CollecTor website</a>.</p>
-
-<p>The interfaces in
-<a href="./org/torproject/descriptor/package-summary.html">{@code org.torproject.descriptor}</a>
-as well as their implementations in the
-{@code org.torproject.descriptor.impl} package were driven by two main
-goals originating from the primary use case to make Tor network data
-accessible for statistical analysis:</p>
-
-<ul>
-<li><em>Complete coverage:</em> This library is supposed to cover the
-complete range of Tor descriptors made available by the
-<a href="https://collector.torproject.org">CollecTor</a> service.</li>
-<li><em>Runtime and memory efficiency:</em> Processing large amounts of
-descriptors in bulk is supposed to be efficient in terms of runtime and
-required memory.</li>
-</ul>
-
-<p>At the same time the current design and implementation were done with a
-number of non-goals in mind, even though some of these might turn into
-goals in the future:</p>
-
-<p><ul>
-<li><em>Verification:</em> The descriptor parser performs some basic
-verifications of descriptor formats, but no cryptographic verifications.
-It may not even be possible to write a cryptographic verification tool
-using parsed descriptor contents, though this has not been attempted
-yet.</li>
-<li><em>Potentially lossy conversion:</em> Descriptor contents may be
-converted to a format that is easier to process, even if that conversion
-makes it harder or impossible to re-create the original descriptor
-contents from a parsed descriptor.</li>
-<li><em>Generating descriptors:</em> This library does not contain any
-functionality to generate new descriptors for testing or related purposes,
-neither from previously set data nor randomly.</li>
-<li><em>Writing descriptors:</em> This library does not support writing
-descriptors to the file system or a database, both of which are left to
-the application. Stated differently, there are no descriptor sinks that
-would correspond to the provided descriptor sources.</li>
-</ul></p>
-
-<p>Hints about using DescripTor can be found in the
-<a href="./org/torproject/descriptor/package-summary.html#package.description">{@code org.torproject.descriptor} package description</a>.
-</p>
-
-<p>Contact and further information:
-<ul>
-<li><a href="https://trac.torproject.org/projects/tor/query?status=!closed&component=Met…">DescripTor Bug Tracker</a></li>
-<li><a href="https://lists.torproject.org/cgi-bin/mailman/listinfo/metrics-team">Metrics Team Mailing List</a></li>
-<li><a href="https://trac.torproject.org/projects/tor/wiki/org/teams/MetricsTeam">Metrics Team Website</a></li>
-</ul>
-</body>
-</html>
-
diff --git a/src/main/resources/overview.html b/src/main/resources/overview.html
new file mode 100644
index 0000000..f5c6b51
--- /dev/null
+++ b/src/main/resources/overview.html
@@ -0,0 +1,69 @@
+<html>
+<body>
+<p>DescripTor API, which is provided and supported by Tor's Metrics
+Team, is a library to obtain and process descriptors containing Tor
+network data. It is the main Java tool for processing Tor descriptors
+and provides a standard API consisting of interfaces and a reference
+implementation for all of them.</p>
+
+<p>Most Tor descriptors understood by this library are specified in the
+<a href="https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt">Tor
+directory protocol, version 3</a> or in the earlier
+<a href="https://gitweb.torproject.org/torspec.git/tree/attic/dir-spec-v2.txt">version 2</a> or
+<a href="https://gitweb.torproject.org/torspec.git/tree/attic/dir-spec-v1.txt">version 1</a>
+of that document.
+Other descriptors are specified on the
+<a href="https://collector.torproject.org/">CollecTor website</a>.</p>
+
+<p>The interfaces in
+<a href="./org/torproject/descriptor/package-summary.html">{@code org.torproject.descriptor}</a>
+as well as their implementations in the
+{@code org.torproject.descriptor.impl} package were driven by two main
+goals originating from the primary use case to make Tor network data
+accessible for statistical analysis:</p>
+
+<ul>
+<li><em>Complete coverage:</em> This library is supposed to cover the
+complete range of Tor descriptors made available by the
+<a href="https://collector.torproject.org">CollecTor</a> service.</li>
+<li><em>Runtime and memory efficiency:</em> Processing large amounts of
+descriptors in bulk is supposed to be efficient in terms of runtime and
+required memory.</li>
+</ul>
+
+<p>At the same time the current design and implementation were done with a
+number of non-goals in mind, even though some of these might turn into
+goals in the future:</p>
+
+<p><ul>
+<li><em>Verification:</em> The descriptor parser performs some basic
+verifications of descriptor formats, but no cryptographic verifications.
+It may not even be possible to write a cryptographic verification tool
+using parsed descriptor contents, though this has not been attempted
+yet.</li>
+<li><em>Potentially lossy conversion:</em> Descriptor contents may be
+converted to a format that is easier to process, even if that conversion
+makes it harder or impossible to re-create the original descriptor
+contents from a parsed descriptor.</li>
+<li><em>Generating descriptors:</em> This library does not contain any
+functionality to generate new descriptors for testing or related purposes,
+neither from previously set data nor randomly.</li>
+<li><em>Writing descriptors:</em> This library does not support writing
+descriptors to the file system or a database, both of which are left to
+the application. Stated differently, there are no descriptor sinks that
+would correspond to the provided descriptor sources.</li>
+</ul></p>
+
+<p>Hints about using DescripTor can be found in the
+<a href="./org/torproject/descriptor/package-summary.html#package.description">{@code org.torproject.descriptor} package description</a>.
+</p>
+
+<p>Contact and further information:
+<ul>
+<li><a href="https://trac.torproject.org/projects/tor/query?status=!closed&component=Met…">DescripTor Bug Tracker</a></li>
+<li><a href="https://lists.torproject.org/cgi-bin/mailman/listinfo/metrics-team">Metrics Team Mailing List</a></li>
+<li><a href="https://trac.torproject.org/projects/tor/wiki/org/teams/MetricsTeam">Metrics Team Website</a></li>
+</ul>
+</body>
+</html>
+
1
0
06 Jul '16
commit 732686a8dcc091330185c58adb95f14632fd4add
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Tue Jul 5 22:39:49 2016 +0200
Add changelog entry for #19571.
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08ad726..9785681 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,10 @@
which may be longer or shorter than 20 bytes.
- Parse "tunnelled-dir-server" lines in server descriptors.
+ * Minor changes
+ - Stop reporting "-----END .*-----" lines in v2 network statuses as
+ unrecognized.
+
# Changes in version 1.2.0 - 2016-05-31
1
0
[metrics-lib/master] Fix most of the style issues found by checkstyle.
by karsten@torproject.org 06 Jul '16
by karsten@torproject.org 06 Jul '16
06 Jul '16
commit 395aedf194918afa8a436d34bb01f49d0187321f
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Tue Jul 5 15:50:07 2016 +0200
Fix most of the style issues found by checkstyle.
Fixed the following number of issues per type:
1131 Indentation
431 EmptyLineSeparator
95 CustomImportOrder
76 OperatorWrap
54 SeparatorWrap
25 LocalVariableName
14 ModifierOrder
13 MultipleVariableDeclarations
5 WhitespaceAround
5 JavadocTagContinuationIndentation
4 EmptyCatchBlock
2 VariableDeclarationUsageDistance
2 MissingSwitchDefault
1 (... types with single occurrence omitted...)
---
.../descriptor/DescriptorParseException.java | 2 +
.../descriptor/DescriptorSourceFactory.java | 54 +-
.../java/org/torproject/descriptor/ExitList.java | 2 +-
.../descriptor/RelayNetworkStatusConsensus.java | 4 +-
.../descriptor/RelayNetworkStatusVote.java | 4 +-
.../descriptor/impl/BandwidthHistoryImpl.java | 21 +-
.../descriptor/impl/BlockingIteratorImpl.java | 2 +
.../impl/BridgeExtraInfoDescriptorImpl.java | 7 +-
.../descriptor/impl/BridgeNetworkStatusImpl.java | 115 +++--
.../descriptor/impl/BridgePoolAssignmentImpl.java | 11 +-
.../impl/BridgeServerDescriptorImpl.java | 7 +-
.../descriptor/impl/DescriptorCollectorImpl.java | 21 +-
.../descriptor/impl/DescriptorDownloaderImpl.java | 25 +-
.../descriptor/impl/DescriptorFileImpl.java | 25 +-
.../torproject/descriptor/impl/DescriptorImpl.java | 165 ++++---
.../descriptor/impl/DescriptorParseException.java | 5 +-
.../descriptor/impl/DescriptorParserImpl.java | 7 +-
.../descriptor/impl/DescriptorReaderImpl.java | 77 ++-
.../descriptor/impl/DescriptorRequestImpl.java | 27 +-
.../descriptor/impl/DirSourceEntryImpl.java | 76 +--
.../descriptor/impl/DirectoryDownloader.java | 13 +-
.../impl/DirectoryKeyCertificateImpl.java | 146 +++---
.../descriptor/impl/DirectorySignatureImpl.java | 87 ++--
.../descriptor/impl/DownloadCoordinator.java | 1 +
.../descriptor/impl/DownloadCoordinatorImpl.java | 87 ++--
.../descriptor/impl/ExitListEntryImpl.java | 23 +-
.../torproject/descriptor/impl/ExitListImpl.java | 18 +-
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 548 ++++++++++++---------
.../descriptor/impl/MicrodescriptorImpl.java | 158 +++---
.../descriptor/impl/NetworkStatusEntryImpl.java | 133 ++---
.../descriptor/impl/NetworkStatusImpl.java | 35 +-
.../torproject/descriptor/impl/ParseHelper.java | 63 ++-
.../descriptor/impl/RelayDirectoryImpl.java | 241 ++++-----
.../impl/RelayExtraInfoDescriptorImpl.java | 7 +-
.../impl/RelayNetworkStatusConsensusImpl.java | 165 ++++---
.../descriptor/impl/RelayNetworkStatusImpl.java | 203 ++++----
.../impl/RelayNetworkStatusVoteImpl.java | 350 +++++++------
.../descriptor/impl/RelayServerDescriptorImpl.java | 7 +-
.../descriptor/impl/RouterStatusEntryImpl.java | 5 +
.../descriptor/impl/ServerDescriptorImpl.java | 421 +++++++++-------
.../descriptor/impl/TorperfResultImpl.java | 208 ++++----
41 files changed, 2028 insertions(+), 1548 deletions(-)
diff --git a/src/main/java/org/torproject/descriptor/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
index 309d3f7..731db3e 100644
--- a/src/main/java/org/torproject/descriptor/DescriptorParseException.java
+++ b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
@@ -12,7 +12,9 @@ package org.torproject.descriptor;
@SuppressWarnings("deprecation")
public class DescriptorParseException
extends org.torproject.descriptor.impl.DescriptorParseException {
+
private static final long serialVersionUID = 100L;
+
public DescriptorParseException(String message) {
super(message);
}
diff --git a/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
index af13f39..f51434f 100644
--- a/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
+++ b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
@@ -44,7 +44,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String DOWNLOADER_DEFAULT =
+ public static final String DOWNLOADER_DEFAULT =
"org.torproject.descriptor.impl.DescriptorDownloaderImpl";
/**
@@ -53,7 +53,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String PARSER_DEFAULT =
+ public static final String PARSER_DEFAULT =
"org.torproject.descriptor.impl.DescriptorParserImpl";
/**
@@ -62,7 +62,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String READER_DEFAULT =
+ public static final String READER_DEFAULT =
"org.torproject.descriptor.impl.DescriptorReaderImpl";
/**
@@ -71,7 +71,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String COLLECTOR_DEFAULT =
+ public static final String COLLECTOR_DEFAULT =
"org.torproject.descriptor.impl.DescriptorCollectorImpl";
/**
@@ -81,7 +81,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String PARSER_PROPERTY = "descriptor.parser";
+ public static final String PARSER_PROPERTY = "descriptor.parser";
/**
* Property name for overriding the implementation of the
@@ -90,7 +90,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String READER_PROPERTY = "descriptor.reader";
+ public static final String READER_PROPERTY = "descriptor.reader";
/**
* Property name for overriding the implementation of the
@@ -99,7 +99,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String DOWNLOADER_PROPERTY =
+ public static final String DOWNLOADER_PROPERTY =
"descriptor.downloader";
/**
@@ -109,7 +109,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static String COLLECTOR_PROPERTY = "descriptor.collector";
+ public static final String COLLECTOR_PROPERTY = "descriptor.collector";
/**
* Create a new {@link DescriptorParser} by instantiating the class in
@@ -117,7 +117,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static DescriptorParser createDescriptorParser() {
+ public static final DescriptorParser createDescriptorParser() {
return (DescriptorParser) retrieve(PARSER_PROPERTY);
}
@@ -127,7 +127,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static DescriptorReader createDescriptorReader() {
+ public static final DescriptorReader createDescriptorReader() {
return (DescriptorReader) retrieve(READER_PROPERTY);
}
@@ -137,7 +137,7 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static DescriptorDownloader createDescriptorDownloader() {
+ public static final DescriptorDownloader createDescriptorDownloader() {
return (DescriptorDownloader) retrieve(DOWNLOADER_PROPERTY);
}
@@ -147,30 +147,30 @@ public final class DescriptorSourceFactory {
*
* @since 1.0.0
*/
- public final static DescriptorCollector createDescriptorCollector() {
+ public static final DescriptorCollector createDescriptorCollector() {
return (DescriptorCollector) retrieve(COLLECTOR_PROPERTY);
}
- private final static <T> Object retrieve(String type) {
+ private static final <T> Object retrieve(String type) {
Object object;
String clazzName = null;
try {
switch (type) {
- case PARSER_PROPERTY:
- clazzName = System.getProperty(type, PARSER_DEFAULT);
- break;
- case DOWNLOADER_PROPERTY:
- clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
- break;
- case READER_PROPERTY:
- clazzName = System.getProperty(type, READER_DEFAULT);
- break;
- case COLLECTOR_PROPERTY:
- clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
- break;
+ case PARSER_PROPERTY:
+ clazzName = System.getProperty(type, PARSER_DEFAULT);
+ break;
+ case DOWNLOADER_PROPERTY:
+ clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
+ break;
+ case READER_PROPERTY:
+ clazzName = System.getProperty(type, READER_DEFAULT);
+ break;
+ case COLLECTOR_PROPERTY:
+ clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
+ break;
}
- object = ClassLoader.getSystemClassLoader().loadClass(clazzName).
- newInstance();
+ object = ClassLoader.getSystemClassLoader().loadClass(clazzName)
+ .newInstance();
} catch (ClassNotFoundException ex) {
throw new ImplementationNotAccessibleException("Cannot load class "
+ clazzName + "for type " + type, ex);
diff --git a/src/main/java/org/torproject/descriptor/ExitList.java b/src/main/java/org/torproject/descriptor/ExitList.java
index 2a5cb2e..181da0c 100644
--- a/src/main/java/org/torproject/descriptor/ExitList.java
+++ b/src/main/java/org/torproject/descriptor/ExitList.java
@@ -19,7 +19,7 @@ public interface ExitList extends Descriptor {
*
* @since 1.0.0
*/
- public final static String EOL = "\n";
+ public static final String EOL = "\n";
/**
* Exit list entry containing results from a single exit scan.
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
index 15fdaca..b38bd1d 100644
--- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -187,8 +187,8 @@ public interface RelayNetworkStatusConsensus extends Descriptor {
* directory protocol, encoded as 40 upper-case hexadecimal characters.
*
* @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by an authority using the same
- * identity key digest and different algorithms.
+ * arbitrary number of signatures made by an authority using the
+ * same identity key digest and different algorithms.
*
* @since 1.0.0
*/
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
index 1f77db6..abb7ff0 100644
--- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -389,8 +389,8 @@ public interface RelayNetworkStatusVote extends Descriptor {
* characters.
*
* @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by the authority using the same
- * identity key digest and different algorithms.
+ * arbitrary number of signatures made by the authority using the
+ * same identity key digest and different algorithms.
*
* @since 1.0.0
*/
diff --git a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
index 295e0a4..0d998f6 100644
--- a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
@@ -1,13 +1,14 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.BandwidthHistory;
import org.torproject.descriptor.DescriptorParseException;
+
import java.util.SortedMap;
import java.util.TreeMap;
-import org.torproject.descriptor.BandwidthHistory;
-
public class BandwidthHistoryImpl implements BandwidthHistory {
protected BandwidthHistoryImpl(String line, String lineNoOpt,
@@ -18,17 +19,17 @@ public class BandwidthHistoryImpl implements BandwidthHistory {
try {
this.historyEndMillis = ParseHelper.parseTimestampAtIndex(line,
partsNoOpt, 1, 2);
- if (partsNoOpt[3].startsWith("(") &&
- partsNoOpt[4].startsWith("s)")) {
- this.intervalLength = Long.parseLong(partsNoOpt[3].
- substring(1));
+ if (partsNoOpt[3].startsWith("(")
+ && partsNoOpt[4].startsWith("s)")) {
+ this.intervalLength = Long.parseLong(partsNoOpt[3]
+ .substring(1));
if (this.intervalLength <= 0L) {
throw new DescriptorParseException("Only positive interval "
+ "lengths are allowed in line '" + line + "'.");
}
String[] values = null;
- if (partsNoOpt.length == 5 &&
- partsNoOpt[4].equals("s)")) {
+ if (partsNoOpt.length == 5
+ && partsNoOpt[4].equals("s)")) {
/* There are no bandwidth values to parse. */
isValid = true;
} else if (partsNoOpt.length == 6) {
@@ -66,24 +67,28 @@ public class BandwidthHistoryImpl implements BandwidthHistory {
}
private String line;
+
@Override
public String getLine() {
return this.line;
}
private long historyEndMillis;
+
@Override
public long getHistoryEndMillis() {
return this.historyEndMillis;
}
private long intervalLength;
+
@Override
public long getIntervalLength() {
return this.intervalLength;
}
private long[] bandwidthValues;
+
@Override
public SortedMap<Long, Long> getBandwidthValues() {
SortedMap<Long, Long> result = new TreeMap<>();
diff --git a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
index 66426d8..c9e0590 100644
--- a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
@@ -1,5 +1,6 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import java.util.Iterator;
@@ -46,6 +47,7 @@ public class BlockingIteratorImpl<T> implements Iterator<T> {
/* Signalize that there won't be any further objects to be enqueued. */
private boolean outOfDescriptors = false;
+
protected synchronized void setOutOfDescriptors() {
if (this.outOfDescriptors) {
throw new IllegalStateException("Internal error: Sending "
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
index 15d40d8..81d0623 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
@@ -1,14 +1,15 @@
/* Copyright 2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import java.util.ArrayList;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.BridgeExtraInfoDescriptor;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.ExtraInfoDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
public class BridgeExtraInfoDescriptorImpl
extends ExtraInfoDescriptorImpl implements BridgeExtraInfoDescriptor {
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
index bf3804d..49abc5c 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -1,8 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.BridgeNetworkStatus;
import org.torproject.descriptor.DescriptorParseException;
+
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -11,8 +14,6 @@ import java.util.Scanner;
import java.util.SortedMap;
import java.util.TimeZone;
-import org.torproject.descriptor.BridgeNetworkStatus;
-
/* Contains a bridge network status. */
public class BridgeNetworkStatusImpl extends NetworkStatusImpl
implements BridgeNetworkStatus {
@@ -31,9 +32,9 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
* "published" line. */
return;
}
- if (fileName.length() ==
- "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D".
- length()) {
+ if (fileName.length()
+ == "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D"
+ .length()) {
String publishedString = fileName.substring(0,
"yyyyMMdd-HHmmss".length());
try {
@@ -41,8 +42,8 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
"yyyyMMdd-HHmmss");
fileNameFormat.setLenient(false);
fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- this.publishedMillis = fileNameFormat.parse(publishedString).
- getTime();
+ this.publishedMillis = fileNameFormat.parse(publishedString)
+ .getTime();
} catch (ParseException e) {
}
}
@@ -74,22 +75,22 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in bridge network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in bridge network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
@@ -111,36 +112,36 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
try {
for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue()
+ .replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
}
}
} catch (NumberFormatException ex) {
@@ -168,60 +169,70 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private long stableUptime;
+
@Override
public long getStableUptime() {
return this.stableUptime;
}
private long stableMtbf;
+
@Override
public long getStableMtbf() {
return this.stableMtbf;
}
private long fastBandwidth;
+
@Override
public long getFastBandwidth() {
return this.fastBandwidth;
}
private double guardWfu;
+
@Override
public double getGuardWfu() {
return this.guardWfu;
}
private long guardTk;
+
@Override
public long getGuardTk() {
return this.guardTk;
}
private long guardBandwidthIncludingExits;
+
@Override
public long getGuardBandwidthIncludingExits() {
return this.guardBandwidthIncludingExits;
}
private long guardBandwidthExcludingExits;
+
@Override
public long getGuardBandwidthExcludingExits() {
return this.guardBandwidthExcludingExits;
}
private int enoughMtbfInfo;
+
@Override
public int getEnoughMtbfInfo() {
return this.enoughMtbfInfo;
}
private int ignoringAdvertisedBws;
+
@Override
public int getIgnoringAdvertisedBws() {
return this.ignoringAdvertisedBws;
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
index 99578e8..f28dcce 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
@@ -1,8 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.BridgePoolAssignment;
import org.torproject.descriptor.DescriptorParseException;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -12,8 +15,6 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import org.torproject.descriptor.BridgePoolAssignment;
-
/* TODO Write a test class. */
public class BridgePoolAssignmentImpl extends DescriptorImpl
implements BridgePoolAssignment {
@@ -48,8 +49,8 @@ public class BridgePoolAssignmentImpl extends DescriptorImpl
}
private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes))
+ .useDelimiter("\n");
while (s.hasNext()) {
String line = s.next();
if (line.startsWith("bridge-pool-assignment ")) {
@@ -85,12 +86,14 @@ public class BridgePoolAssignmentImpl extends DescriptorImpl
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private SortedMap<String, String> entries = new TreeMap<>();
+
@Override
public SortedMap<String, String> getEntries() {
return new TreeMap<>(this.entries);
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
index eb2b933..f9e9ba8 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
@@ -1,14 +1,15 @@
/* Copyright 2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import java.util.ArrayList;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.BridgeServerDescriptor;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.ServerDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
public class BridgeServerDescriptorImpl extends ServerDescriptorImpl
implements BridgeServerDescriptor {
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
index 1a030ef..5776827 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
@@ -1,9 +1,12 @@
/* Copyright 2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
-import java.io.BufferedOutputStream;
+import org.torproject.descriptor.DescriptorCollector;
+
import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
@@ -26,8 +29,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
-import org.torproject.descriptor.DescriptorCollector;
-
public class DescriptorCollectorImpl implements DescriptorCollector {
@Override
@@ -51,8 +52,8 @@ public class DescriptorCollectorImpl implements DescriptorCollector {
this.fetchRemoteDirectories(collecTorBaseUrl, remoteDirectories);
SortedSet<String> parsedDirectories = new TreeSet<>();
SortedMap<String, Long> remoteFiles = new TreeMap<>();
- for (Map.Entry<String, String> e :
- fetchedDirectoryListings.entrySet()) {
+ for (Map.Entry<String, String> e
+ : fetchedDirectoryListings.entrySet()) {
String remoteDirectory = e.getKey();
String directoryListing = e.getValue();
SortedMap<String, Long> parsedRemoteFiles =
@@ -174,9 +175,9 @@ public class DescriptorCollectorImpl implements DescriptorCollector {
for (Map.Entry<String, Long> e : remoteFiles.entrySet()) {
String filename = e.getKey();
long lastModifiedMillis = e.getValue();
- if (lastModifiedMillis < minLastModified ||
- (localFiles.containsKey(filename) &&
- localFiles.get(filename) >= lastModifiedMillis)) {
+ if (lastModifiedMillis < minLastModified
+ || (localFiles.containsKey(filename)
+ && localFiles.get(filename) >= lastModifiedMillis)) {
continue;
}
String url = collecTorBaseUrl + filename;
@@ -206,8 +207,8 @@ public class DescriptorCollectorImpl implements DescriptorCollector {
int responseCode = huc.getResponseCode();
if (responseCode == 200) {
InputStream is;
- if (huc.getContentEncoding() != null &&
- huc.getContentEncoding().equalsIgnoreCase("gzip")) {
+ if (huc.getContentEncoding() != null
+ && huc.getContentEncoding().equalsIgnoreCase("gzip")) {
is = new GZIPInputStream(huc.getInputStream());
} else {
is = huc.getInputStream();
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
index e726ce9..637c80e 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
@@ -1,16 +1,17 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorDownloader;
+import org.torproject.descriptor.DescriptorRequest;
+
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DescriptorDownloader;
-
public class DescriptorDownloaderImpl
implements DescriptorDownloader {
@@ -18,6 +19,7 @@ public class DescriptorDownloaderImpl
private SortedMap<String, DirectoryDownloader> directoryAuthorities =
new TreeMap<>();
+
@Override
public void addDirectoryAuthority(String nickname, String ip,
int dirPort) {
@@ -33,6 +35,7 @@ public class DescriptorDownloaderImpl
private SortedMap<String, DirectoryDownloader> directoryMirrors =
new TreeMap<>();
+
@Override
public void addDirectoryMirror(String nickname, String ip,
int dirPort) {
@@ -67,14 +70,15 @@ public class DescriptorDownloaderImpl
}
/* TODO Relax the requirement for directory nicknames to be unique.
* In theory, we can identify them by ip+port. */
- if (this.directoryAuthorities.containsKey(nickname) ||
- this.directoryMirrors.containsKey(nickname)) {
+ if (this.directoryAuthorities.containsKey(nickname)
+ || this.directoryMirrors.containsKey(nickname)) {
throw new IllegalArgumentException("Directory nicknames must be "
+ "unique.");
}
}
private boolean downloadConsensus = false;
+
@Override
public void setIncludeCurrentConsensus() {
if (this.hasStartedDownloading) {
@@ -85,6 +89,7 @@ public class DescriptorDownloaderImpl
}
private boolean downloadConsensusFromAllAuthorities = false;
+
@Override
public void setIncludeCurrentConsensusFromAllDirectoryAuthorities() {
if (this.hasStartedDownloading) {
@@ -95,6 +100,7 @@ public class DescriptorDownloaderImpl
}
private boolean includeCurrentReferencedVotes = false;
+
@Override
public void setIncludeCurrentReferencedVotes() {
if (this.hasStartedDownloading) {
@@ -105,6 +111,7 @@ public class DescriptorDownloaderImpl
}
private Set<String> downloadVotes = new HashSet<>();
+
@Override
public void setIncludeCurrentVote(String fingerprint) {
if (this.hasStartedDownloading) {
@@ -207,6 +214,7 @@ public class DescriptorDownloaderImpl
}
private long readTimeoutMillis = 60L * 1000L;
+
@Override
public void setReadTimeout(long readTimeoutMillis) {
if (this.hasStartedDownloading) {
@@ -222,6 +230,7 @@ public class DescriptorDownloaderImpl
}
private long connectTimeoutMillis = 60L * 1000L;
+
@Override
public void setConnectTimeout(long connectTimeoutMillis) {
if (this.hasStartedDownloading) {
@@ -237,6 +246,7 @@ public class DescriptorDownloaderImpl
}
private long globalTimeoutMillis = 60L * 60L * 1000L;
+
@Override
public void setGlobalTimeout(long globalTimeoutMillis) {
if (this.hasStartedDownloading) {
@@ -252,6 +262,7 @@ public class DescriptorDownloaderImpl
}
private boolean failUnrecognizedDescriptorLines = false;
+
@Override
public void setFailUnrecognizedDescriptorLines() {
if (this.hasStartedDownloading) {
@@ -275,8 +286,8 @@ public class DescriptorDownloaderImpl
this.includeCurrentReferencedVotes, this.connectTimeoutMillis,
this.readTimeoutMillis, this.globalTimeoutMillis,
this.failUnrecognizedDescriptorLines);
- Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator.
- getDescriptorQueue();
+ Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator
+ .getDescriptorQueue();
return descriptorQueue;
}
}
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
index 801c546..479f855 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
@@ -1,75 +1,90 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-
public class DescriptorFileImpl implements DescriptorFile {
private File directory;
+
protected void setDirectory(File directory) {
this.directory = directory;
}
+
@Override
public File getDirectory() {
return this.directory;
}
private File tarball;
+
protected void setTarball(File tarball) {
this.tarball = tarball;
}
+
@Override
public File getTarball() {
return this.tarball;
}
private File file;
+
protected void setFile(File file) {
this.file = file;
}
+
@Override
public File getFile() {
return this.file;
}
private String fileName;
+
protected void setFileName(String fileName) {
this.fileName = fileName;
}
+
@Override
public String getFileName() {
return this.fileName;
}
private long lastModified;
+
protected void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
+
@Override
public long getLastModified() {
return this.lastModified;
}
private List<Descriptor> descriptors;
+
protected void setDescriptors(List<Descriptor> descriptors) {
this.descriptors = descriptors;
}
+
@Override
public List<Descriptor> getDescriptors() {
- return this.descriptors == null ? new ArrayList<Descriptor>() :
- new ArrayList<>(this.descriptors);
+ return this.descriptors == null ? new ArrayList<Descriptor>()
+ : new ArrayList<>(this.descriptors);
}
private Exception exception;
+
protected void setException(Exception exception) {
this.exception = exception;
}
+
@Override
public Exception getException() {
return this.exception;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
index 5625b3f..a32b6cc 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -1,8 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.Descriptor;
import org.torproject.descriptor.DescriptorParseException;
+
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -11,8 +14,6 @@ import java.util.Map;
import java.util.Scanner;
import java.util.Set;
-import org.torproject.descriptor.Descriptor;
-
public abstract class DescriptorImpl implements Descriptor {
protected static List<Descriptor> parseDescriptors(
@@ -28,21 +29,21 @@ public abstract class DescriptorImpl implements Descriptor {
System.arraycopy(rawDescriptorBytes, 0, first100Chars, 0,
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"))) {
- parsedDescriptors.addAll(RelayNetworkStatusConsensusImpl.
- parseConsensuses(rawDescriptorBytes,
+ 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"))) {
+ 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"))) {
- parsedDescriptors.addAll(RelayNetworkStatusVoteImpl.
- parseVotes(rawDescriptorBytes,
+ || ((firstLines.startsWith("network-status-version 3\n")
+ || firstLines.contains("\nnetwork-status-version 3\n"))
+ && firstLines.contains("\nvote-status vote\n"))) {
+ parsedDescriptors.addAll(RelayNetworkStatusVoteImpl
+ .parseVotes(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
} else if (firstLines.startsWith("@type bridge-network-status 1.")
|| firstLines.startsWith("r ")) {
@@ -50,56 +51,56 @@ public abstract class DescriptorImpl implements Descriptor {
rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines));
} else if (firstLines.startsWith(
"@type bridge-server-descriptor 1.")) {
- parsedDescriptors.addAll(BridgeServerDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
+ parsedDescriptors.addAll(BridgeServerDescriptorImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type server-descriptor 1.") ||
- firstLines.startsWith("router ") ||
- firstLines.contains("\nrouter ")) {
- parsedDescriptors.addAll(RelayServerDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
+ } else if (firstLines.startsWith("@type server-descriptor 1.")
+ || firstLines.startsWith("router ")
+ || firstLines.contains("\nrouter ")) {
+ parsedDescriptors.addAll(RelayServerDescriptorImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
} else if (firstLines.startsWith("@type bridge-extra-info 1.")) {
- parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
+ parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type extra-info 1.") ||
- firstLines.startsWith("extra-info ") ||
- firstLines.contains("\nextra-info ")) {
- parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
+ } else if (firstLines.startsWith("@type extra-info 1.")
+ || firstLines.startsWith("extra-info ")
+ || firstLines.contains("\nextra-info ")) {
+ parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type microdescriptor 1.") ||
- firstLines.startsWith("onion-key\n") ||
- firstLines.contains("\nonion-key\n")) {
- parsedDescriptors.addAll(MicrodescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
+ } else if (firstLines.startsWith("@type microdescriptor 1.")
+ || firstLines.startsWith("onion-key\n")
+ || firstLines.contains("\nonion-key\n")) {
+ parsedDescriptors.addAll(MicrodescriptorImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-pool-assignment 1.") ||
- firstLines.startsWith("bridge-pool-assignment ") ||
- firstLines.contains("\nbridge-pool-assignment ")) {
- parsedDescriptors.addAll(BridgePoolAssignmentImpl.
- parseDescriptors(rawDescriptorBytes,
+ } else if (firstLines.startsWith("@type bridge-pool-assignment 1.")
+ || firstLines.startsWith("bridge-pool-assignment ")
+ || firstLines.contains("\nbridge-pool-assignment ")) {
+ 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 ")) {
- parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
- parseDescriptors(rawDescriptorBytes,
+ } else if (firstLines.startsWith("@type dir-key-certificate-3 1.")
+ || firstLines.startsWith("dir-key-certificate-version ")
+ || firstLines.contains("\ndir-key-certificate-version ")) {
+ parsedDescriptors.addAll(DirectoryKeyCertificateImpl
+ .parseDescriptors(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type tordnsel 1.") ||
- firstLines.startsWith("ExitNode ") ||
- firstLines.contains("\nExitNode ")) {
+ } else if (firstLines.startsWith("@type tordnsel 1.")
+ || firstLines.startsWith("ExitNode ")
+ || firstLines.contains("\nExitNode ")) {
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")) {
+ } else if (firstLines.startsWith("@type network-status-2 1.")
+ || firstLines.startsWith("network-status-version 2\n")
+ || firstLines.contains("\nnetwork-status-version 2\n")) {
parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type directory 1.") ||
- firstLines.startsWith("signed-directory\n") ||
- firstLines.contains("\nsigned-directory\n")) {
+ } else if (firstLines.startsWith("@type directory 1.")
+ || firstLines.startsWith("signed-directory\n")
+ || firstLines.contains("\nsigned-directory\n")) {
parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes,
failUnrecognizedDescriptorLines));
} else if (firstLines.startsWith("@type torperf 1.")) {
@@ -122,10 +123,10 @@ public abstract class DescriptorImpl implements Descriptor {
} catch (UnsupportedEncodingException e) {
return rawDescriptors;
}
- int endAllDescriptors = rawDescriptorBytes.length,
- startAnnotations = 0;
- boolean containsAnnotations = ascii.startsWith("@") ||
- ascii.contains("\n@");
+ int endAllDescriptors = rawDescriptorBytes.length;
+ int startAnnotations = 0;
+ boolean containsAnnotations = ascii.startsWith("@")
+ || ascii.contains("\n@");
while (startAnnotations < endAllDescriptors) {
int startDescriptor;
if (ascii.indexOf(startToken, startAnnotations) == 0) {
@@ -159,6 +160,7 @@ public abstract class DescriptorImpl implements Descriptor {
}
protected byte[] rawDescriptorBytes;
+
@Override
public byte[] getRawDescriptorBytes() {
return this.rawDescriptorBytes;
@@ -167,10 +169,11 @@ public abstract class DescriptorImpl implements Descriptor {
protected boolean failUnrecognizedDescriptorLines = false;
protected List<String> unrecognizedLines;
+
@Override
public List<String> getUnrecognizedLines() {
- return this.unrecognizedLines == null ? new ArrayList<String>() :
- new ArrayList<>(this.unrecognizedLines);
+ return this.unrecognizedLines == null ? new ArrayList<String>()
+ : new ArrayList<>(this.unrecognizedLines);
}
protected DescriptorImpl(byte[] rawDescriptorBytes,
@@ -185,12 +188,13 @@ public abstract class DescriptorImpl implements Descriptor {
/* Parse annotation lines from the descriptor bytes. */
private List<String> annotations = new ArrayList<>();
+
private void cutOffAnnotations(byte[] rawDescriptorBytes)
throws DescriptorParseException {
String ascii = new String(rawDescriptorBytes);
int start = 0;
- while ((start == 0 && ascii.startsWith("@")) ||
- (start > 0 && ascii.indexOf("\n@", start - 1) >= 0)) {
+ while ((start == 0 && ascii.startsWith("@"))
+ || (start > 0 && ascii.indexOf("\n@", start - 1) >= 0)) {
int end = ascii.indexOf("\n", start);
if (end < 0) {
throw new DescriptorParseException("Annotation line does not "
@@ -203,26 +207,31 @@ public abstract class DescriptorImpl implements Descriptor {
int length = rawDescriptorBytes.length;
byte[] rawDescriptor = new byte[length - start];
System.arraycopy(rawDescriptorBytes, start, rawDescriptor, 0,
- length - start);
+ length - start);
this.rawDescriptorBytes = rawDescriptor;
}
}
+
@Override
public List<String> getAnnotations() {
return new ArrayList<>(this.annotations);
}
- /* Count parsed keywords for consistency checks by subclasses. */
- private String firstKeyword, lastKeyword;
+ private String firstKeyword;
+
+ private String lastKeyword;
+
private Map<String, Integer> parsedKeywords = new HashMap<>();
+
+ /* Count parsed keywords for consistency checks by subclasses. */
private void countKeywords(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("\n")
+ || descriptorString.contains("\n\n"))) {
throw new DescriptorParseException("Blank lines are not allowed.");
}
boolean skipCrypto = false;
@@ -233,10 +242,10 @@ public abstract class DescriptorImpl implements Descriptor {
skipCrypto = true;
} else if (line.startsWith("-----END")) {
skipCrypto = false;
- } else if (!line.isEmpty() && !line.startsWith("@") &&
- !skipCrypto) {
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
+ } else if (!line.isEmpty() && !line.startsWith("@")
+ && !skipCrypto) {
+ String lineNoOpt = line.startsWith("opt ")
+ ? line.substring("opt ".length()) : line;
String keyword = lineNoOpt.split(" ", -1)[0];
if (keyword.equals("")) {
throw new DescriptorParseException("Illegal keyword in line '"
@@ -257,8 +266,8 @@ public abstract class DescriptorImpl implements Descriptor {
protected void checkFirstKeyword(String keyword)
throws DescriptorParseException {
- if (this.firstKeyword == null ||
- !this.firstKeyword.equals(keyword)) {
+ if (this.firstKeyword == null
+ || !this.firstKeyword.equals(keyword)) {
throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ "be contained in the first line.");
}
@@ -266,8 +275,8 @@ public abstract class DescriptorImpl implements Descriptor {
protected void checkLastKeyword(String keyword)
throws DescriptorParseException {
- if (this.lastKeyword == null ||
- !this.lastKeyword.equals(keyword)) {
+ if (this.lastKeyword == null
+ || !this.lastKeyword.equals(keyword)) {
throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ "be contained in the last line.");
}
@@ -301,8 +310,8 @@ public abstract class DescriptorImpl implements Descriptor {
protected void checkAtMostOnceKeywords(Set<String> keywords)
throws DescriptorParseException {
for (String keyword : keywords) {
- if (this.parsedKeywords.containsKey(keyword) &&
- this.parsedKeywords.get(keyword) > 1) {
+ if (this.parsedKeywords.containsKey(keyword)
+ && this.parsedKeywords.get(keyword) > 1) {
throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ "contained " + this.parsedKeywords.get(keyword) + " times, "
+ "but must be contained at most once.");
@@ -313,8 +322,8 @@ public abstract class DescriptorImpl implements Descriptor {
protected void checkKeywordsDependOn(Set<String> dependentKeywords,
String dependingKeyword) throws DescriptorParseException {
for (String dependentKeyword : dependentKeywords) {
- if (this.parsedKeywords.containsKey(dependentKeyword) &&
- !this.parsedKeywords.containsKey(dependingKeyword)) {
+ if (this.parsedKeywords.containsKey(dependentKeyword)
+ && !this.parsedKeywords.containsKey(dependingKeyword)) {
throw new DescriptorParseException("Keyword '" + dependentKeyword
+ "' is contained, but keyword '" + dependingKeyword + "' is "
+ "not.");
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
index 0f9add2..9015471 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
@@ -1,13 +1,16 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
/**
* @deprecated Replaced by
- * org.torproject.descriptor.DescriptorParseException
+ * org.torproject.descriptor.DescriptorParseException
*/
@Deprecated public class DescriptorParseException extends Exception {
+
private static final long serialVersionUID = 100L;
+
protected DescriptorParseException(String message) {
super(message);
}
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
index 6ac53f8..24cbf15 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
@@ -1,13 +1,14 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.DescriptorParser;
+import java.util.List;
+
public class DescriptorParserImpl implements DescriptorParser {
private boolean failUnrecognizedDescriptorLines;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
index 8da88e9..b77b3b1 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
@@ -1,8 +1,19 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorReader;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
+
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -21,20 +32,12 @@ import java.util.SortedMap;
import java.util.Stack;
import java.util.TreeMap;
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
-import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorReader;
-
public class DescriptorReaderImpl implements DescriptorReader {
private boolean hasStartedReading = false;
private List<File> directories = new ArrayList<>();
+
@Override
public void addDirectory(File directory) {
if (this.hasStartedReading) {
@@ -45,6 +48,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private List<File> tarballs = new ArrayList<>();
+
@Override
public void addTarball(File tarball) {
if (this.hasStartedReading) {
@@ -55,6 +59,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private File historyFile;
+
@Override
public void setExcludeFiles(File historyFile) {
if (this.hasStartedReading) {
@@ -65,6 +70,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private SortedMap<String, Long> excludedFiles;
+
@Override
public void setExcludedFiles(SortedMap<String, Long> excludedFiles) {
if (this.hasStartedReading) {
@@ -93,6 +99,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private boolean failUnrecognizedDescriptorLines = false;
+
@Override
public void setFailUnrecognizedDescriptorLines() {
if (this.hasStartedReading) {
@@ -103,6 +110,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private Integer maxDescriptorFilesInQueue = null;
+
@Override
public void setMaxDescriptorFilesInQueue(int max) {
if (this.hasStartedReading) {
@@ -113,6 +121,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private DescriptorReaderRunnable reader;
+
@Override
public Iterator<DescriptorFile> readDescriptors() {
if (this.hasStartedReading) {
@@ -133,15 +142,25 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
private static class DescriptorReaderRunnable implements Runnable {
+
private List<File> directories;
+
private List<File> tarballs;
+
private BlockingIteratorImpl<DescriptorFile> descriptorQueue;
+
private File historyFile;
- private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>(),
- excludedFilesAfter = new TreeMap<>(),
- parsedFilesAfter = new TreeMap<>();
+
+ private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>();
+
+ private SortedMap<String, Long> excludedFilesAfter = new TreeMap<>();
+
+ private SortedMap<String, Long> parsedFilesAfter = new TreeMap<>();
+
private DescriptorParser descriptorParser;
+
private boolean hasFinishedReading = false;
+
private DescriptorReaderRunnable(List<File> directories,
List<File> tarballs,
BlockingIteratorImpl<DescriptorFile> descriptorQueue,
@@ -158,6 +177,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
this.descriptorParser.setFailUnrecognizedDescriptorLines(
failUnrecognizedDescriptorLines);
}
+
public void run() {
try {
this.readOldHistory();
@@ -178,6 +198,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
this.writeNewHistory();
}
}
+
private void readOldHistory() {
if (this.historyFile == null) {
return;
@@ -203,6 +224,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
/* TODO Handle this exception. */
}
}
+
private void writeNewHistory() {
if (this.historyFile == null) {
return;
@@ -227,6 +249,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
/* TODO Handle this exception. */
}
}
+
private void readDescriptors() {
for (File directory : this.directories) {
if (!directory.exists() || !directory.isDirectory()) {
@@ -239,16 +262,16 @@ public class DescriptorReaderImpl implements DescriptorReader {
File file = files.pop();
if (file.isDirectory()) {
files.addAll(Arrays.asList(file.listFiles()));
- } else if (file.getName().endsWith(".tar") ||
- file.getName().endsWith(".tar.bz2") ||
- file.getName().endsWith(".tar.xz")) {
+ } else if (file.getName().endsWith(".tar")
+ || file.getName().endsWith(".tar.bz2")
+ || file.getName().endsWith(".tar.xz")) {
this.tarballs.add(file);
} else {
String absolutePath = file.getAbsolutePath();
long lastModifiedMillis = file.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
+ if (this.excludedFilesBefore.containsKey(absolutePath)
+ && this.excludedFilesBefore.get(absolutePath)
+ == lastModifiedMillis) {
this.excludedFilesAfter.put(absolutePath,
lastModifiedMillis);
continue;
@@ -272,21 +295,22 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
}
}
+
private void readTarballs() {
List<File> files = new ArrayList<>(this.tarballs);
boolean abortReading = false;
while (!abortReading && !files.isEmpty()) {
File tarball = files.remove(0);
- if (!tarball.getName().endsWith(".tar") &&
- !tarball.getName().endsWith(".tar.bz2") &&
- !tarball.getName().endsWith(".tar.xz")) {
+ if (!tarball.getName().endsWith(".tar")
+ && !tarball.getName().endsWith(".tar.bz2")
+ && !tarball.getName().endsWith(".tar.xz")) {
continue;
}
String absolutePath = tarball.getAbsolutePath();
long lastModifiedMillis = tarball.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
+ if (this.excludedFilesBefore.containsKey(absolutePath)
+ && this.excludedFilesBefore.get(absolutePath)
+ == lastModifiedMillis) {
this.excludedFilesAfter.put(absolutePath, lastModifiedMillis);
continue;
}
@@ -314,8 +338,8 @@ public class DescriptorReaderImpl implements DescriptorReader {
new DescriptorFileImpl();
descriptorFile.setTarball(tarball);
descriptorFile.setFileName(tae.getName());
- descriptorFile.setLastModified(tae.getLastModifiedDate().
- getTime());
+ descriptorFile.setLastModified(tae.getLastModifiedDate()
+ .getTime());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
byte[] data = new byte[1024];
@@ -344,6 +368,7 @@ public class DescriptorReaderImpl implements DescriptorReader {
}
}
}
+
private List<Descriptor> readFile(File file) throws IOException,
DescriptorParseException {
FileInputStream fis = new FileInputStream(file);
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
index 0238f24..f27796f 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
@@ -1,111 +1,134 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.Descriptor;
import org.torproject.descriptor.DescriptorRequest;
+import java.util.List;
+
public class DescriptorRequestImpl implements DescriptorRequest {
private String requestedResource;
+
protected void setRequestedResource(String requestedResource) {
this.requestedResource = requestedResource;
}
+
protected String getRequestedResource() {
return this.requestedResource;
}
private String descriptorType;
+
protected void setDescriptorType(String descriptorType) {
this.descriptorType = descriptorType;
}
+
protected String getDescriptorType() {
return this.descriptorType;
}
private byte[] responseBytes;
+
protected byte[] getResponseBytes() {
return this.responseBytes;
}
+
protected void setResponseBytes(byte[] responseBytes) {
this.responseBytes = responseBytes;
}
private String requestUrl;
+
@Override
public String getRequestUrl() {
return this.requestUrl;
}
private String directoryNickname;
+
protected void setDirectoryNickname(String directoryNickname) {
this.directoryNickname = directoryNickname;
}
+
@Override
public String getDirectoryNickname() {
return this.directoryNickname;
}
private int responseCode;
+
protected void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
+
@Override
public int getResponseCode() {
return this.responseCode;
}
private long requestStart;
+
protected void setRequestStart(long requestStart) {
this.requestStart = requestStart;
}
+
@Override
public long getRequestStart() {
return this.requestStart;
}
private long requestEnd;
+
protected void setRequestEnd(long requestEnd) {
this.requestEnd = requestEnd;
}
+
@Override
public long getRequestEnd() {
return this.requestEnd;
}
private boolean connectTimeoutHasExpired;
+
@Override
public boolean connectTimeoutHasExpired() {
return this.connectTimeoutHasExpired;
}
private boolean readTimeoutHasExpired;
+
@Override
public boolean readTimeoutHasExpired() {
return this.readTimeoutHasExpired;
}
private boolean globalTimeoutHasExpired;
+
@Override
public boolean globalTimeoutHasExpired() {
return this.globalTimeoutHasExpired;
}
private List<Descriptor> descriptors;
+
protected void setDescriptors(List<Descriptor> descriptors) {
this.descriptors = descriptors;
}
+
@Override
public List<Descriptor> getDescriptors() {
return this.descriptors;
}
private Exception exception;
+
protected void setException(Exception exception) {
this.exception = exception;
}
+
@Override
public Exception getException() {
return this.exception;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
index fb2f5ad..2782925 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
@@ -1,26 +1,30 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirSourceEntry;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.torproject.descriptor.DirSourceEntry;
-
public class DirSourceEntryImpl implements DirSourceEntry {
private byte[] dirSourceEntryBytes;
+
@Override
public byte[] getDirSourceEntryBytes() {
return this.dirSourceEntryBytes;
}
private boolean failUnrecognizedDescriptorLines;
+
private List<String> unrecognizedLines;
+
protected List<String> getAndClearUnrecognizedLines() {
List<String> lines = this.unrecognizedLines;
this.unrecognizedLines = null;
@@ -38,7 +42,10 @@ public class DirSourceEntryImpl implements DirSourceEntry {
this.checkAndClearKeywords();
}
- private SortedSet<String> exactlyOnceKeywords, atMostOnceKeywords;
+ private SortedSet<String> exactlyOnceKeywords;
+
+ private SortedSet<String> atMostOnceKeywords;
+
private void initializeKeywords() {
this.exactlyOnceKeywords = new TreeSet<>();
this.exactlyOnceKeywords.add("dir-source");
@@ -76,40 +83,40 @@ public class DirSourceEntryImpl implements DirSourceEntry {
private void parseDirSourceEntryBytes()
throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.dirSourceEntryBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.dirSourceEntryBytes))
+ .useDelimiter("\n");
boolean skipCrypto = false;
while (s.hasNext()) {
String line = s.next();
String[] parts = line.split(" ");
switch (parts[0]) {
- case "dir-source":
- this.parseDirSourceLine(line);
- break;
- case "contact":
- this.parseContactLine(line);
- break;
- case "vote-digest":
- this.parseVoteDigestLine(line);
- break;
- case "-----BEGIN":
- skipCrypto = true;
- break;
- case "-----END":
- skipCrypto = false;
- break;
- default:
- if (!skipCrypto) {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "dir-source":
+ this.parseDirSourceLine(line);
+ break;
+ case "contact":
+ this.parseContactLine(line);
+ break;
+ case "vote-digest":
+ this.parseVoteDigestLine(line);
+ break;
+ case "-----BEGIN":
+ skipCrypto = true;
+ break;
+ case "-----END":
+ skipCrypto = false;
+ break;
+ default:
+ if (!skipCrypto) {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -162,54 +169,63 @@ public class DirSourceEntryImpl implements DirSourceEntry {
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String identity;
+
@Override
public String getIdentity() {
return this.identity;
}
private boolean isLegacy;
+
@Override
public boolean isLegacy() {
return this.isLegacy;
}
private String hostname;
+
@Override
public String getHostname() {
return this.hostname;
}
private String ip;
+
@Override
public String getIp() {
return this.ip;
}
private int dirPort;
+
@Override
public int getDirPort() {
return this.dirPort;
}
private int orPort;
+
@Override
public int getOrPort() {
return this.orPort;
}
private String contactLine;
+
@Override
public String getContactLine() {
return this.contactLine;
}
private String voteDigest;
+
@Override
public String getVoteDigest() {
return this.voteDigest;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
index a27ed76..d3a707a 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
@@ -1,16 +1,17 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorSourceFactory;
+
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.InflaterInputStream;
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorSourceFactory;
-
/* Download descriptors from one directory authority or mirror. First,
* ask the coordinator thread to create a request, run it, and deliver
* the response. Repeat until the coordinator thread says there are no
@@ -18,8 +19,11 @@ import org.torproject.descriptor.DescriptorSourceFactory;
public class DirectoryDownloader implements Runnable {
private String nickname;
+
private String ipPort;
+
private DescriptorParser descriptorParser;
+
protected DirectoryDownloader(String nickname, String ip, int dirPort) {
this.nickname = nickname;
this.ipPort = ip + ":" + String.valueOf(dirPort);
@@ -28,17 +32,20 @@ public class DirectoryDownloader implements Runnable {
}
private DownloadCoordinator downloadCoordinator;
+
protected void setDownloadCoordinator(
DownloadCoordinator downloadCoordinator) {
this.downloadCoordinator = downloadCoordinator;
}
private long connectTimeout;
+
protected void setConnectTimeout(long connectTimeout) {
this.connectTimeout = connectTimeout;
}
private long readTimeout;
+
protected void setReadTimeout(long readTimeout) {
this.readTimeout = readTimeout;
}
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
index b62fc8e..d76b32c 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
@@ -1,7 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectoryKeyCertificate;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -14,9 +18,6 @@ import java.util.Set;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectoryKeyCertificate;
-
/* TODO Add test class. */
public class DirectoryKeyCertificateImpl extends DescriptorImpl
@@ -58,8 +59,8 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
}
private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
StringBuilder crypto = null;
while (s.hasNext()) {
@@ -67,78 +68,78 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-key-published":
- this.parseDirKeyPublishedLine(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublishedLine(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
break;
case "dir-signing-key":
- this.dirSigningKey = cryptoString;
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
break;
case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
break;
case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in directory key certificate.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in directory key certificate.");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in directory key certificate.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in directory key certificate.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -225,8 +226,8 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.certificateDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -240,66 +241,77 @@ public class DirectoryKeyCertificateImpl extends DescriptorImpl
}
private int dirKeyCertificateVersion;
+
@Override
public int getDirKeyCertificateVersion() {
return this.dirKeyCertificateVersion;
}
private String address;
+
@Override
public String getAddress() {
return this.address;
}
private int port = -1;
+
@Override
public int getPort() {
return this.port;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private String dirIdentityKey;
+
@Override
public String getDirIdentityKey() {
return this.dirIdentityKey;
}
private long dirKeyPublishedMillis;
+
@Override
public long getDirKeyPublishedMillis() {
return this.dirKeyPublishedMillis;
}
private long dirKeyExpiresMillis;
+
@Override
public long getDirKeyExpiresMillis() {
return this.dirKeyExpiresMillis;
}
private String dirSigningKey;
+
@Override
public String getDirSigningKey() {
return this.dirSigningKey;
}
private String dirKeyCrosscert;
+
@Override
public String getDirKeyCrosscert() {
return this.dirKeyCrosscert;
}
private String dirKeyCertification;
+
@Override
public String getDirKeyCertification() {
return this.dirKeyCertification;
}
private String certificateDigest;
+
@Override
public String getCertificateDigest() {
return this.certificateDigest;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
index a955f62..771b632 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -1,20 +1,23 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
-import org.torproject.descriptor.DirectorySignature;
-
public class DirectorySignatureImpl implements DirectorySignature {
private byte[] directorySignatureBytes;
private boolean failUnrecognizedDescriptorLines;
+
private List<String> unrecognizedLines;
+
protected List<String> getAndClearUnrecognizedLines() {
List<String> lines = this.unrecognizedLines;
this.unrecognizedLines = null;
@@ -32,56 +35,56 @@ public class DirectorySignatureImpl implements DirectorySignature {
private void parseDirectorySignatureBytes()
throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.directorySignatureBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.directorySignatureBytes))
+ .useDelimiter("\n");
StringBuilder crypto = null;
while (s.hasNext()) {
String line = s.next();
String[] parts = line.split(" ", -1);
String keyword = parts[0];
switch (keyword) {
- case "directory-signature":
- int algorithmOffset = 0;
- switch (parts.length) {
- case 4:
- this.algorithm = parts[1];
- algorithmOffset = 1;
+ case "directory-signature":
+ int algorithmOffset = 0;
+ switch (parts.length) {
+ case 4:
+ this.algorithm = parts[1];
+ algorithmOffset = 1;
+ break;
+ case 3:
+ break;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ this.identity = ParseHelper.parseHexString(line,
+ parts[1 + algorithmOffset]);
+ this.signingKeyDigest = ParseHelper.parseHexString(
+ line, parts[2 + algorithmOffset]);
break;
- case 3:
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
break;
- default:
- throw new DescriptorParseException("Illegal line '" + line
- + "'.");
- }
- this.identity = ParseHelper.parseHexString(line,
- parts[1 + algorithmOffset]);
- this.signingKeyDigest = ParseHelper.parseHexString(
- line, parts[2 + algorithmOffset]);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- this.signature = cryptoString;
- break;
- default:
- if (crypto != null) {
+ case "-----END":
crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ this.signature = cryptoString;
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -89,24 +92,28 @@ public class DirectorySignatureImpl implements DirectorySignature {
static final String DEFAULT_ALGORITHM = "sha1";
private String algorithm;
+
@Override
public String getAlgorithm() {
return this.algorithm == null ? DEFAULT_ALGORITHM : this.algorithm;
}
private String identity;
+
@Override
public String getIdentity() {
return this.identity;
}
private String signingKeyDigest;
+
@Override
public String getSigningKeyDigest() {
return this.signingKeyDigest;
}
private String signature;
+
@Override
public String getSignature() {
return this.signature;
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
index 72cfeae..fbad62d 100644
--- a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
@@ -1,5 +1,6 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
public interface DownloadCoordinator {
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
index a8e3731..0c31736 100644
--- a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
@@ -1,7 +1,13 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorRequest;
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -11,28 +17,32 @@ import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
/* TODO This whole download logic is a mess and needs a cleanup. */
public class DownloadCoordinatorImpl implements DownloadCoordinator {
private BlockingIteratorImpl<DescriptorRequest> descriptorQueue =
new BlockingIteratorImpl<>();
+
protected Iterator<DescriptorRequest> getDescriptorQueue() {
return this.descriptorQueue;
}
private SortedSet<String> runningDirectories;
+
private SortedMap<String, DirectoryDownloader> directoryAuthorities;
+
private SortedMap<String, DirectoryDownloader> directoryMirrors;
+
private boolean downloadConsensusFromAllAuthorities;
+
private boolean includeCurrentReferencedVotes;
+
private long connectTimeoutMillis;
+
private long readTimeoutMillis;
+
private long globalTimeoutMillis;
+
private boolean failUnrecognizedDescriptorLines;
protected DownloadCoordinatorImpl(
@@ -58,8 +68,8 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
this.globalTimeoutMillis = globalTimeoutMillis;
this.failUnrecognizedDescriptorLines =
failUnrecognizedDescriptorLines;
- if (this.directoryMirrors.isEmpty() &&
- this.directoryAuthorities.isEmpty()) {
+ if (this.directoryMirrors.isEmpty()
+ && this.directoryAuthorities.isEmpty()) {
this.descriptorQueue.setOutOfDescriptors();
/* TODO Should we say anything if we don't have any directories
* configured? */
@@ -68,8 +78,8 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
this);
this.globalTimerThread = new Thread(globalTimer);
this.globalTimerThread.start();
- for (DirectoryDownloader directoryMirror :
- this.directoryMirrors.values()) {
+ for (DirectoryDownloader directoryMirror
+ : this.directoryMirrors.values()) {
directoryMirror.setDownloadCoordinator(this);
directoryMirror.setConnectTimeout(this.connectTimeoutMillis);
directoryMirror.setReadTimeout(this.readTimeoutMillis);
@@ -77,8 +87,8 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
this.failUnrecognizedDescriptorLines);
new Thread(directoryMirror).start();
}
- for (DirectoryDownloader directoryAuthority :
- this.directoryAuthorities.values()) {
+ for (DirectoryDownloader directoryAuthority
+ : this.directoryAuthorities.values()) {
directoryAuthority.setDownloadCoordinator(this);
directoryAuthority.setConnectTimeout(this.connectTimeoutMillis);
directoryAuthority.setReadTimeout(this.readTimeoutMillis);
@@ -92,16 +102,22 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
/* Interrupt all downloads if the total download time exceeds a given
* time. */
private Thread globalTimerThread;
+
private static class GlobalTimer implements Runnable {
+
private long timeoutMillis;
+
private DownloadCoordinatorImpl downloadCoordinator;
+
private GlobalTimer(long timeoutMillis,
DownloadCoordinatorImpl downloadCoordinator) {
this.timeoutMillis = timeoutMillis;
this.downloadCoordinator = downloadCoordinator;
}
+
public void run() {
- long started = System.currentTimeMillis(), sleep;
+ long started = System.currentTimeMillis();
+ long sleep;
while ((sleep = started + this.timeoutMillis
- System.currentTimeMillis()) > 0L) {
try {
@@ -148,10 +164,10 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
while (!this.hasFinishedDownloading) {
DescriptorRequestImpl request = new DescriptorRequestImpl();
request.setDirectoryNickname(nickname);
- if ((this.missingConsensus ||
- (this.downloadConsensusFromAllAuthorities &&
- this.directoryAuthorities.containsKey(nickname))) &&
- !this.requestedConsensuses.contains(nickname)) {
+ if ((this.missingConsensus
+ || (this.downloadConsensusFromAllAuthorities
+ && this.directoryAuthorities.containsKey(nickname)))
+ && !this.requestedConsensuses.contains(nickname)) {
if (!this.downloadConsensusFromAllAuthorities) {
this.missingConsensus = false;
}
@@ -162,12 +178,13 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
request.setDescriptorType("consensus");
return request;
}
- if (!this.missingVotes.isEmpty() &&
- this.directoryAuthorities.containsKey(nickname)) {
+ if (!this.missingVotes.isEmpty()
+ && this.directoryAuthorities.containsKey(nickname)) {
String requestingVote = null;
for (String missingVote : this.missingVotes) {
- if (!this.requestedVotes.containsKey(nickname) ||
- !this.requestedVotes.get(nickname).contains(missingVote)) {
+ if (!this.requestedVotes.containsKey(nickname)
+ || !this.requestedVotes.get(nickname).contains(
+ missingVote)) {
requestingVote = missingVote;
}
}
@@ -208,26 +225,26 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
switch (response.getDescriptorType()) {
case "consensus":
this.requestingConsensuses.remove(nickname);
- if (response.getResponseCode() == 200 &&
- response.getDescriptors() != null) {
+ if (response.getResponseCode() == 200
+ && response.getDescriptors() != null) {
if (this.includeCurrentReferencedVotes) {
/* TODO Only add votes if the consensus is not older than one
* hour. Or does that make no sense? */
- for (Descriptor parsedDescriptor :
- response.getDescriptors()) {
- if (!(parsedDescriptor instanceof
- RelayNetworkStatusConsensus)) {
+ for (Descriptor parsedDescriptor
+ : response.getDescriptors()) {
+ if (!(parsedDescriptor
+ instanceof RelayNetworkStatusConsensus)) {
continue;
}
RelayNetworkStatusConsensus parsedConsensus =
(RelayNetworkStatusConsensus) parsedDescriptor;
- for (DirSourceEntry dirSource :
- parsedConsensus.getDirSourceEntries().values()) {
+ for (DirSourceEntry dirSource
+ : parsedConsensus.getDirSourceEntries().values()) {
String identity = dirSource.getIdentity();
if (!this.missingVotes.contains(identity)) {
boolean alreadyRequested = false;
- for (Set<String> requestedBefore :
- this.requestedVotes.values()) {
+ for (Set<String> requestedBefore
+ : this.requestedVotes.values()) {
if (requestedBefore.contains(identity)) {
alreadyRequested = true;
break;
@@ -255,11 +272,11 @@ public class DownloadCoordinatorImpl implements DownloadCoordinator {
this.descriptorQueue.add(response);
}
boolean doneDownloading = true;
- if ((this.missingConsensus ||
- this.downloadConsensusFromAllAuthorities) &&
- (!this.requestedConsensuses.containsAll(
- this.runningDirectories) ||
- !this.requestingConsensuses.isEmpty())) {
+ if ((this.missingConsensus
+ || this.downloadConsensusFromAllAuthorities)
+ && (!this.requestedConsensuses.containsAll(
+ this.runningDirectories)
+ || !this.requestingConsensuses.isEmpty())) {
doneDownloading = false;
}
if (!this.requestingVotes.isEmpty()) {
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
index efbf31c..e62fb91 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
@@ -1,9 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.ExitList;
+import org.torproject.descriptor.ExitListEntry;
import java.util.ArrayList;
import java.util.HashMap;
@@ -13,14 +15,14 @@ import java.util.Scanner;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.torproject.descriptor.ExitListEntry;
-
public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
private byte[] exitListEntryBytes;
private boolean failUnrecognizedDescriptorLines;
+
private List<String> unrecognizedLines;
+
protected List<String> getAndClearUnrecognizedLines() {
List<String> lines = this.unrecognizedLines;
this.unrecognizedLines = null;
@@ -40,8 +42,8 @@ public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
List<ExitListEntry> oldEntries() {
List<ExitListEntry> result = new ArrayList<>();
if (this.exitAddresses.size() > 1) {
- for (Map.Entry<String, Long> entry :
- this.exitAddresses.entrySet()) {
+ for (Map.Entry<String, Long> entry
+ : this.exitAddresses.entrySet()) {
result.add(new ExitListEntryImpl(this.fingerprint,
this.publishedMillis, this.lastStatusMillis, entry.getKey(),
entry.getValue()));
@@ -64,6 +66,7 @@ public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
}
private SortedSet<String> keywordCountingSet;
+
private void initializeKeywords() {
this.keywordCountingSet = new TreeSet<>();
this.keywordCountingSet.add("ExitNode");
@@ -91,8 +94,8 @@ public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
private void parseExitListEntryBytes()
throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.exitListEntryBytes)).
- useDelimiter(ExitList.EOL);
+ Scanner s = new Scanner(new String(this.exitListEntryBytes))
+ .useDelimiter(ExitList.EOL);
while (s.hasNext()) {
String line = s.next();
String[] parts = line.split(" ");
@@ -169,24 +172,28 @@ public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private long lastStatusMillis;
+
@Override
public long getLastStatusMillis() {
return this.lastStatusMillis;
}
private String exitAddress;
+
@Override
public String getExitAddress() {
if (null == exitAddress) {
@@ -199,12 +206,14 @@ public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
}
private Map<String, Long> exitAddresses = new HashMap<>();
+
@Override
- public Map<String, Long> getExitAddresses(){
+ public Map<String, Long> getExitAddresses() {
return new HashMap<>(this.exitAddresses);
}
private long scanMillis;
+
@Override
public long getScanMillis() {
if (null == exitAddress) {
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
index 10619ba..ab3e516 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
@@ -1,8 +1,12 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExitList;
+import org.torproject.descriptor.ExitListEntry;
+
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -12,9 +16,6 @@ import java.util.Scanner;
import java.util.Set;
import java.util.TimeZone;
-import org.torproject.descriptor.ExitList;
-import org.torproject.descriptor.ExitListEntry;
-
public class ExitListImpl extends DescriptorImpl implements ExitList {
protected ExitListImpl(byte[] rawDescriptorBytes, String fileName,
@@ -27,8 +28,8 @@ public class ExitListImpl extends DescriptorImpl implements ExitList {
private void setPublishedMillisFromFileName(String fileName)
throws DescriptorParseException {
- if (this.downloadedMillis == 0L &&
- fileName.length() == "2012-02-01-04-06-24".length()) {
+ if (this.downloadedMillis == 0L
+ && fileName.length() == "2012-02-01-04-06-24".length()) {
try {
SimpleDateFormat fileNameFormat = new SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss");
@@ -110,8 +111,8 @@ public class ExitListImpl extends DescriptorImpl implements ExitList {
exitListEntryBytes, this.failUnrecognizedDescriptorLines);
this.exitListEntries.add(exitListEntry);
this.oldExitListEntries.addAll(exitListEntry.oldEntries());
- List<String> unrecognizedExitListEntryLines = exitListEntry.
- getAndClearUnrecognizedLines();
+ List<String> unrecognizedExitListEntryLines = exitListEntry
+ .getAndClearUnrecognizedLines();
if (unrecognizedExitListEntryLines != null) {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -121,12 +122,14 @@ public class ExitListImpl extends DescriptorImpl implements ExitList {
}
private long downloadedMillis;
+
@Override
public long getDownloadedMillis() {
return this.downloadedMillis;
}
private Set<ExitListEntry> oldExitListEntries = new HashSet<>();
+
@Deprecated
@Override
public Set<ExitListEntry> getExitListEntries() {
@@ -134,6 +137,7 @@ public class ExitListImpl extends DescriptorImpl implements ExitList {
}
private Set<ExitList.Entry> exitListEntries = new HashSet<>();
+
@Override
public Set<ExitList.Entry> getEntries() {
return new HashSet<>(this.exitListEntries);
diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
index 3f72616..f67c793 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -1,7 +1,12 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -20,10 +25,6 @@ import java.util.TreeMap;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-
public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
implements ExtraInfoDescriptor {
@@ -77,216 +78,217 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
List<String> cryptoLines = null;
while (s.hasNext()) {
String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
+ String lineNoOpt = line.startsWith("opt ")
+ ? line.substring("opt ".length()) : line;
String[] partsNoOpt = lineNoOpt.split("[ \t]+");
String keyword = partsNoOpt[0];
switch (keyword) {
- case "extra-info":
- this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-db-digest":
- this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip6-db-digest":
- this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-start-time":
- this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-client-origins":
- this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-stats-end":
- this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-ips":
- this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-ips":
- this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-reqs":
- this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-reqs":
- this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-share":
- this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-share":
- this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-resp":
- this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-resp":
- this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-direct-dl":
- this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-direct-dl":
- this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-tunneled-dl":
- this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-tunneled-dl":
- this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-read-history":
- this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-write-history":
- this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-stats-end":
- this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-ips":
- this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-stats-end":
- this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-processed-cells":
- this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-queued-cells":
- this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-time-in-queue":
- this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-circuits-per-decile":
- this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt);
- break;
- case "conn-bi-direct":
- this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-stats-end":
- this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-written":
- this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-read":
- this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-streams-opened":
- this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-stats-end":
- this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ips":
- this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-versions":
- this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-transports":
- this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "transport":
- this.parseTransportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-stats-end":
- this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-rend-relayed-cells":
- this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
- partsNoOpt);
- break;
- case "hidserv-dir-onions-seen":
- this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
- case "router-signature":
- this.routerSignature = cryptoString;
+ case "extra-info":
+ this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-db-digest":
+ this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip6-db-digest":
+ this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-start-time":
+ this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-client-origins":
+ this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-stats-end":
+ this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-ips":
+ this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-ips":
+ this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-reqs":
+ this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-reqs":
+ this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-share":
+ this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-share":
+ this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-resp":
+ this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-resp":
+ this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-direct-dl":
+ this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-direct-dl":
+ this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-tunneled-dl":
+ this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-tunneled-dl":
+ this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-read-history":
+ this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-write-history":
+ this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-stats-end":
+ this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-ips":
+ this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-stats-end":
+ this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-processed-cells":
+ this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-queued-cells":
+ this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-time-in-queue":
+ this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-circuits-per-decile":
+ this.parseCellCircuitsPerDecileLine(line, lineNoOpt,
+ partsNoOpt);
+ break;
+ case "conn-bi-direct":
+ this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-stats-end":
+ this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-written":
+ this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-read":
+ this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-streams-opened":
+ this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-stats-end":
+ this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ips":
+ this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-versions":
+ this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-transports":
+ this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "transport":
+ this.parseTransportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-stats-end":
+ this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-rend-relayed-cells":
+ this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
+ partsNoOpt);
+ break;
+ case "hidserv-dir-onions-seen":
+ this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
break;
case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in extra-info "
- + "descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- cryptoLines = null;
- nextCrypto = "";
- }
- break;
- default:
- if (cryptoLines != null) {
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-signature":
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in extra-info descriptor.");
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in extra-info "
+ + "descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ }
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in extra-info descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -365,11 +367,11 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
this.dirreqStatsIntervalLength = parsedStatsEndData[1];
}
- private long[] parseStatsEndLine(String line, String partsNoOpt[],
+ private long[] parseStatsEndLine(String line, String[] partsNoOpt,
int partsNoOptExpectedLength) throws DescriptorParseException {
- if (partsNoOpt.length != partsNoOptExpectedLength ||
- partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
- !partsNoOpt[4].equals("s)")) {
+ if (partsNoOpt.length != partsNoOptExpectedLength
+ || partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(")
+ || !partsNoOpt[4].equals("s)")) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
long[] result = new long[2];
@@ -422,8 +424,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
private double parseShareLine(String line, String[] partsNoOpt)
throws DescriptorParseException {
double share = -1.0;
- if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
- partsNoOpt[1].endsWith("%")) {
+ if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2
+ && partsNoOpt[1].endsWith("%")) {
String shareString = partsNoOpt[1];
shareString = shareString.substring(0, shareString.length() - 1);
try {
@@ -516,8 +518,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseCellProcessedCellsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- this.cellProcessedCells = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ this.cellProcessedCells = ParseHelper
+ .parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
if (this.cellProcessedCells.length != 10) {
throw new DescriptorParseException("There must be exact ten values "
+ "in line '" + line + "'.");
@@ -536,8 +538,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseCellTimeInQueueLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- this.cellTimeInQueue = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ this.cellTimeInQueue = ParseHelper
+ .parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
if (this.cellTimeInQueue.length != 10) {
throw new DescriptorParseException("There must be exact ten values "
+ "in line '" + line + "'.");
@@ -567,8 +569,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
6);
this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
- Integer[] parsedConnBiDirectStats = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
+ Integer[] parsedConnBiDirectStats = ParseHelper
+ .parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
if (parsedConnBiDirectStats.length != 4) {
throw new DescriptorParseException("Illegal line '" + line + "' in "
+ "extra-info descriptor.");
@@ -590,24 +592,24 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseExitKibibytesWrittenLine(String line,
String lineNoOpt, String[] partsNoOpt)
throws DescriptorParseException {
- this.exitKibibytesWritten = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.exitKibibytesWritten = this.sortByPorts(ParseHelper
+ .parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
this.verifyPorts(line, this.exitKibibytesWritten.keySet());
this.verifyBytesOrStreams(line, this.exitKibibytesWritten.values());
}
private void parseExitKibibytesReadLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- this.exitKibibytesRead = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.exitKibibytesRead = this.sortByPorts(ParseHelper
+ .parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
this.verifyPorts(line, this.exitKibibytesRead.keySet());
this.verifyBytesOrStreams(line, this.exitKibibytesRead.values());
}
private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- this.exitStreamsOpened = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.exitStreamsOpened = this.sortByPorts(ParseHelper
+ .parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
this.verifyPorts(line, this.exitStreamsOpened.keySet());
this.verifyBytesOrStreams(line, this.exitStreamsOpened.values());
}
@@ -617,7 +619,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
SortedMap<String, Long> byPortNumber =
new TreeMap<String, Long>(new Comparator<String>() {
public int compare(String arg0, String arg1) {
- int port0 = 0, port1 = 0;
+ int port0 = 0;
+ int port1 = 0;
try {
port1 = Integer.parseInt(arg1);
} catch (NumberFormatException e) {
@@ -635,7 +638,9 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
} else {
return 0;
}
- }});
+ }
+ }
+ );
byPortNumber.putAll(naturalOrder);
return byPortNumber;
}
@@ -835,8 +840,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.extraInfoDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -866,8 +871,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
0, sig - start);
this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
+ MessageDigest.getInstance("SHA-256").digest(forDigest))
+ .replaceAll("=", "");
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -881,72 +886,84 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String extraInfoDigest;
+
@Override
public String getExtraInfoDigest() {
return this.extraInfoDigest;
}
private String extraInfoDigestSha256;
+
@Override
public String getExtraInfoDigestSha256() {
return this.extraInfoDigestSha256;
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private BandwidthHistory readHistory;
+
@Override
public BandwidthHistory getReadHistory() {
return this.readHistory;
}
private BandwidthHistory writeHistory;
+
@Override
public BandwidthHistory getWriteHistory() {
return this.writeHistory;
}
private String geoipDbDigest;
+
@Override
public String getGeoipDbDigest() {
return this.geoipDbDigest;
}
private String geoip6DbDigest;
+
@Override
public String getGeoip6DbDigest() {
return this.geoip6DbDigest;
}
private long dirreqStatsEndMillis = -1L;
+
@Override
public long getDirreqStatsEndMillis() {
return this.dirreqStatsEndMillis;
}
private long dirreqStatsIntervalLength = -1L;
+
@Override
public long getDirreqStatsIntervalLength() {
return this.dirreqStatsIntervalLength;
}
private String dirreqV2Ips;
+
@Override
public SortedMap<String, Integer> getDirreqV2Ips() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -954,6 +971,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV3Ips;
+
@Override
public SortedMap<String, Integer> getDirreqV3Ips() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -961,6 +979,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV2Reqs;
+
@Override
public SortedMap<String, Integer> getDirreqV2Reqs() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -968,6 +987,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV3Reqs;
+
@Override
public SortedMap<String, Integer> getDirreqV3Reqs() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -975,18 +995,21 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private double dirreqV2Share = -1.0;
+
@Override
public double getDirreqV2Share() {
return this.dirreqV2Share;
}
private double dirreqV3Share = -1.0;
+
@Override
public double getDirreqV3Share() {
return this.dirreqV3Share;
}
private String dirreqV2Resp;
+
@Override
public SortedMap<String, Integer> getDirreqV2Resp() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -994,6 +1017,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV3Resp;
+
@Override
public SortedMap<String, Integer> getDirreqV3Resp() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1001,6 +1025,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV2DirectDl;
+
@Override
public SortedMap<String, Integer> getDirreqV2DirectDl() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1008,6 +1033,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV3DirectDl;
+
@Override
public SortedMap<String, Integer> getDirreqV3DirectDl() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1015,6 +1041,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV2TunneledDl;
+
@Override
public SortedMap<String, Integer> getDirreqV2TunneledDl() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1022,6 +1049,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String dirreqV3TunneledDl;
+
@Override
public SortedMap<String, Integer> getDirreqV3TunneledDl() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1029,30 +1057,35 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private BandwidthHistory dirreqReadHistory;
+
@Override
public BandwidthHistory getDirreqReadHistory() {
return this.dirreqReadHistory;
}
private BandwidthHistory dirreqWriteHistory;
+
@Override
public BandwidthHistory getDirreqWriteHistory() {
return this.dirreqWriteHistory;
}
private long entryStatsEndMillis = -1L;
+
@Override
public long getEntryStatsEndMillis() {
return this.entryStatsEndMillis;
}
private long entryStatsIntervalLength = -1L;
+
@Override
public long getEntryStatsIntervalLength() {
return this.entryStatsIntervalLength;
}
private String entryIps;
+
@Override
public SortedMap<String, Integer> getEntryIps() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1060,120 +1093,139 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private long cellStatsEndMillis = -1L;
+
@Override
public long getCellStatsEndMillis() {
return this.cellStatsEndMillis;
}
private long cellStatsIntervalLength = -1L;
+
@Override
public long getCellStatsIntervalLength() {
return this.cellStatsIntervalLength;
}
private Integer[] cellProcessedCells;
+
@Override
public List<Integer> getCellProcessedCells() {
- return this.cellProcessedCells == null ? null :
- Arrays.asList(this.cellProcessedCells);
+ return this.cellProcessedCells == null ? null
+ : Arrays.asList(this.cellProcessedCells);
}
private Double[] cellQueuedCells;
+
@Override
public List<Double> getCellQueuedCells() {
- return this.cellQueuedCells == null ? null :
- Arrays.asList(this.cellQueuedCells);
+ return this.cellQueuedCells == null ? null
+ : Arrays.asList(this.cellQueuedCells);
}
private Integer[] cellTimeInQueue;
+
@Override
public List<Integer> getCellTimeInQueue() {
- return this.cellTimeInQueue == null ? null :
- Arrays.asList(this.cellTimeInQueue);
+ return this.cellTimeInQueue == null ? null
+ : Arrays.asList(this.cellTimeInQueue);
}
private int cellCircuitsPerDecile = -1;
+
@Override
public int getCellCircuitsPerDecile() {
return this.cellCircuitsPerDecile;
}
private long connBiDirectStatsEndMillis = -1L;
+
@Override
public long getConnBiDirectStatsEndMillis() {
return this.connBiDirectStatsEndMillis;
}
private long connBiDirectStatsIntervalLength = -1L;
+
@Override
public long getConnBiDirectStatsIntervalLength() {
return this.connBiDirectStatsIntervalLength;
}
private int connBiDirectBelow = -1;
+
@Override
public int getConnBiDirectBelow() {
return this.connBiDirectBelow;
}
private int connBiDirectRead = -1;
+
@Override
public int getConnBiDirectRead() {
return this.connBiDirectRead;
}
private int connBiDirectWrite = -1;
+
@Override
public int getConnBiDirectWrite() {
return this.connBiDirectWrite;
}
private int connBiDirectBoth = -1;
+
@Override
public int getConnBiDirectBoth() {
return this.connBiDirectBoth;
}
private long exitStatsEndMillis = -1L;
+
@Override
public long getExitStatsEndMillis() {
return this.exitStatsEndMillis;
}
private long exitStatsIntervalLength = -1L;
+
@Override
public long getExitStatsIntervalLength() {
return this.exitStatsIntervalLength;
}
private SortedMap<String, Long> exitKibibytesWritten;
+
@Override
public SortedMap<String, Long> getExitKibibytesWritten() {
- return this.exitKibibytesWritten == null ? null :
- new TreeMap<>(this.exitKibibytesWritten);
+ return this.exitKibibytesWritten == null ? null
+ : new TreeMap<>(this.exitKibibytesWritten);
}
private SortedMap<String, Long> exitKibibytesRead;
+
@Override
public SortedMap<String, Long> getExitKibibytesRead() {
- return this.exitKibibytesRead == null ? null :
- new TreeMap<>(this.exitKibibytesRead);
+ return this.exitKibibytesRead == null ? null
+ : new TreeMap<>(this.exitKibibytesRead);
}
private SortedMap<String, Long> exitStreamsOpened;
+
@Override
public SortedMap<String, Long> getExitStreamsOpened() {
- return this.exitStreamsOpened == null ? null :
- new TreeMap<>(this.exitStreamsOpened);
+ return this.exitStreamsOpened == null ? null
+ : new TreeMap<>(this.exitStreamsOpened);
}
private long geoipStartTimeMillis = -1L;
+
@Override
public long getGeoipStartTimeMillis() {
return this.geoipStartTimeMillis;
}
private String geoipClientOrigins;
+
@Override
public SortedMap<String, Integer> getGeoipClientOrigins() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1181,18 +1233,21 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private long bridgeStatsEndMillis = -1L;
+
@Override
public long getBridgeStatsEndMillis() {
return this.bridgeStatsEndMillis;
}
private long bridgeStatsIntervalLength = -1L;
+
@Override
public long getBridgeStatsIntervalLength() {
return this.bridgeStatsIntervalLength;
}
private String bridgeIps;
+
@Override
public SortedMap<String, Integer> getBridgeIps() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1200,6 +1255,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String bridgeIpVersions;
+
@Override
public SortedMap<String, Integer> getBridgeIpVersions() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1207,6 +1263,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private String bridgeIpTransports;
+
@Override
public SortedMap<String, Integer> getBridgeIpTransports() {
return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
@@ -1214,68 +1271,79 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
private List<String> transports = new ArrayList<>();
+
@Override
public List<String> getTransports() {
return new ArrayList<>(this.transports);
}
private long hidservStatsEndMillis = -1L;
+
@Override
public long getHidservStatsEndMillis() {
return this.hidservStatsEndMillis;
}
private long hidservStatsIntervalLength = -1L;
+
@Override
public long getHidservStatsIntervalLength() {
return this.hidservStatsIntervalLength;
}
private Double hidservRendRelayedCells;
+
@Override
public Double getHidservRendRelayedCells() {
return this.hidservRendRelayedCells;
}
private Map<String, Double> hidservRendRelayedCellsParameters;
+
@Override
public Map<String, Double> getHidservRendRelayedCellsParameters() {
- return this.hidservRendRelayedCellsParameters == null ? null :
- new HashMap<>(this.hidservRendRelayedCellsParameters);
+ return this.hidservRendRelayedCellsParameters == null ? null
+ : new HashMap<>(this.hidservRendRelayedCellsParameters);
}
private Double hidservDirOnionsSeen;
+
@Override
public Double getHidservDirOnionsSeen() {
return this.hidservDirOnionsSeen;
}
private Map<String, Double> hidservDirOnionsSeenParameters;
+
@Override
public Map<String, Double> getHidservDirOnionsSeenParameters() {
- return this.hidservDirOnionsSeenParameters == null ? null :
- new HashMap<>(this.hidservDirOnionsSeenParameters);
+ return this.hidservDirOnionsSeenParameters == null ? null
+ : new HashMap<>(this.hidservDirOnionsSeenParameters);
}
private String routerSignature;
+
@Override
public String getRouterSignature() {
return this.routerSignature;
}
private String identityEd25519;
+
@Override
public String getIdentityEd25519() {
return this.identityEd25519;
}
private String masterKeyEd25519;
+
@Override
public String getMasterKeyEd25519() {
return this.masterKeyEd25519;
}
private String routerSignatureEd25519;
+
@Override
public String getRouterSignatureEd25519() {
return this.routerSignatureEd25519;
diff --git a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
index 4931c31..7f07da9 100644
--- a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -1,7 +1,11 @@
/* Copyright 2014--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.Microdescriptor;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -14,9 +18,6 @@ import java.util.Set;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.Microdescriptor;
-
/* Contains a microdescriptor. */
public class MicrodescriptorImpl extends DescriptorImpl
implements Microdescriptor {
@@ -55,8 +56,8 @@ public class MicrodescriptorImpl extends DescriptorImpl
}
private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
StringBuilder crypto = null;
while (s.hasNext()) {
@@ -67,59 +68,59 @@ public class MicrodescriptorImpl extends DescriptorImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "onion-key":
- this.parseOnionKeyLine(line, parts);
- nextCrypto = "onion-key";
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, parts);
- break;
- case "a":
- this.parseALine(line, parts);
- break;
- case "family":
- this.parseFamilyLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "p6":
- this.parseP6Line(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("onion-key")) {
- this.onionKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in microdescriptor.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "onion-key":
+ this.parseOnionKeyLine(line, parts);
+ nextCrypto = "onion-key";
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, parts);
+ break;
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "family":
+ this.parseFamilyLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "p6":
+ this.parseP6Line(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
crypto.append(line).append("\n");
- } else {
- ParseHelper.parseKeyword(line, parts[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in microdescriptor.");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("onion-key")) {
+ this.onionKey = cryptoString;
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in microdescriptor.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ ParseHelper.parseKeyword(line, parts[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in microdescriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -195,18 +196,18 @@ public class MicrodescriptorImpl extends DescriptorImpl
isValid = false;
} else {
switch (parts[1]) {
- case "accept":
- case "reject":
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
+ case "accept":
+ case "reject":
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
}
- }
- break;
- default:
- isValid = false;
+ break;
+ default:
+ isValid = false;
}
}
if (!isValid) {
@@ -219,7 +220,7 @@ public class MicrodescriptorImpl extends DescriptorImpl
if (parts.length != 3) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
} else {
- switch (parts[1]) {
+ switch (parts[1]) {
case "ed25519":
ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
this.ed25519Identity = parts[2];
@@ -229,8 +230,9 @@ public class MicrodescriptorImpl extends DescriptorImpl
this.rsa1024Identity = parts[2];
break;
default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
}
}
@@ -245,8 +247,8 @@ public class MicrodescriptorImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, end - start);
this.microdescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-256").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -260,66 +262,78 @@ public class MicrodescriptorImpl extends DescriptorImpl
}
private String microdescriptorDigest;
+
@Override
public String getMicrodescriptorDigest() {
return this.microdescriptorDigest;
}
private String onionKey;
+
@Override
public String getOnionKey() {
return this.onionKey;
}
private String ntorOnionKey;
+
@Override
public String getNtorOnionKey() {
return this.ntorOnionKey;
}
private List<String> orAddresses = new ArrayList<>();
+
@Override
public List<String> getOrAddresses() {
return new ArrayList<>(this.orAddresses);
}
private String[] familyEntries;
+
@Override
public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
+ return this.familyEntries == null ? null
+ : Arrays.asList(this.familyEntries);
}
+
private String defaultPolicy;
+
@Override
public String getDefaultPolicy() {
return this.defaultPolicy;
}
private String portList;
+
@Override
public String getPortList() {
return this.portList;
}
private String ipv6DefaultPolicy;
+
@Override
public String getIpv6DefaultPolicy() {
return this.ipv6DefaultPolicy;
}
private String ipv6PortList;
+
@Override
public String getIpv6PortList() {
return this.ipv6PortList;
}
private String rsa1024Identity;
+
@Override
public String getRsa1024Identity() {
return this.rsa1024Identity;
}
private String ed25519Identity;
+
@Override
public String getEd25519Identity() {
return this.ed25519Identity;
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index b73d211..6ffaf62 100644
--- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -1,8 +1,11 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.NetworkStatusEntry;
+
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
@@ -15,11 +18,10 @@ import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.torproject.descriptor.NetworkStatusEntry;
-
public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private byte[] statusEntryBytes;
+
@Override
public byte[] getStatusEntryBytes() {
return this.statusEntryBytes;
@@ -28,7 +30,9 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
private boolean microdescConsensus;
private boolean failUnrecognizedDescriptorLines;
+
private List<String> unrecognizedLines;
+
protected List<String> getAndClearUnrecognizedLines() {
List<String> lines = this.unrecognizedLines;
this.unrecognizedLines = null;
@@ -48,6 +52,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
private SortedSet<String> atMostOnceKeywords;
+
private void initializeKeywords() {
this.atMostOnceKeywords = new TreeSet<>();
this.atMostOnceKeywords.add("s");
@@ -66,8 +71,8 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
private void parseStatusEntryBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.statusEntryBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.statusEntryBytes))
+ .useDelimiter("\n");
String line = null;
if (!s.hasNext() || !(line = s.next()).startsWith("r ")) {
throw new DescriptorParseException("Status entry must start with "
@@ -77,49 +82,49 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
this.parseRLine(line, rLineParts);
while (s.hasNext()) {
line = s.next();
- String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") :
- line.substring("opt ".length()).split("[ \t]+");
+ String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+")
+ : line.substring("opt ".length()).split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "a":
- this.parseALine(line, parts);
- break;
- case "s":
- this.parseSLine(line, parts);
- break;
- case "v":
- this.parseVLine(line, parts);
- break;
- case "w":
- this.parseWLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "m":
- this.parseMLine(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in status entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "s":
+ this.parseSLine(line, parts);
+ break;
+ case "v":
+ this.parseVLine(line, parts);
+ break;
+ case "w":
+ this.parseWLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "m":
+ this.parseMLine(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in status entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
private void parseRLine(String line, String[] parts)
throws DescriptorParseException {
- if ((!this.microdescConsensus && parts.length != 9) ||
- (this.microdescConsensus && parts.length != 8)) {
+ if ((!this.microdescConsensus && parts.length != 9)
+ || (this.microdescConsensus && parts.length != 8)) {
throw new DescriptorParseException("r line '" + line + "' has "
+ "fewer space-separated elements than expected.");
}
@@ -154,6 +159,7 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
private static Map<String, Integer> flagIndexes = new HashMap<>();
+
private static Map<Integer, String> flagStrings = new HashMap<>();
private void parseSLine(String line, String[] parts)
@@ -215,22 +221,22 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
if (parts.length != 3) {
isValid = false;
} else {
- switch (parts[1]) {
- case "accept":
- case "reject":
- this.defaultPolicy = parts[1];
- this.portList = parts[2];
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
+ switch (parts[1]) {
+ case "accept":
+ case "reject":
+ this.defaultPolicy = parts[1];
+ this.portList = parts[2];
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
}
- break;
- default:
- isValid = false;
- }
+ }
+ break;
+ default:
+ isValid = false;
+ }
}
if (!isValid) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
@@ -270,61 +276,71 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private String descriptor;
+
@Override
public String getDescriptor() {
return this.descriptor;
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private String address;
+
@Override
public String getAddress() {
return this.address;
}
private int orPort;
+
@Override
public int getOrPort() {
return this.orPort;
}
private int dirPort;
+
@Override
public int getDirPort() {
return this.dirPort;
}
private Set<String> microdescriptorDigests;
+
@Override
public Set<String> getMicrodescriptorDigests() {
- return this.microdescriptorDigests == null ? null :
- new HashSet<>(this.microdescriptorDigests);
+ return this.microdescriptorDigests == null ? null
+ : new HashSet<>(this.microdescriptorDigests);
}
private List<String> orAddresses = new ArrayList<>();
+
@Override
public List<String> getOrAddresses() {
return new ArrayList<>(this.orAddresses);
}
private BitSet flags;
+
@Override
public SortedSet<String> getFlags() {
SortedSet<String> result = new TreeSet<>();
@@ -338,42 +354,49 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
private String version;
+
@Override
public String getVersion() {
return this.version;
}
private long bandwidth = -1L;
+
@Override
public long getBandwidth() {
return this.bandwidth;
}
private long measured = -1L;
+
@Override
public long getMeasured() {
return this.measured;
}
private boolean unmeasured = false;
+
@Override
public boolean getUnmeasured() {
return this.unmeasured;
}
private String defaultPolicy;
+
@Override
public String getDefaultPolicy() {
return this.defaultPolicy;
}
private String portList;
+
@Override
public String getPortList() {
return this.portList;
}
private String masterKeyEd25519;
+
@Override
public String getMasterKeyEd25519() {
return this.masterKeyEd25519;
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
index 5fa22c7..4c99aca 100644
--- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -1,17 +1,18 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.DirectorySignature;
+import org.torproject.descriptor.NetworkStatusEntry;
+
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.DirectorySignature;
-import org.torproject.descriptor.NetworkStatusEntry;
-
/* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
* consensuses, v2 statuses, and sanitized bridge network statuses and
* delegate the specific parts to the subclasses. */
@@ -34,8 +35,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
}
String descriptorString = new String(rawDescriptorBytes);
int startIndex = 0;
- int firstDirSourceIndex = !containsDirSourceEntries ? -1 :
- this.findFirstIndexOfKeyword(descriptorString, "dir-source");
+ int firstDirSourceIndex = !containsDirSourceEntries ? -1
+ : this.findFirstIndexOfKeyword(descriptorString, "dir-source");
int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r");
int directoryFooterIndex = this.findFirstIndexOfKeyword(
descriptorString, "directory-footer");
@@ -164,8 +165,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
dirSourceBytes, this.failUnrecognizedDescriptorLines);
this.dirSourceEntries.put(dirSourceEntry.getIdentity(),
dirSourceEntry);
- List<String> unrecognizedDirSourceLines = dirSourceEntry.
- getAndClearUnrecognizedLines();
+ List<String> unrecognizedDirSourceLines = dirSourceEntry
+ .getAndClearUnrecognizedLines();
if (unrecognizedDirSourceLines != null) {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -191,8 +192,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
}
break;
default:
- throw new DescriptorParseException("Illegal versions line '" + line
- + "'.");
+ throw new DescriptorParseException("Illegal versions line '"
+ + line + "'.");
}
return result;
}
@@ -202,8 +203,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
+ List<String> unrecognizedStatusEntryLines = statusEntry
+ .getAndClearUnrecognizedLines();
if (unrecognizedStatusEntryLines != null) {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -223,8 +224,8 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
DirectorySignatureImpl signature = new DirectorySignatureImpl(
directorySignatureBytes, failUnrecognizedDescriptorLines);
this.signatures.add(signature);
- List<String> unrecognizedStatusEntryLines = signature.
- getAndClearUnrecognizedLines();
+ List<String> unrecognizedStatusEntryLines = signature
+ .getAndClearUnrecognizedLines();
if (unrecognizedStatusEntryLines != null) {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -235,27 +236,33 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
protected SortedMap<String, DirSourceEntry> dirSourceEntries =
new TreeMap<>();
+
public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
return new TreeMap<>(this.dirSourceEntries);
}
protected SortedMap<String, NetworkStatusEntry> statusEntries =
new TreeMap<>();
+
public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
return new TreeMap<>(this.statusEntries);
}
+
public boolean containsStatusEntry(String fingerprint) {
return this.statusEntries.containsKey(fingerprint);
}
+
public NetworkStatusEntry getStatusEntry(String fingerprint) {
return this.statusEntries.get(fingerprint);
}
protected List<DirectorySignature> signatures;
+
public List<DirectorySignature> getSignatures() {
return this.signatures == null ? null
: new ArrayList<>(this.signatures);
}
+
public SortedMap<String, DirectorySignature> getDirectorySignatures() {
SortedMap<String, DirectorySignature> directorySignatures = null;
if (this.signatures != null) {
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
index 82c0813..a40a351 100644
--- a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -1,7 +1,10 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -16,12 +19,11 @@ import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-
public class ParseHelper {
private static Pattern keywordPattern =
Pattern.compile("^[A-Za-z0-9-]+$");
+
protected static String parseKeyword(String line, String keyword)
throws DescriptorParseException {
if (!keywordPattern.matcher(keyword).matches()) {
@@ -33,6 +35,7 @@ public class ParseHelper {
private static Pattern ipv4Pattern =
Pattern.compile("^[0-9\\.]{7,15}$");
+
protected static String parseIpv4Address(String line, String address)
throws DescriptorParseException {
boolean isValid = true;
@@ -141,20 +144,25 @@ public class ParseHelper {
}
private static ThreadLocal<Map<String, DateFormat>> dateFormats =
- new ThreadLocal<Map<String, DateFormat>> () {
+ new ThreadLocal<Map<String, DateFormat>>() {
+
public Map<String, DateFormat> get() {
return super.get();
}
+
protected Map<String, DateFormat> initialValue() {
return new HashMap<>();
}
+
public void remove() {
super.remove();
}
+
public void set(Map<String, DateFormat> value) {
super.set(value);
}
};
+
static DateFormat getDateFormat(String format) {
Map<String, DateFormat> threadDateFormats = dateFormats.get();
if (!threadDateFormats.containsKey(format)) {
@@ -218,11 +226,13 @@ public class ParseHelper {
}
private static Pattern hexPattern = Pattern.compile("^[0-9a-fA-F]*$");
+
private static String parseHexString(String line, String hexString,
int expectedLength) throws DescriptorParseException {
- if (!hexPattern.matcher(hexString).matches() ||
- hexString.length() % 2 != 0 ||
- (expectedLength >= 0 && hexString.length() != expectedLength)) {
+ if (!hexPattern.matcher(hexString).matches()
+ || hexString.length() % 2 != 0
+ || (expectedLength >= 0
+ && hexString.length() != expectedLength)) {
throw new DescriptorParseException("Illegal hex string in line '"
+ line + "'.");
}
@@ -265,6 +275,7 @@ public class ParseHelper {
private static Pattern nicknamePattern =
Pattern.compile("^[0-9a-zA-Z]{1,19}$");
+
protected static String parseNickname(String line, String nickname)
throws DescriptorParseException {
if (!nicknamePattern.matcher(nickname).matches()) {
@@ -277,17 +288,19 @@ public class ParseHelper {
protected static boolean parseBoolean(String b, String line)
throws DescriptorParseException {
switch (b) {
- case "1":
- return true;
- case "0":
- return false;
- default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
+ case "1":
+ return true;
+ case "0":
+ return false;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
}
}
private static Pattern twentyByteBase64Pattern =
Pattern.compile("^[0-9a-zA-Z+/]{27}$");
+
protected static String parseTwentyByteBase64String(String line,
String base64String) throws DescriptorParseException {
if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
@@ -296,12 +309,13 @@ public class ParseHelper {
+ "20-byte value.");
}
return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
+ DatatypeConverter.parseBase64Binary(base64String + "="))
+ .toUpperCase();
}
private static Pattern thirtyTwoByteBase64Pattern =
Pattern.compile("^[0-9a-zA-Z+/]{43}$");
+
protected static String parseThirtyTwoByteBase64String(String line,
String base64String) throws DescriptorParseException {
if (!thirtyTwoByteBase64Pattern.matcher(base64String).matches()) {
@@ -310,12 +324,13 @@ public class ParseHelper {
+ "32-byte value.");
}
return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
+ DatatypeConverter.parseBase64Binary(base64String + "="))
+ .toUpperCase();
}
private static Map<Integer, Pattern>
commaSeparatedKeyValueListPatterns = new HashMap<>();
+
protected static String parseCommaSeparatedKeyIntegerValueList(
String line, String[] partsNoOpt, int index, int keyLength)
throws DescriptorParseException {
@@ -382,8 +397,8 @@ public class ParseHelper {
String[] keyAndValue = listElement.split("=");
String key = null;
long value = -1;
- if (keyAndValue.length == 2 && (keyLength == 0 ||
- keyAndValue[0].length() == keyLength)) {
+ if (keyAndValue.length == 2 && (keyLength == 0
+ || keyAndValue[0].length() == keyLength)) {
try {
value = Long.parseLong(keyAndValue[1]);
key = keyAndValue[0];
@@ -496,8 +511,8 @@ public class ParseHelper {
String identityEd25519CryptoBlock) throws DescriptorParseException {
String identityEd25519CryptoBlockNoNewlines =
identityEd25519CryptoBlock.replaceAll("\n", "");
- String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
- endEd25519CertLine = "-----END ED25519 CERT-----";
+ String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----";
+ String endEd25519CertLine = "-----END ED25519 CERT-----";
if (!identityEd25519CryptoBlockNoNewlines.startsWith(
beginEd25519CertLine)) {
throw new DescriptorParseException("Illegal start of "
@@ -510,8 +525,8 @@ public class ParseHelper {
+ "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ "'.");
}
- String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
- substring(beginEd25519CertLine.length(),
+ String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines
+ .substring(beginEd25519CertLine.length(),
identityEd25519CryptoBlock.length()
- endEd25519CertLine.length()).replaceAll("=", "");
byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
@@ -551,8 +566,8 @@ public class ParseHelper {
byte[] masterKeyEd25519 = new byte[32];
System.arraycopy(identityEd25519, extensionStart + 4,
masterKeyEd25519, 0, masterKeyEd25519.length);
- String masterKeyEd25519Base64 = DatatypeConverter.
- printBase64Binary(masterKeyEd25519).replaceAll("=", "");
+ String masterKeyEd25519Base64 = DatatypeConverter
+ .printBase64Binary(masterKeyEd25519).replaceAll("=", "");
String masterKeyEd25519Base64NoTrailingEqualSigns =
masterKeyEd25519Base64.replaceAll("=", "");
return masterKeyEd25519Base64NoTrailingEqualSigns;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
index 1ff15cb..1df9c55 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
@@ -1,7 +1,13 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayDirectory;
+import org.torproject.descriptor.RouterStatusEntry;
+import org.torproject.descriptor.ServerDescriptor;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -14,11 +20,6 @@ import java.util.Set;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayDirectory;
-import org.torproject.descriptor.RouterStatusEntry;
-import org.torproject.descriptor.ServerDescriptor;
-
/* TODO Write unit tests. */
public class RelayDirectoryImpl extends DescriptorImpl
@@ -73,8 +74,8 @@ public class RelayDirectoryImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.directoryDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -173,8 +174,8 @@ public class RelayDirectoryImpl extends DescriptorImpl
to += 1;
}
int toNoNewline = to;
- while (toNoNewline > from &&
- descriptorString.charAt(toNoNewline - 1) == '\n') {
+ while (toNoNewline > from
+ && descriptorString.charAt(toNoNewline - 1) == '\n') {
toNoNewline--;
}
byte[] part = new byte[toNoNewline - from];
@@ -189,99 +190,102 @@ public class RelayDirectoryImpl extends DescriptorImpl
private void parseHeader(byte[] headerBytes)
throws DescriptorParseException {
Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String publishedLine = null, nextCrypto = "",
- runningRoutersLine = null, routerStatusLine = null;
+ String publishedLine = null;
+ String nextCrypto = "";
+ String runningRoutersLine = null;
+ String routerStatusLine = null;
StringBuilder crypto = null;
while (s.hasNext()) {
String line = s.next();
if (line.isEmpty() || line.startsWith("@")) {
continue;
}
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
+ String lineNoOpt = line.startsWith("opt ")
+ ? line.substring("opt ".length()) : line;
String[] partsNoOpt = lineNoOpt.split("[ \t]+");
String keyword = partsNoOpt[0];
switch (keyword) {
- case "signed-directory":
- this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- if (publishedLine != null) {
- throw new DescriptorParseException("Keyword 'published' is "
- + "contained more than once, but must be contained exactly "
- + "once.");
- } else {
- publishedLine = line;
- }
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "dir-signing-key";
- break;
- case "recommended-software":
- this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "running-routers":
- runningRoutersLine = line;
- break;
- case "router-status":
- routerStatusLine = line;
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key") &&
- this.dirSigningKey == null) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v1 directory.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "signed-directory":
+ this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ if (publishedLine != null) {
+ throw new DescriptorParseException("Keyword 'published' is "
+ + "contained more than once, but must be contained "
+ + "exactly once.");
+ } else {
+ publishedLine = line;
+ }
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "recommended-software":
+ this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "running-routers":
+ runningRoutersLine = line;
+ break;
+ case "router-status":
+ routerStatusLine = line;
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in v1 directory.");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key")
+ && this.dirSigningKey == null) {
+ this.dirSigningKey = cryptoString;
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v1 directory.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v1 directory.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
if (publishedLine == null) {
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("opt ")
+ ? publishedLine.substring("opt ".length()) : publishedLine;
String[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+");
this.parsePublishedLine(publishedLine, publishedLineNoOpt,
publishedPartsNoOpt);
}
if (routerStatusLine != null) {
- String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") ?
- routerStatusLine.substring("opt ".length()) : routerStatusLine;
+ String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ")
+ ? routerStatusLine.substring("opt ".length())
+ : routerStatusLine;
String[] routerStatusPartsNoOpt =
routerStatusLineNoOpt.split("[ \t]+");
this.parseRouterStatusLine(routerStatusLine, routerStatusLineNoOpt,
routerStatusPartsNoOpt);
} else if (runningRoutersLine != null) {
String runningRoutersLineNoOpt =
- runningRoutersLine.startsWith("opt ") ?
- runningRoutersLine.substring("opt ".length()) :
- runningRoutersLine;
+ runningRoutersLine.startsWith("opt ")
+ ? runningRoutersLine.substring("opt ".length())
+ : runningRoutersLine;
String[] runningRoutersPartsNoOpt =
runningRoutersLineNoOpt.split("[ \t]+");
this.parseRunningRoutersLine(runningRoutersLine,
@@ -305,49 +309,49 @@ public class RelayDirectoryImpl extends DescriptorImpl
private void parseDirectorySignature(byte[] directorySignatureBytes)
throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(directorySignatureBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
StringBuilder crypto = null;
while (s.hasNext()) {
String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
+ String lineNoOpt = line.startsWith("opt ")
+ ? line.substring("opt ".length()) : line;
String[] partsNoOpt = lineNoOpt.split("[ \t]+");
String keyword = partsNoOpt[0];
switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
@@ -419,7 +423,8 @@ public class RelayDirectoryImpl extends DescriptorImpl
part = part.substring(1);
}
boolean isVerified;
- String fingerprint = null, nickname = null;
+ String fingerprint = null;
+ String nickname = null;
if (part.startsWith("$")) {
isVerified = false;
fingerprint = ParseHelper.parseTwentyByteHexString(debugLine,
@@ -442,7 +447,8 @@ public class RelayDirectoryImpl extends DescriptorImpl
if (part.contains("=")) {
String[] partParts = part.split("=");
if (partParts.length == 2) {
- boolean isVerified = true, isLive;
+ boolean isVerified = true;
+ boolean isLive;
String nickname;
if (partParts[0].startsWith("!")) {
isLive = false;
@@ -458,8 +464,10 @@ public class RelayDirectoryImpl extends DescriptorImpl
isVerified);
}
} else {
- boolean isVerified = false, isLive;
- String nickname = null, fingerprint;
+ boolean isVerified = false;
+ boolean isLive;
+ String nickname = null;
+ String fingerprint;
if (part.startsWith("!")) {
isLive = false;
fingerprint = ParseHelper.parseTwentyByteHexString(
@@ -489,37 +497,43 @@ public class RelayDirectoryImpl extends DescriptorImpl
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private String dirSigningKey;
+
@Override
public String getDirSigningKey() {
return this.dirSigningKey;
}
private List<String> recommendedSoftware;
+
@Override
public List<String> getRecommendedSoftware() {
- return this.recommendedSoftware == null ? null :
- new ArrayList<>(this.recommendedSoftware);
+ return this.recommendedSoftware == null ? null
+ : new ArrayList<>(this.recommendedSoftware);
}
private String directorySignature;
+
@Override
public String getDirectorySignature() {
return this.directorySignature;
}
private List<RouterStatusEntry> statusEntries = new ArrayList<>();
+
@Override
public List<RouterStatusEntry> getRouterStatusEntries() {
return new ArrayList<>(this.statusEntries);
}
private List<ServerDescriptor> serverDescriptors = new ArrayList<>();
+
@Override
public List<ServerDescriptor> getServerDescriptors() {
return new ArrayList<>(this.serverDescriptors);
@@ -527,18 +541,21 @@ public class RelayDirectoryImpl extends DescriptorImpl
private List<Exception> serverDescriptorParseExceptions =
new ArrayList<>();
+
@Override
public List<Exception> getServerDescriptorParseExceptions() {
return new ArrayList<>(this.serverDescriptorParseExceptions);
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String directoryDigest;
+
@Override
public String getDirectoryDigest() {
return this.directoryDigest;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
index 73d4dfa..6fe3fc6 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
@@ -1,14 +1,15 @@
/* Copyright 2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import java.util.ArrayList;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.ExtraInfoDescriptor;
import org.torproject.descriptor.RelayExtraInfoDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
public class RelayExtraInfoDescriptorImpl
extends ExtraInfoDescriptorImpl implements RelayExtraInfoDescriptor {
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index fe045c1..eae3ace 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -1,7 +1,11 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -18,9 +22,6 @@ import java.util.TreeSet;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
/* Contains a network status consensus or microdesc consensus. */
public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
implements RelayNetworkStatusConsensus {
@@ -74,8 +75,8 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.consensusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -96,65 +97,66 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-method":
- this.parseConsensusMethodLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-method":
+ this.parseConsensusMethodLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
private boolean microdescConsensus = false;
+
protected void parseStatusEntry(byte[] statusEntryBytes)
throws DescriptorParseException {
NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
statusEntryBytes, this.microdescConsensus,
this.failUnrecognizedDescriptorLines);
this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
+ List<String> unrecognizedStatusEntryLines = statusEntry
+ .getAndClearUnrecognizedLines();
if (unrecognizedStatusEntryLines != null) {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -171,21 +173,21 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "directory-footer":
- break;
- case "bandwidth-weights":
- this.parseBandwidthWeightsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "directory-footer":
+ break;
+ case "bandwidth-weights":
+ this.parseBandwidthWeightsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
@@ -317,74 +319,86 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
}
private String consensusDigest;
+
@Override
public String getConsensusDigest() {
return this.consensusDigest;
}
private int networkStatusVersion;
+
@Override
public int getNetworkStatusVersion() {
return this.networkStatusVersion;
}
private String consensusFlavor;
+
@Override
public String getConsensusFlavor() {
return this.consensusFlavor;
}
private int consensusMethod;
+
@Override
public int getConsensusMethod() {
return this.consensusMethod;
}
private long validAfterMillis;
+
@Override
public long getValidAfterMillis() {
return this.validAfterMillis;
}
private long freshUntilMillis;
+
@Override
public long getFreshUntilMillis() {
return this.freshUntilMillis;
}
private long validUntilMillis;
+
@Override
public long getValidUntilMillis() {
return this.validUntilMillis;
}
private long voteSeconds;
+
@Override
public long getVoteSeconds() {
return this.voteSeconds;
}
private long distSeconds;
+
@Override
public long getDistSeconds() {
return this.distSeconds;
}
private String[] recommendedClientVersions;
+
@Override
public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
+ return this.recommendedClientVersions == null ? null
+ : Arrays.asList(this.recommendedClientVersions);
}
private String[] recommendedServerVersions;
+
@Override
public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
+ return this.recommendedServerVersions == null ? null
+ : Arrays.asList(this.recommendedServerVersions);
}
private List<String> packageLines;
+
@Override
public List<String> getPackageLines() {
return this.packageLines == null ? null
@@ -392,23 +406,26 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
}
private String[] knownFlags;
+
@Override
public SortedSet<String> getKnownFlags() {
return new TreeSet<>(Arrays.asList(this.knownFlags));
}
private SortedMap<String, Integer> consensusParams;
+
@Override
public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
+ return this.consensusParams == null ? null
+ : new TreeMap<>(this.consensusParams);
}
private SortedMap<String, Integer> bandwidthWeights;
+
@Override
public SortedMap<String, Integer> getBandwidthWeights() {
- return this.bandwidthWeights == null ? null :
- new TreeMap<>(this.bandwidthWeights);
+ return this.bandwidthWeights == null ? null
+ : new TreeMap<>(this.bandwidthWeights);
}
}
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
index a5469db..2ea9fdf 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
@@ -1,7 +1,11 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatus;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -16,9 +20,6 @@ import java.util.TreeSet;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatus;
-
/* TODO Write unit tests. */
public class RelayNetworkStatusImpl extends NetworkStatusImpl
@@ -71,8 +72,8 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.statusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -98,61 +99,61 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "dir-options":
- this.parseDirOptionsLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key")) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- default:
- if (crypto != null) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "dir-options":
+ this.parseDirOptionsLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key")) {
+ this.dirSigningKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
@@ -165,8 +166,8 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
protected void parseDirectorySignature(byte[] directorySignatureBytes)
throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(directorySignatureBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
StringBuilder crypto = null;
while (s.hasNext()) {
@@ -174,38 +175,38 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, parts);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, parts);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
- }
}
}
}
@@ -296,86 +297,100 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
}
private String statusDigest;
+
@Override
public String getStatusDigest() {
return this.statusDigest;
}
private int networkStatusVersion;
+
@Override
public int getNetworkStatusVersion() {
return this.networkStatusVersion;
}
private String hostname;
+
@Override
public String getHostname() {
return this.hostname;
}
private String address;
+
@Override
public String getAddress() {
return this.address;
}
private int dirPort;
+
@Override
public int getDirport() {
return this.dirPort;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private String contactLine;
+
@Override
public String getContactLine() {
return this.contactLine;
}
private String dirSigningKey;
+
@Override
public String getDirSigningKey() {
return this.dirSigningKey;
}
private String[] recommendedClientVersions;
+
@Override
public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
+ return this.recommendedClientVersions == null ? null
+ : Arrays.asList(this.recommendedClientVersions);
}
private String[] recommendedServerVersions;
+
@Override
public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
+ return this.recommendedServerVersions == null ? null
+ : Arrays.asList(this.recommendedServerVersions);
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private String[] dirOptions;
+
@Override
public SortedSet<String> getDirOptions() {
return new TreeSet<>(Arrays.asList(this.dirOptions));
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String directorySignature;
+
@Override
public String getDirectorySignature() {
return this.directorySignature;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index 384ad1f..a9bb928 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -1,9 +1,11 @@
/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.DirectorySignature;
+import org.torproject.descriptor.RelayNetworkStatusVote;
import java.util.ArrayList;
import java.util.Arrays;
@@ -17,8 +19,6 @@ import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
-import org.torproject.descriptor.RelayNetworkStatusVote;
-
/* Contains a network status vote. */
public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
implements RelayNetworkStatusVote {
@@ -53,8 +53,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList((
"consensus-methods,client-versions,server-versions,"
+ "flag-thresholds,params,contact,"
- + "legacy-key,dir-key-crosscert,dir-address,directory-footer").
- split(",")));
+ + "legacy-key,dir-key-crosscert,dir-address,directory-footer")
+ .split(",")));
this.checkAtMostOnceKeywords(atMostOnceKeywords);
Set<String> atLeastOnceKeywords = new HashSet<>(Arrays.asList(
"directory-signature"));
@@ -87,129 +87,129 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
String[] parts = line.split("[ \t]+");
String keyword = parts[0];
switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-methods":
- this.parseConsensusMethodsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "legacy-dir-key":
- this.parseLegacyDirKeyLine(line, parts);
- break;
- case "dir-key-published":
- this.parseDirKeyPublished(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-methods":
+ this.parseConsensusMethodsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "legacy-dir-key":
+ this.parseLegacyDirKeyLine(line, parts);
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublished(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
+ break;
case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
break;
case "dir-signing-key":
- this.dirSigningKey = cryptoString;
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
break;
case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
break;
case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in vote.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
+ case "-----BEGIN":
+ crypto = new StringBuilder();
crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in vote.");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in vote.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in vote.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -341,38 +341,38 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
try {
for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
- default:
- // empty
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue()
+ .replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
+ default:
+ // empty
}
}
} catch (NumberFormatException ex) {
@@ -454,7 +454,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
if (parts.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
- this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line, parts[1]);
+ this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
}
private void parseDirKeyPublished(String line, String[] parts)
@@ -517,90 +518,105 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String identity;
+
@Override
public String getIdentity() {
return this.identity;
}
private String hostname;
+
@Override
public String getHostname() {
return this.hostname;
}
private String address;
+
@Override
public String getAddress() {
return this.address;
}
private int dirPort;
+
@Override
public int getDirport() {
return this.dirPort;
}
private int orPort;
+
@Override
public int getOrport() {
return this.orPort;
}
private String contactLine;
+
@Override
public String getContactLine() {
return this.contactLine;
}
private int dirKeyCertificateVersion;
+
@Override
public int getDirKeyCertificateVersion() {
return this.dirKeyCertificateVersion;
}
private String legacyDirKey;
+
@Override
public String getLegacyDirKey() {
return this.legacyDirKey;
}
private long dirKeyPublishedMillis;
+
@Override
public long getDirKeyPublishedMillis() {
return this.dirKeyPublishedMillis;
}
private long dirKeyExpiresMillis;
+
@Override
public long getDirKeyExpiresMillis() {
return this.dirKeyExpiresMillis;
}
private String dirIdentityKey;
+
@Override
public String getDirIdentityKey() {
return this.dirIdentityKey;
}
private String dirSigningKey;
+
@Override
public String getDirSigningKey() {
return this.dirSigningKey;
}
private String dirKeyCrosscert;
+
@Override
public String getDirKeyCrosscert() {
return this.dirKeyCrosscert;
}
private String dirKeyCertification;
+
@Override
public String getDirKeyCertification() {
return this.dirKeyCertification;
@@ -622,69 +638,80 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
}
private int networkStatusVersion;
+
@Override
public int getNetworkStatusVersion() {
return this.networkStatusVersion;
}
private Integer[] consensusMethods;
+
@Override
public List<Integer> getConsensusMethods() {
- return this.consensusMethods == null ? null :
- Arrays.asList(this.consensusMethods);
+ return this.consensusMethods == null ? null
+ : Arrays.asList(this.consensusMethods);
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private long validAfterMillis;
+
@Override
public long getValidAfterMillis() {
return this.validAfterMillis;
}
private long freshUntilMillis;
+
@Override
public long getFreshUntilMillis() {
return this.freshUntilMillis;
}
private long validUntilMillis;
+
@Override
public long getValidUntilMillis() {
return this.validUntilMillis;
}
private long voteSeconds;
+
@Override
public long getVoteSeconds() {
return this.voteSeconds;
}
private long distSeconds;
+
@Override
public long getDistSeconds() {
return this.distSeconds;
}
private String[] recommendedClientVersions;
+
@Override
public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
+ return this.recommendedClientVersions == null ? null
+ : Arrays.asList(this.recommendedClientVersions);
}
private String[] recommendedServerVersions;
+
@Override
public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
+ return this.recommendedServerVersions == null ? null
+ : Arrays.asList(this.recommendedServerVersions);
}
private List<String> packageLines;
+
@Override
public List<String> getPackageLines() {
return this.packageLines == null ? null
@@ -692,70 +719,81 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
}
private String[] knownFlags;
+
@Override
public SortedSet<String> getKnownFlags() {
return new TreeSet<>(Arrays.asList(this.knownFlags));
}
private long stableUptime;
+
@Override
public long getStableUptime() {
return this.stableUptime;
}
private long stableMtbf;
+
@Override
public long getStableMtbf() {
return this.stableMtbf;
}
private long fastBandwidth;
+
@Override
public long getFastBandwidth() {
return this.fastBandwidth;
}
private double guardWfu;
+
@Override
public double getGuardWfu() {
return this.guardWfu;
}
private long guardTk;
+
@Override
public long getGuardTk() {
return this.guardTk;
}
private long guardBandwidthIncludingExits;
+
@Override
public long getGuardBandwidthIncludingExits() {
return this.guardBandwidthIncludingExits;
}
private long guardBandwidthExcludingExits;
+
@Override
public long getGuardBandwidthExcludingExits() {
return this.guardBandwidthExcludingExits;
}
private int enoughMtbfInfo;
+
@Override
public int getEnoughMtbfInfo() {
return this.enoughMtbfInfo;
}
private int ignoringAdvertisedBws;
+
@Override
public int getIgnoringAdvertisedBws() {
return this.ignoringAdvertisedBws;
}
private SortedMap<String, Integer> consensusParams;
+
@Override
public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
+ return this.consensusParams == null ? null
+ : new TreeMap<>(this.consensusParams);
}
}
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
index 4957072..b40423d 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
@@ -1,14 +1,15 @@
/* Copyright 2015 The Tor Project
* See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-import java.util.ArrayList;
-import java.util.List;
+package org.torproject.descriptor.impl;
import org.torproject.descriptor.DescriptorParseException;
import org.torproject.descriptor.RelayServerDescriptor;
import org.torproject.descriptor.ServerDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
public class RelayServerDescriptorImpl extends ServerDescriptorImpl
implements RelayServerDescriptor {
diff --git a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
index a359c50..1ada050 100644
--- a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
@@ -1,5 +1,6 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
import org.torproject.descriptor.RouterStatusEntry;
@@ -15,24 +16,28 @@ public class RouterStatusEntryImpl implements RouterStatusEntry {
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private boolean isLive;
+
@Override
public boolean isLive() {
return this.isLive;
}
private boolean isVerified;
+
@Override
public boolean isVerified() {
return this.isVerified;
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
index 1805dca..47d1b5b 100644
--- a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -1,7 +1,12 @@
/* Copyright 2012--2015 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ServerDescriptor;
+
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -14,10 +19,6 @@ import java.util.Set;
import javax.xml.bind.DatatypeConverter;
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ServerDescriptor;
-
/* Contains a server descriptor. */
public abstract class ServerDescriptorImpl extends DescriptorImpl
implements ServerDescriptor {
@@ -43,8 +44,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
+ "router-digest").split(",")));
this.checkAtMostOnceKeywords(atMostOnceKeywords);
this.checkFirstKeyword("router");
- if (this.getKeywordCount("accept") == 0 &&
- this.getKeywordCount("reject") == 0) {
+ if (this.getKeywordCount("accept") == 0
+ && this.getKeywordCount("reject") == 0) {
throw new DescriptorParseException("Either keyword 'accept' or "
+ "'reject' must be contained at least once.");
}
@@ -53,8 +54,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
}
private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes))
+ .useDelimiter("\n");
String nextCrypto = "";
List<String> cryptoLines = null;
while (s.hasNext()) {
@@ -62,179 +63,179 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
if (line.startsWith("@")) {
continue;
}
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
+ String lineNoOpt = line.startsWith("opt ")
+ ? line.substring("opt ".length()) : line;
String[] partsNoOpt = lineNoOpt.split("[ \t]+");
String keyword = partsNoOpt[0];
switch (keyword) {
- case "router":
- this.parseRouterLine(line, lineNoOpt, partsNoOpt);
- break;
- case "or-address":
- this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bandwidth":
- this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
- break;
- case "platform":
- this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hibernating":
- this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
- break;
- case "uptime":
- this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key":
- this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key";
- break;
- case "signing-key":
- this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "signing-key";
- break;
- case "accept":
- this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
- break;
- case "reject":
- this.parseRejectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "contact":
- this.parseContactLine(line, lineNoOpt, partsNoOpt);
- break;
- case "family":
- this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "eventdns":
- this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "caches-extra-info":
- this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "extra-info-digest":
- this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidden-service-dir":
- this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
- break;
- case "protocols":
- this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "allow-single-hop-exits":
- this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dircacheport":
- this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "ipv6-policy":
- this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key-crosscert":
- this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key-crosscert";
- break;
- case "ntor-onion-key-crosscert":
- this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "ntor-onion-key-crosscert";
- break;
- case "tunnelled-dir-server":
- this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
+ case "router":
+ this.parseRouterLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "or-address":
+ this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bandwidth":
+ this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "platform":
+ this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hibernating":
+ this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "uptime":
+ this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
+ break;
case "onion-key":
- this.onionKey = cryptoString;
+ this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key";
break;
case "signing-key":
- this.signingKey = cryptoString;
+ this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "signing-key";
+ break;
+ case "accept":
+ this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "reject":
+ this.parseRejectLine(line, lineNoOpt, partsNoOpt);
break;
case "router-signature":
- this.routerSignature = cryptoString;
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "contact":
+ this.parseContactLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "family":
+ this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "eventdns":
+ this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "caches-extra-info":
+ this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "extra-info-digest":
+ this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidden-service-dir":
+ this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "protocols":
+ this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "allow-single-hop-exits":
+ this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dircacheport":
+ this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ipv6-policy":
+ this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
break;
case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
+ break;
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
break;
case "onion-key-crosscert":
- this.onionKeyCrosscert = cryptoString;
+ this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key-crosscert";
break;
case "ntor-onion-key-crosscert":
- this.ntorOnionKeyCrosscert = cryptoString;
+ this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "ntor-onion-key-crosscert";
break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in server descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- }
- cryptoLines = null;
- nextCrypto = "";
- break;
- default:
- if (cryptoLines != null) {
+ case "tunnelled-dir-server":
+ this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in server descriptor.");
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "onion-key":
+ this.onionKey = cryptoString;
+ break;
+ case "signing-key":
+ this.signingKey = cryptoString;
+ break;
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ case "onion-key-crosscert":
+ this.onionKeyCrosscert = cryptoString;
+ break;
+ case "ntor-onion-key-crosscert":
+ this.ntorOnionKeyCrosscert = cryptoString;
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
} else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
}
- this.unrecognizedLines.add(line);
}
- }
}
}
}
@@ -276,8 +277,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
if (partsNoOpt.length == 4) {
this.bandwidthObserved = Integer.parseInt(partsNoOpt[3]);
}
- if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0 &&
- this.bandwidthObserved >= 0) {
+ if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0
+ && this.bandwidthObserved >= 0) {
isValid = true;
}
if (partsNoOpt.length < 4) {
@@ -438,7 +439,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
if (partsNoOpt.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
- this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1], line);
+ this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1],
+ line);
}
private void parseCachesExtraInfoLine(String line, String lineNoOpt,
@@ -482,17 +484,18 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
private void parseProtocolsLine(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
- int linkIndex = -1, circuitIndex = -1;
+ int linkIndex = -1;
+ int circuitIndex = -1;
for (int i = 1; i < partsNoOpt.length; i++) {
switch (partsNoOpt[i]) {
- case "Link":
- linkIndex = i;
- break;
- case "Circuit":
- circuitIndex = i;
- break;
- default:
- // empty
+ case "Link":
+ linkIndex = i;
+ break;
+ case "Circuit":
+ circuitIndex = i;
+ break;
+ default:
+ // empty
}
}
if (linkIndex < 0 || circuitIndex < 0 || circuitIndex < linkIndex) {
@@ -555,7 +558,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
if (partsNoOpt.length != 3) {
isValid = false;
} else {
- switch (partsNoOpt[1]) {
+ switch (partsNoOpt[1]) {
case "accept":
case "reject":
this.ipv6DefaultPolicy = partsNoOpt[1];
@@ -570,7 +573,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
break;
default:
isValid = false;
- }
+ }
}
if (!isValid) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
@@ -680,8 +683,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
System.arraycopy(this.getRawDescriptorBytes(), start,
forDigest, 0, sig - start);
this.serverDescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
+ MessageDigest.getInstance("SHA-1").digest(forDigest))
+ .toLowerCase();
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -712,8 +715,8 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
0, sig - start);
this.serverDescriptorDigestSha256 =
DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
+ MessageDigest.getInstance("SHA-256").digest(forDigest))
+ .replaceAll("=", "");
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
@@ -727,256 +730,298 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
}
private String serverDescriptorDigest;
+
@Override
public String getServerDescriptorDigest() {
return this.serverDescriptorDigest;
}
private String serverDescriptorDigestSha256;
+
@Override
public String getServerDescriptorDigestSha256() {
return this.serverDescriptorDigestSha256;
}
private String nickname;
+
@Override
public String getNickname() {
return this.nickname;
}
private String address;
+
@Override
public String getAddress() {
return this.address;
}
private int orPort;
+
@Override
public int getOrPort() {
return this.orPort;
}
private int socksPort;
+
@Override
public int getSocksPort() {
return this.socksPort;
}
private int dirPort;
+
@Override
public int getDirPort() {
return this.dirPort;
}
private List<String> orAddresses = new ArrayList<>();
+
@Override
public List<String> getOrAddresses() {
return new ArrayList<>(this.orAddresses);
}
private int bandwidthRate;
+
@Override
public int getBandwidthRate() {
return this.bandwidthRate;
}
private int bandwidthBurst;
+
@Override
public int getBandwidthBurst() {
return this.bandwidthBurst;
}
private int bandwidthObserved;
+
@Override
public int getBandwidthObserved() {
return this.bandwidthObserved;
}
private String platform;
+
@Override
public String getPlatform() {
return this.platform;
}
private long publishedMillis;
+
@Override
public long getPublishedMillis() {
return this.publishedMillis;
}
private String fingerprint;
+
@Override
public String getFingerprint() {
return this.fingerprint;
}
private boolean hibernating;
+
@Override
public boolean isHibernating() {
return this.hibernating;
}
private Long uptime;
+
@Override
public Long getUptime() {
return this.uptime;
}
private String onionKey;
+
@Override
public String getOnionKey() {
return this.onionKey;
}
private String signingKey;
+
@Override
public String getSigningKey() {
return this.signingKey;
}
private List<String> exitPolicyLines = new ArrayList<>();
+
@Override
public List<String> getExitPolicyLines() {
return new ArrayList<>(this.exitPolicyLines);
}
private String routerSignature;
+
@Override
public String getRouterSignature() {
return this.routerSignature;
}
private String contact;
+
@Override
public String getContact() {
return this.contact;
}
private String[] familyEntries;
+
@Override
public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
+ return this.familyEntries == null ? null
+ : Arrays.asList(this.familyEntries);
}
private BandwidthHistory readHistory;
+
@Override
public BandwidthHistory getReadHistory() {
return this.readHistory;
}
private BandwidthHistory writeHistory;
+
@Override
public BandwidthHistory getWriteHistory() {
return this.writeHistory;
}
private boolean usesEnhancedDnsLogic;
+
@Override
public boolean getUsesEnhancedDnsLogic() {
return this.usesEnhancedDnsLogic;
}
private boolean cachesExtraInfo;
+
@Override
public boolean getCachesExtraInfo() {
return this.cachesExtraInfo;
}
private String extraInfoDigest;
+
@Override
public String getExtraInfoDigest() {
return this.extraInfoDigest;
}
private String extraInfoDigestSha256;
+
@Override
public String getExtraInfoDigestSha256() {
return this.extraInfoDigestSha256;
}
private Integer[] hiddenServiceDirVersions;
+
@Override
public List<Integer> getHiddenServiceDirVersions() {
- return this.hiddenServiceDirVersions == null ? null :
- Arrays.asList(this.hiddenServiceDirVersions);
+ return this.hiddenServiceDirVersions == null ? null
+ : Arrays.asList(this.hiddenServiceDirVersions);
}
private Integer[] linkProtocolVersions;
+
@Override
public List<Integer> getLinkProtocolVersions() {
- return this.linkProtocolVersions == null ? null :
- Arrays.asList(this.linkProtocolVersions);
+ return this.linkProtocolVersions == null ? null
+ : Arrays.asList(this.linkProtocolVersions);
}
private Integer[] circuitProtocolVersions;
+
@Override
public List<Integer> getCircuitProtocolVersions() {
- return this.circuitProtocolVersions == null ? null :
- Arrays.asList(this.circuitProtocolVersions);
+ return this.circuitProtocolVersions == null ? null
+ : Arrays.asList(this.circuitProtocolVersions);
}
private boolean allowSingleHopExits;
+
@Override
public boolean getAllowSingleHopExits() {
return this.allowSingleHopExits;
}
private String ipv6DefaultPolicy;
+
@Override
public String getIpv6DefaultPolicy() {
return this.ipv6DefaultPolicy;
}
private String ipv6PortList;
+
@Override
public String getIpv6PortList() {
return this.ipv6PortList;
}
private String ntorOnionKey;
+
@Override
public String getNtorOnionKey() {
return this.ntorOnionKey;
}
private String identityEd25519;
+
@Override
public String getIdentityEd25519() {
return this.identityEd25519;
}
private String masterKeyEd25519;
+
@Override
public String getMasterKeyEd25519() {
return this.masterKeyEd25519;
}
private String routerSignatureEd25519;
+
@Override
public String getRouterSignatureEd25519() {
return this.routerSignatureEd25519;
}
private String onionKeyCrosscert;
+
@Override
public String getOnionKeyCrosscert() {
return this.onionKeyCrosscert;
}
private String ntorOnionKeyCrosscert;
+
@Override
public String getNtorOnionKeyCrosscert() {
return this.ntorOnionKeyCrosscert;
}
private int ntorOnionKeyCrosscertSign = -1;
+
@Override
public int getNtorOnionKeyCrosscertSign() {
return ntorOnionKeyCrosscertSign;
}
private boolean tunnelledDirServer;
+
@Override
public boolean getTunnelledDirServer() {
return this.tunnelledDirServer;
diff --git a/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
index 0800de0..71c762f 100644
--- a/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
@@ -1,8 +1,12 @@
/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
+
package org.torproject.descriptor.impl;
+import org.torproject.descriptor.Descriptor;
import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.TorperfResult;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -12,9 +16,6 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.TorperfResult;
-
public class TorperfResultImpl extends DescriptorImpl
implements TorperfResult {
@@ -80,100 +81,102 @@ public class TorperfResultImpl extends DescriptorImpl
this.markKeyAsParsed(key, line);
String value = keyValueParts[1];
switch (key) {
- case "SOURCE":
- this.parseSource(value, keyValue, line);
- break;
- case "FILESIZE":
- this.parseFileSize(value, keyValue, line);
- break;
- case "START":
- this.parseStart(value, keyValue, line);
- break;
- case "SOCKET":
- this.parseSocket(value, keyValue, line);
- break;
- case "CONNECT":
- this.parseConnect(value, keyValue, line);
- break;
- case "NEGOTIATE":
- this.parseNegotiate(value, keyValue, line);
- break;
- case "REQUEST":
- this.parseRequest(value, keyValue, line);
- break;
- case "RESPONSE":
- this.parseResponse(value, keyValue, line);
- break;
- case "DATAREQUEST":
- this.parseDataRequest(value, keyValue, line);
- break;
- case "DATARESPONSE":
- this.parseDataResponse(value, keyValue, line);
- break;
- case "DATACOMPLETE":
- this.parseDataComplete(value, keyValue, line);
- break;
- case "WRITEBYTES":
- this.parseWriteBytes(value, keyValue, line);
- break;
- case "READBYTES":
- this.parseReadBytes(value, keyValue, line);
- break;
- case "DIDTIMEOUT":
- this.parseDidTimeout(value, keyValue, line);
- break;
- case "LAUNCH":
- this.parseLaunch(value, keyValue, line);
- break;
- case "USED_AT":
- this.parseUsedAt(value, keyValue, line);
- break;
- case "PATH":
- this.parsePath(value, keyValue, line);
- break;
- case "BUILDTIMES":
- this.parseBuildTimes(value, keyValue, line);
- break;
- case "TIMEOUT":
- this.parseTimeout(value, keyValue, line);
- break;
- case "QUANTILE":
- this.parseQuantile(value, keyValue, line);
- break;
- case "CIRC_ID":
- this.parseCircId(value, keyValue, line);
- break;
- case "USED_BY":
- this.parseUsedBy(value, keyValue, line);
- break;
- default:
- if (key.startsWith("DATAPERC")) {
- this.parseDataPercentile(value, keyValue, line);
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized key '" + key
- + "' in line '" + line + "'.");
- } else {
- if (this.unrecognizedKeys == null) {
- this.unrecognizedKeys = new TreeMap<>();
- }
- this.unrecognizedKeys.put(key, value);
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
+ case "SOURCE":
+ this.parseSource(value, keyValue, line);
+ break;
+ case "FILESIZE":
+ this.parseFileSize(value, keyValue, line);
+ break;
+ case "START":
+ this.parseStart(value, keyValue, line);
+ break;
+ case "SOCKET":
+ this.parseSocket(value, keyValue, line);
+ break;
+ case "CONNECT":
+ this.parseConnect(value, keyValue, line);
+ break;
+ case "NEGOTIATE":
+ this.parseNegotiate(value, keyValue, line);
+ break;
+ case "REQUEST":
+ this.parseRequest(value, keyValue, line);
+ break;
+ case "RESPONSE":
+ this.parseResponse(value, keyValue, line);
+ break;
+ case "DATAREQUEST":
+ this.parseDataRequest(value, keyValue, line);
+ break;
+ case "DATARESPONSE":
+ this.parseDataResponse(value, keyValue, line);
+ break;
+ case "DATACOMPLETE":
+ this.parseDataComplete(value, keyValue, line);
+ break;
+ case "WRITEBYTES":
+ this.parseWriteBytes(value, keyValue, line);
+ break;
+ case "READBYTES":
+ this.parseReadBytes(value, keyValue, line);
+ break;
+ case "DIDTIMEOUT":
+ this.parseDidTimeout(value, keyValue, line);
+ break;
+ case "LAUNCH":
+ this.parseLaunch(value, keyValue, line);
+ break;
+ case "USED_AT":
+ this.parseUsedAt(value, keyValue, line);
+ break;
+ case "PATH":
+ this.parsePath(value, keyValue, line);
+ break;
+ case "BUILDTIMES":
+ this.parseBuildTimes(value, keyValue, line);
+ break;
+ case "TIMEOUT":
+ this.parseTimeout(value, keyValue, line);
+ break;
+ case "QUANTILE":
+ this.parseQuantile(value, keyValue, line);
+ break;
+ case "CIRC_ID":
+ this.parseCircId(value, keyValue, line);
+ break;
+ case "USED_BY":
+ this.parseUsedBy(value, keyValue, line);
+ break;
+ default:
+ if (key.startsWith("DATAPERC")) {
+ this.parseDataPercentile(value, keyValue, line);
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized key '" + key
+ + "' in line '" + line + "'.");
+ } else {
+ if (this.unrecognizedKeys == null) {
+ this.unrecognizedKeys = new TreeMap<>();
+ }
+ this.unrecognizedKeys.put(key, value);
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ if (!this.unrecognizedLines.contains(line)) {
+ this.unrecognizedLines.add(line);
+ }
}
- if (!this.unrecognizedLines.contains(line)) {
- this.unrecognizedLines.add(line);
- }
- }
}
}
this.checkAllRequiredKeysParsed(line);
}
private Set<String> parsedKeys = new HashSet<>();
+
private Set<String> requiredKeys = new HashSet<>(Arrays.asList(
("SOURCE,FILESIZE,START,SOCKET,CONNECT,NEGOTIATE,REQUEST,RESPONSE,"
- + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES").
- split(",")));
+ + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES")
+ .split(",")));
+
private void markKeyAsParsed(String key, String line)
throws DescriptorParseException {
if (this.parsedKeys.contains(key)) {
@@ -184,6 +187,7 @@ public class TorperfResultImpl extends DescriptorImpl
this.parsedKeys.add(key);
this.requiredKeys.remove(key);
}
+
private void checkAllRequiredKeysParsed(String line)
throws DescriptorParseException {
for (String key : this.requiredKeys) {
@@ -397,6 +401,7 @@ public class TorperfResultImpl extends DescriptorImpl
}
private SortedMap<String, String> unrecognizedKeys;
+
@Override
public SortedMap<String, String> getUnrecognizedKeys() {
return this.unrecognizedKeys == null ? null
@@ -404,90 +409,105 @@ public class TorperfResultImpl extends DescriptorImpl
}
private String source;
+
@Override
public String getSource() {
return this.source;
}
private int fileSize;
+
@Override
public int getFileSize() {
return this.fileSize;
}
private long startMillis;
+
@Override
public long getStartMillis() {
return this.startMillis;
}
private long socketMillis;
+
@Override
public long getSocketMillis() {
return this.socketMillis;
}
private long connectMillis;
+
@Override
public long getConnectMillis() {
return this.connectMillis;
}
private long negotiateMillis;
+
@Override
public long getNegotiateMillis() {
return this.negotiateMillis;
}
private long requestMillis;
+
@Override
public long getRequestMillis() {
return this.requestMillis;
}
private long responseMillis;
+
@Override
public long getResponseMillis() {
return this.responseMillis;
}
private long dataRequestMillis;
+
@Override
public long getDataRequestMillis() {
return this.dataRequestMillis;
}
private long dataResponseMillis;
+
@Override
public long getDataResponseMillis() {
return this.dataResponseMillis;
}
private long dataCompleteMillis;
+
@Override
public long getDataCompleteMillis() {
return this.dataCompleteMillis;
}
private int writeBytes;
+
@Override
public int getWriteBytes() {
return this.writeBytes;
}
private int readBytes;
+
@Override
public int getReadBytes() {
return this.readBytes;
}
private boolean didTimeout;
+
@Override
public Boolean didTimeout() {
return this.didTimeout;
}
private SortedMap<Integer, Long> dataPercentiles;
+
@Override
public SortedMap<Integer, Long> getDataPercentiles() {
return this.dataPercentiles == null ? null
@@ -495,49 +515,57 @@ public class TorperfResultImpl extends DescriptorImpl
}
private long launchMillis = -1L;
+
@Override
public long getLaunchMillis() {
return this.launchMillis;
}
private long usedAtMillis = -1L;
+
@Override
public long getUsedAtMillis() {
return this.usedAtMillis;
}
private String[] path;
+
@Override
public List<String> getPath() {
return this.path == null ? null : Arrays.asList(this.path);
}
private Long[] buildTimes;
+
@Override
public List<Long> getBuildTimes() {
- return this.buildTimes == null ? null :
- Arrays.asList(this.buildTimes);
+ return this.buildTimes == null ? null
+ : Arrays.asList(this.buildTimes);
}
private long timeout = -1L;
+
@Override
public long getTimeout() {
return this.timeout;
}
private double quantile = -1.0;
+
@Override
public double getQuantile() {
return this.quantile;
}
private int circId = -1;
+
@Override
public int getCircId() {
return this.circId;
}
private int usedBy = -1;
+
@Override
public int getUsedBy() {
return this.usedBy;
1
0
06 Jul '16
commit 600a020877bb82ee1fd5852a5694d268411e2ed4
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Tue Jul 5 10:28:06 2016 +0200
Move all Java sources to src/main/java/.
---
build.xml | 10 +-
.../torproject/descriptor/BandwidthHistory.java | 52 +
.../descriptor/BridgeExtraInfoDescriptor.java | 25 +
.../torproject/descriptor/BridgeNetworkStatus.java | 128 ++
.../descriptor/BridgePoolAssignment.java | 47 +
.../descriptor/BridgeServerDescriptor.java | 24 +
.../java/org/torproject/descriptor/Descriptor.java | 39 +
.../torproject/descriptor/DescriptorCollector.java | 62 +
.../descriptor/DescriptorDownloader.java | 198 +++
.../org/torproject/descriptor/DescriptorFile.java | 77 +
.../descriptor/DescriptorParseException.java | 20 +
.../torproject/descriptor/DescriptorParser.java | 47 +
.../torproject/descriptor/DescriptorReader.java | 143 ++
.../torproject/descriptor/DescriptorRequest.java | 100 ++
.../descriptor/DescriptorSourceFactory.java | 187 +++
.../org/torproject/descriptor/DirSourceEntry.java | 96 ++
.../descriptor/DirectoryKeyCertificate.java | 109 ++
.../torproject/descriptor/DirectorySignature.java | 52 +
.../java/org/torproject/descriptor/ExitList.java | 92 ++
.../org/torproject/descriptor/ExitListEntry.java | 55 +
.../torproject/descriptor/ExtraInfoDescriptor.java | 646 ++++++++
.../ImplementationNotAccessibleException.java | 22 +
.../org/torproject/descriptor/Microdescriptor.java | 135 ++
.../torproject/descriptor/NetworkStatusEntry.java | 177 ++
.../org/torproject/descriptor/RelayDirectory.java | 104 ++
.../descriptor/RelayExtraInfoDescriptor.java | 21 +
.../torproject/descriptor/RelayNetworkStatus.java | 176 ++
.../descriptor/RelayNetworkStatusConsensus.java | 223 +++
.../descriptor/RelayNetworkStatusVote.java | 408 +++++
.../descriptor/RelayServerDescriptor.java | 20 +
.../torproject/descriptor/RouterStatusEntry.java | 51 +
.../torproject/descriptor/ServerDescriptor.java | 435 +++++
.../org/torproject/descriptor/TorperfResult.java | 215 +++
.../descriptor/impl/BandwidthHistoryImpl.java | 100 ++
.../descriptor/impl/BlockingIteratorImpl.java | 98 ++
.../impl/BridgeExtraInfoDescriptorImpl.java | 37 +
.../descriptor/impl/BridgeNetworkStatusImpl.java | 230 +++
.../descriptor/impl/BridgePoolAssignmentImpl.java | 99 ++
.../impl/BridgeServerDescriptorImpl.java | 37 +
.../descriptor/impl/DescriptorCollectorImpl.java | 249 +++
.../descriptor/impl/DescriptorDownloaderImpl.java | 283 ++++
.../descriptor/impl/DescriptorFileImpl.java | 78 +
.../torproject/descriptor/impl/DescriptorImpl.java | 337 ++++
.../descriptor/impl/DescriptorParseException.java | 15 +
.../descriptor/impl/DescriptorParserImpl.java | 28 +
.../descriptor/impl/DescriptorReaderImpl.java | 364 ++++
.../descriptor/impl/DescriptorRequestImpl.java | 114 ++
.../descriptor/impl/DirSourceEntryImpl.java | 218 +++
.../descriptor/impl/DirectoryDownloader.java | 104 ++
.../impl/DirectoryKeyCertificateImpl.java | 308 ++++
.../descriptor/impl/DirectorySignatureImpl.java | 115 ++
.../descriptor/impl/DownloadCoordinator.java | 10 +
.../descriptor/impl/DownloadCoordinatorImpl.java | 298 ++++
.../descriptor/impl/ExitListEntryImpl.java | 216 +++
.../torproject/descriptor/impl/ExitListImpl.java | 142 ++
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 1284 +++++++++++++++
.../descriptor/impl/MicrodescriptorImpl.java | 328 ++++
.../descriptor/impl/NetworkStatusEntryImpl.java | 382 +++++
.../descriptor/impl/NetworkStatusImpl.java | 270 +++
.../torproject/descriptor/impl/ParseHelper.java | 567 +++++++
.../descriptor/impl/RelayDirectoryImpl.java | 547 ++++++
.../impl/RelayExtraInfoDescriptorImpl.java | 37 +
.../impl/RelayNetworkStatusConsensusImpl.java | 414 +++++
.../descriptor/impl/RelayNetworkStatusImpl.java | 384 +++++
.../impl/RelayNetworkStatusVoteImpl.java | 761 +++++++++
.../descriptor/impl/RelayServerDescriptorImpl.java | 37 +
.../descriptor/impl/RouterStatusEntryImpl.java | 41 +
.../descriptor/impl/ServerDescriptorImpl.java | 985 +++++++++++
.../descriptor/impl/TorperfResultImpl.java | 546 ++++++
.../org/torproject/descriptor/package-info.java | 80 +
.../torproject/descriptor/BandwidthHistory.java | 52 -
.../descriptor/BridgeExtraInfoDescriptor.java | 25 -
.../torproject/descriptor/BridgeNetworkStatus.java | 128 --
.../descriptor/BridgePoolAssignment.java | 47 -
.../descriptor/BridgeServerDescriptor.java | 24 -
src/org/torproject/descriptor/Descriptor.java | 39 -
.../torproject/descriptor/DescriptorCollector.java | 62 -
.../descriptor/DescriptorDownloader.java | 198 ---
src/org/torproject/descriptor/DescriptorFile.java | 77 -
.../descriptor/DescriptorParseException.java | 20 -
.../torproject/descriptor/DescriptorParser.java | 47 -
.../torproject/descriptor/DescriptorReader.java | 143 --
.../torproject/descriptor/DescriptorRequest.java | 100 --
.../descriptor/DescriptorSourceFactory.java | 187 ---
src/org/torproject/descriptor/DirSourceEntry.java | 96 --
.../descriptor/DirectoryKeyCertificate.java | 109 --
.../torproject/descriptor/DirectorySignature.java | 52 -
src/org/torproject/descriptor/ExitList.java | 92 --
src/org/torproject/descriptor/ExitListEntry.java | 55 -
.../torproject/descriptor/ExtraInfoDescriptor.java | 646 --------
.../ImplementationNotAccessibleException.java | 22 -
src/org/torproject/descriptor/Microdescriptor.java | 135 --
.../torproject/descriptor/NetworkStatusEntry.java | 177 --
src/org/torproject/descriptor/RelayDirectory.java | 104 --
.../descriptor/RelayExtraInfoDescriptor.java | 21 -
.../torproject/descriptor/RelayNetworkStatus.java | 176 --
.../descriptor/RelayNetworkStatusConsensus.java | 223 ---
.../descriptor/RelayNetworkStatusVote.java | 408 -----
.../descriptor/RelayServerDescriptor.java | 20 -
.../torproject/descriptor/RouterStatusEntry.java | 51 -
.../torproject/descriptor/ServerDescriptor.java | 435 -----
src/org/torproject/descriptor/TorperfResult.java | 215 ---
.../descriptor/impl/BandwidthHistoryImpl.java | 100 --
.../descriptor/impl/BlockingIteratorImpl.java | 98 --
.../impl/BridgeExtraInfoDescriptorImpl.java | 37 -
.../descriptor/impl/BridgeNetworkStatusImpl.java | 230 ---
.../descriptor/impl/BridgePoolAssignmentImpl.java | 99 --
.../impl/BridgeServerDescriptorImpl.java | 37 -
.../descriptor/impl/DescriptorCollectorImpl.java | 249 ---
.../descriptor/impl/DescriptorDownloaderImpl.java | 283 ----
.../descriptor/impl/DescriptorFileImpl.java | 78 -
.../torproject/descriptor/impl/DescriptorImpl.java | 337 ----
.../descriptor/impl/DescriptorParseException.java | 15 -
.../descriptor/impl/DescriptorParserImpl.java | 28 -
.../descriptor/impl/DescriptorReaderImpl.java | 364 ----
.../descriptor/impl/DescriptorRequestImpl.java | 114 --
.../descriptor/impl/DirSourceEntryImpl.java | 218 ---
.../descriptor/impl/DirectoryDownloader.java | 104 --
.../impl/DirectoryKeyCertificateImpl.java | 308 ----
.../descriptor/impl/DirectorySignatureImpl.java | 115 --
.../descriptor/impl/DownloadCoordinator.java | 10 -
.../descriptor/impl/DownloadCoordinatorImpl.java | 298 ----
.../descriptor/impl/ExitListEntryImpl.java | 216 ---
.../torproject/descriptor/impl/ExitListImpl.java | 142 --
.../descriptor/impl/ExtraInfoDescriptorImpl.java | 1284 ---------------
.../descriptor/impl/MicrodescriptorImpl.java | 328 ----
.../descriptor/impl/NetworkStatusEntryImpl.java | 382 -----
.../descriptor/impl/NetworkStatusImpl.java | 270 ---
.../torproject/descriptor/impl/ParseHelper.java | 567 -------
.../descriptor/impl/RelayDirectoryImpl.java | 547 ------
.../impl/RelayExtraInfoDescriptorImpl.java | 37 -
.../impl/RelayNetworkStatusConsensusImpl.java | 414 -----
.../descriptor/impl/RelayNetworkStatusImpl.java | 384 -----
.../impl/RelayNetworkStatusVoteImpl.java | 761 ---------
.../descriptor/impl/RelayServerDescriptorImpl.java | 37 -
.../descriptor/impl/RouterStatusEntryImpl.java | 41 -
.../descriptor/impl/ServerDescriptorImpl.java | 985 -----------
.../descriptor/impl/TorperfResultImpl.java | 546 ------
src/org/torproject/descriptor/package-info.java | 80 -
.../descriptor/benchmark/MeasurePerformance.java | 278 ++++
.../descriptor/impl/BridgeNetworkStatusTest.java | 151 ++
.../descriptor/impl/ConsensusBuilder.java | 321 ++++
.../impl/DescriptorCollectorImplTest.java | 134 ++
.../descriptor/impl/ExitListImplTest.java | 131 ++
.../impl/ExtraInfoDescriptorImplTest.java | 1737 ++++++++++++++++++++
.../descriptor/impl/MicrodescriptorImplTest.java | 82 +
.../impl/RelayNetworkStatusConsensusImplTest.java | 1272 ++++++++++++++
.../impl/RelayNetworkStatusVoteImplTest.java | 1373 ++++++++++++++++
.../descriptor/impl/ServerDescriptorImplTest.java | 1605 ++++++++++++++++++
.../descriptor/impl/TorperfResultImplTest.java | 97 ++
.../descriptor/benchmark/MeasurePerformance.java | 278 ----
.../descriptor/impl/BridgeNetworkStatusTest.java | 151 --
.../descriptor/impl/ConsensusBuilder.java | 321 ----
.../impl/DescriptorCollectorImplTest.java | 134 --
.../descriptor/impl/ExitListImplTest.java | 131 --
.../impl/ExtraInfoDescriptorImplTest.java | 1737 --------------------
.../descriptor/impl/MicrodescriptorImplTest.java | 82 -
.../impl/RelayNetworkStatusConsensusImplTest.java | 1272 --------------
.../impl/RelayNetworkStatusVoteImplTest.java | 1373 ----------------
.../descriptor/impl/ServerDescriptorImplTest.java | 1605 ------------------
.../descriptor/impl/TorperfResultImplTest.java | 97 --
161 files changed, 21515 insertions(+), 21515 deletions(-)
diff --git a/build.xml b/build.xml
index 6bb773b..0d6cf7d 100644
--- a/build.xml
+++ b/build.xml
@@ -1,10 +1,10 @@
<project default="jar" name="descriptor" basedir=".">
<property name="release.version" value="1.2.0-dev" />
- <property name="sources" value="src"/>
+ <property name="sources" value="src/main/java"/>
<property name="resources" value="resources"/>
<property name="classes" value="classes"/>
<property name="docs" value="javadoc"/>
- <property name="tests" value="test"/>
+ <property name="tests" value="src/test/java"/>
<property name="libs" value="lib"/>
<property name="jarfile" value="descriptor-${release.version}.jar" />
<property name="jarsourcesfile"
@@ -152,11 +152,11 @@
<include name="*.md" />
</tarfileset>
<tarfileset dir="${sources}"
- prefix="descriptor-${release.version}/src" />
+ prefix="descriptor-${release.version}/${sources}" />
<tarfileset dir="${tests}"
- prefix="descriptor-${release.version}/test" />
+ prefix="descriptor-${release.version}/${tests}" />
<tarfileset dir="${libs}"
- prefix="descriptor-${release.version}/lib" />
+ prefix="descriptor-${release.version}/${libs}" />
</tar>
</target>
</project>
diff --git a/src/main/java/org/torproject/descriptor/BandwidthHistory.java b/src/main/java/org/torproject/descriptor/BandwidthHistory.java
new file mode 100644
index 0000000..0be1a53
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BandwidthHistory.java
@@ -0,0 +1,52 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains the bandwidth history of a relay or bridge.
+ *
+ * <p>A bandwidth history is not a descriptor type of its own but usually
+ * part of extra-info descriptors ({@link ExtraInfoDescriptor}) or server
+ * descriptors ({@link ServerDescriptor}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface BandwidthHistory {
+
+ /**
+ * Return the original bandwidth history line as contained in the
+ * descriptor, possibly prefixed with {@code "opt "}.
+ *
+ * @since 1.0.0
+ */
+ public String getLine();
+
+ /**
+ * Return the time in milliseconds since the epoch when the most recent
+ * interval ends.
+ *
+ * @since 1.0.0
+ */
+ public long getHistoryEndMillis();
+
+ /**
+ * Return the interval length in seconds.
+ *
+ * @since 1.0.0
+ */
+ public long getIntervalLength();
+
+ /**
+ * Return the (possibly empty) bandwidth history with map keys being
+ * times in milliseconds since the epoch when intervals end and map
+ * values being number of bytes used in the interval, ordered from
+ * oldest to newest interval.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<Long, Long> getBandwidthValues();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
new file mode 100644
index 0000000..a3c168d
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
@@ -0,0 +1,25 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a sanitized bridge extra-info descriptor.
+ *
+ * <p>Sanitized bridge extra-info descriptors share many contents with
+ * relay extra-info descriptors ({@link RelayExtraInfoDescriptor}), which
+ * is why they share a common
+ * superinterface ({@link ExtraInfoDescriptor}). The main purpose of
+ * having two subinterfaces is being able to distinguish descriptor types
+ * more easily.</p>
+ *
+ * <p>Details about sanitizing bridge extra-info descriptors can be found
+ * <a href="https://collector.torproject.org/#type-bridge-extra-info">here</a>.
+ * </p>
+ *
+ * @since 1.1.0
+ */
+public interface BridgeExtraInfoDescriptor extends ExtraInfoDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
new file mode 100644
index 0000000..c7458fd
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
@@ -0,0 +1,128 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains a sanitized bridge network status document.
+ *
+ * <p>The bridge directory authority periodically publishes a network
+ * status document with one entry per known bridge in the network
+ * ({@link NetworkStatusEntry}) containing: a hash of its identity key, a
+ * hash of its most recent server descriptor, and a summary of what the
+ * bridge authority believed about its status.</p>
+ *
+ * <p>The main purpose of this document is to get an authoritative list of
+ * running bridges to the bridge distribution service BridgeDB.</p>
+ *
+ * <p>Details about sanitizing bridge network statuses can be found
+ * <a href="https://collector.torproject.org/#type-bridge-network-status">here</a>.
+ * </p>
+ *
+ * @since 1.0.0
+ */
+public interface BridgeNetworkStatus extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the minimum uptime in seconds that this authority requires
+ * for assigning the Stable flag, or -1 if the authority doesn't report
+ * this value.
+ *
+ * @since 1.1.0
+ */
+ public long getStableUptime();
+
+ /**
+ * Return the minimum MTBF (mean time between failure) that this
+ * authority requires for assigning the Stable flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getStableMtbf();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Fast flag, or -1 if the authority doesn't report this
+ * value.
+ *
+ * @since 1.1.0
+ */
+ public long getFastBandwidth();
+
+ /**
+ * Return the minimum WFU (weighted fractional uptime) in percent that
+ * this authority requires for assigning the Guard flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public double getGuardWfu();
+
+ /**
+ * Return the minimum weighted time in seconds that this authority
+ * needs to know about a relay before assigning the Guard flag, or -1 if
+ * the authority doesn't report this information.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardTk();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardBandwidthIncludingExits();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can not be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.1.0
+ */
+ public long getGuardBandwidthExcludingExits();
+
+ /**
+ * Return 1 if the authority has measured enough MTBF info to use the
+ * MTBF requirement instead of the uptime requirement for assigning the
+ * Stable flag, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getEnoughMtbfInfo();
+
+ /**
+ * Return 1 if the authority has enough measured bandwidths that it'll
+ * ignore the advertised bandwidth claims of routers without measured
+ * bandwidth, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getIgnoringAdvertisedBws();
+
+ /**
+ * Return status entries for each contained bridge, with map keys being
+ * SHA-1 digests of SHA-1 digest of the bridges' public identity keys,
+ * encoded as 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java b/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java
new file mode 100644
index 0000000..2de4ee9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgePoolAssignment.java
@@ -0,0 +1,47 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.SortedMap;
+
+/**
+ * Contains a sanitized list of bridges together with the distribution
+ * pools they have been assigned to by the bridge distribution service
+ * BridgeDB.
+ *
+ * <p>BridgeDB receives bridge network statuses
+ * ({@link BridgeNetworkStatus}) from the bridge authority, assigns these
+ * bridges to persistent distribution rings, and hands them out to bridge
+ * users. BridgeDB periodically dumps the list of running bridges with
+ * information about the rings, subrings, and file buckets to which they
+ * are assigned to a local file.</p>
+ *
+ * <p>Details about sanitizing bridge pool assignments can be found
+ * <a href="https://collector.torproject.org/#type-bridge-pool-assignment">here</a>.
+ * </p>
+ *
+ * @since 1.0.0
+ */
+public interface BridgePoolAssignment extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the entries contained in this bridge pool assignment list
+ * with map keys being SHA-1 digests of SHA-1 digest of the bridges'
+ * public identity keys, encoded as 40 upper-case hexadecimal
+ * characters, and map values being assignment strings, e.g.
+ * {@code "https ring=3 flag=stable"}.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, String> getEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java b/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java
new file mode 100644
index 0000000..7d4503f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/BridgeServerDescriptor.java
@@ -0,0 +1,24 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a sanitized bridge server descriptor.
+ *
+ * <p>Sanitized bridge server descriptors share many contents with relay
+ * server descriptors ({@link RelayServerDescriptor}), which is why they
+ * share a common superinterface ({@link ServerDescriptor}). The main
+ * purpose of having two subinterfaces is being able to distinguish
+ * descriptor types more easily.</p>
+ *
+ * <p>Details about sanitizing bridge server descriptors can be found
+ * <a href="https://collector.torproject.org/#type-bridge-server-descriptor">here</a>.
+ * </p>
+ *
+ * @since 1.1.0
+ */
+public interface BridgeServerDescriptor extends ServerDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/Descriptor.java b/src/main/java/org/torproject/descriptor/Descriptor.java
new file mode 100644
index 0000000..7cad109
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/Descriptor.java
@@ -0,0 +1,39 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Superinterface for any descriptor with access to generic information
+ * about the descriptor.
+ *
+ * @since 1.0.0
+ */
+public interface Descriptor {
+
+ /**
+ * Return the raw descriptor bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getRawDescriptorBytes();
+
+ /**
+ * Return the (possibly empty) list of annotations in the format
+ * {@code "@key( value)*"}.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getAnnotations();
+
+ /**
+ * Return any unrecognized lines when parsing this descriptor, or an
+ * empty list if there were no unrecognized lines.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getUnrecognizedLines();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorCollector.java b/src/main/java/org/torproject/descriptor/DescriptorCollector.java
new file mode 100644
index 0000000..b1027dc
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorCollector.java
@@ -0,0 +1,62 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+
+/**
+ * Descriptor source that synchronizes descriptors from the CollecTor
+ * service to a given local directory.
+ *
+ * <p>This type is not a descriptor source in the proper sense, because it
+ * does not produce descriptors by itself. But it often creates the
+ * prerequisites for reading descriptors from disk using
+ * {@link DescriptorReader}.</p>
+ *
+ * <p>Code sample:</p>
+ * <pre>{@code
+ * DescriptorCollector descriptorCollector =
+ * DescriptorSourceFactory.createDescriptorCollector();
+ * descriptorCollector.collectDescriptors(
+ * // Download from Tor's main CollecTor instance,
+ * "https://collector.torproject.org",
+ * // include network status consensuses and relay server descriptors
+ * new String[] { "/recent/relay-descriptors/consensuses/",
+ * "/recent/relay-descriptors/server-descriptors/" },
+ * // regardless of last-modified time,
+ * 0L,
+ * // write to the local directory called in/,
+ * new File("in"),
+ * // and delete extraneous files that do not exist remotely anymore.
+ * true);
+ * }</pre>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorCollector {
+
+ /**
+ * Fetch remote files from a CollecTor instance that do not yet exist
+ * locally and possibly delete local files that do not exist remotely
+ * anymore.
+ *
+ * @param collecTorBaseUrl CollecTor base URL without trailing slash,
+ * e.g., {@code "https://collector.torproject.org"}
+ * @param remoteDirectories Remote directories to collect descriptors
+ * from, e.g.,
+ * {@code "/recent/relay-descriptors/server-descriptors/"}, without
+ * processing subdirectories unless they are explicitly listed
+ * @param minLastModified Minimum last-modified time in milliseconds of
+ * files to be collected, or 0 for collecting all files
+ * @param localDirectory Directory where collected files will be written
+ * @param deleteExtraneousLocalFiles Whether to delete all local files
+ * that do not exist remotely anymore
+ *
+ * @since 1.0.0
+ */
+ public void collectDescriptors(String collecTorBaseUrl,
+ String[] remoteDirectories, long minLastModified,
+ File localDirectory, boolean deleteExtraneousLocalFiles);
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorDownloader.java b/src/main/java/org/torproject/descriptor/DescriptorDownloader.java
new file mode 100644
index 0000000..f0b1101
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorDownloader.java
@@ -0,0 +1,198 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Descriptor source that downloads relay descriptors from directory
+ * authorities or mirrors.
+ *
+ * <p>Downloading descriptors is done in a batch which starts after
+ * setting any configuration options and initiating the download
+ * process.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorDownloader {
+
+ /**
+ * Add a directory authority to download descriptors from, which is
+ * only required for downloading network status votes and will be used
+ * when no directory mirrors are available.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectoryAuthority(String nickname, String ip,
+ int dirPort);
+
+ /**
+ * Add a directory mirror to download descriptors from, which is
+ * preferred for downloading descriptors, except for network status
+ * votes which are only available on directory authorities.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectoryMirror(String nickname, String ip, int dirPort);
+
+ /**
+ * Include the current network status consensus in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentConsensus();
+
+ /**
+ * Include the current network status consensus in the downloads, and
+ * attempt to download it from all directory authorities.
+ *
+ * <p>The primary purpose of doing this is to compare different
+ * consensuses and download characteristics to each other. Typically,
+ * downloading from a single directory mirror or authority is
+ * sufficient.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentConsensusFromAllDirectoryAuthorities();
+
+ /**
+ * Include the current network status votes referenced from a
+ * previously downloaded consensus in the downloads, which requires
+ * downloading the current consensus from at least one directory mirror
+ * or authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentReferencedVotes();
+
+ /**
+ * Include the current network status vote published by the given
+ * directory authority in the downloads, which requires downloading from
+ * at least one directory authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentVote(String fingerprint);
+
+ /**
+ * Include the current network status votes published by the given
+ * directory authorities in the downloads, which requires downloading
+ * from at least one directory authority.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeCurrentVotes(Set<String> fingerprints);
+
+ /**
+ * Include all server descriptors referenced from a previously
+ * downloaded network status consensus in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeReferencedServerDescriptors();
+
+ /**
+ * Exclude the server descriptor with the given identifier from the
+ * downloads even if it's referenced from a consensus and we're supposed
+ * to download all referenced server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeServerDescriptor(String identifier);
+
+ /**
+ * Exclude the server descriptors with the given identifiers from the
+ * downloads even if they are referenced from a consensus and we're
+ * supposed to download all referenced server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeServerDescriptors(Set<String> identifier);
+
+ /**
+ * Include all extra-info descriptors referenced from previously
+ * downloaded server descriptors in the downloads.
+ *
+ * @since 1.0.0
+ */
+ public void setIncludeReferencedExtraInfoDescriptors();
+
+ /**
+ * Exclude the extra-info descriptor with the given identifier from the
+ * downloads even if it's referenced from a server descriptor and we're
+ * supposed to download all referenced extra-info descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeExtraInfoDescriptor(String identifier);
+
+ /**
+ * Exclude the extra-info descriptors with the given identifiers from
+ * the downloads even if they are referenced from server descriptors
+ * and we're supposed to download all referenced extra-info
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeExtraInfoDescriptors(Set<String> identifiers);
+
+ /**
+ * Define a connect timeout for a single request.
+ *
+ * <p>If a timeout expires, no further requests will be sent to the
+ * directory authority or mirror. Setting this value to 0 disables the
+ * connect timeout. Default value is 1 minute (60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setConnectTimeout(long connectTimeoutMillis);
+
+ /**
+ * Define a read timeout for a single request.
+ *
+ * <p>If a timeout expires, no further requests will be sent to the
+ * directory authority or mirror. Setting this value to 0 disables the
+ * read timeout. Default value is 1 minute (60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setReadTimeout(long readTimeoutMillis);
+
+ /**
+ * Define a global timeout for all requests.
+ *
+ * <p>Once this timeout expires, all running requests are aborted and no
+ * further requests are made. Setting this value to 0 disables the
+ * global timeout. Default is 1 hour (60 * 60 * 1000).</p>
+ *
+ * @since 1.0.0
+ */
+ public void setGlobalTimeout(long globalTimeoutMillis);
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines();
+
+ /**
+ * Download the previously configured relay descriptors and make them
+ * available via the returned blocking iterator.
+ *
+ * <p>Whenever the downloader runs out of descriptors and expects to
+ * provide more shortly after, it blocks the caller. This method can
+ * only be run once.</p>
+ *
+ * @since 1.0.0
+ */
+ public Iterator<DescriptorRequest> downloadDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorFile.java b/src/main/java/org/torproject/descriptor/DescriptorFile.java
new file mode 100644
index 0000000..417d7f9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorFile.java
@@ -0,0 +1,77 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Container for descriptors read from a file.
+ *
+ * <p>When the {@link DescriptorReader} reads descriptors from local files
+ * it provides an iterator over these containers which in turn contain
+ * references to classes implementing the {@link Descriptor} interface.
+ * This container also stores potentially useful meta-data about the
+ * descriptor file.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorFile {
+
+ /**
+ * Return the directory where this descriptor file was contained, or
+ * null if the file was contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getDirectory();
+
+ /**
+ * Return the tarball where this descriptor file was contained, or null
+ * if the file was not contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getTarball();
+
+ /**
+ * Return the descriptor file itself, or null if the descriptor file
+ * was contained in a tarball.
+ *
+ * @since 1.0.0
+ */
+ public File getFile();
+
+ /**
+ * Return the descriptor file name, which is either the absolute path
+ * of the file on disk, or the tar file entry name.
+ *
+ * @since 1.0.0
+ */
+ public String getFileName();
+
+ /**
+ * Return the time in milliseconds since the epoch when the descriptor
+ * file on disk was last modified.
+ *
+ * @since 1.0.0
+ */
+ public long getLastModified();
+
+ /**
+ * Return the descriptors contained in the descriptor file.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> getDescriptors();
+
+ /**
+ * Return the first exception that was thrown when reading this file or
+ * parsing its content, or null if no exception was thrown.
+ *
+ * @since 1.0.0
+ */
+ public Exception getException();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
new file mode 100644
index 0000000..309d3f7
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorParseException.java
@@ -0,0 +1,20 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Thrown if raw descriptor contents cannot be parsed to one or more
+ * {@link Descriptor} instances, according to descriptor specifications.
+ *
+ * @since 1.0.0
+ */
+@SuppressWarnings("deprecation")
+public class DescriptorParseException
+ extends org.torproject.descriptor.impl.DescriptorParseException {
+ private static final long serialVersionUID = 100L;
+ public DescriptorParseException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorParser.java b/src/main/java/org/torproject/descriptor/DescriptorParser.java
new file mode 100644
index 0000000..680b8b2
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorParser.java
@@ -0,0 +1,47 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Descriptor source that parses descriptors from raw descriptor contents.
+ *
+ * <p>Unlike most of the other descriptor sources this descriptor source
+ * does not operate in a batch-processing mode. It takes the raw
+ * descriptor contents of one or more descriptors, parses them, and
+ * returns a list of descriptors.</p>
+ *
+ * <p>This descriptor source is internally used by other descriptor
+ * sources but can also be used directly by applications that obtain
+ * raw descriptor contents via other means than one of the existing
+ * descriptor sources.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorParser {
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines);
+
+ /**
+ * Parse descriptors in the given byte array, possibly parsing the
+ * publication time from the file name, depending on the descriptor
+ * type.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
+ String fileName) throws DescriptorParseException;
+}
diff --git a/src/main/java/org/torproject/descriptor/DescriptorReader.java b/src/main/java/org/torproject/descriptor/DescriptorReader.java
new file mode 100644
index 0000000..771755e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorReader.java
@@ -0,0 +1,143 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.SortedMap;
+
+/**
+ * Descriptor source that reads descriptors from local files and provides
+ * an iterator over parsed descriptors.
+ *
+ * <p>This descriptor source is likely the most widely used one, possibly
+ * in combination with {@link DescriptorCollector} to synchronize
+ * descriptors from the CollecTor service.</p>
+ *
+ * <p>Reading descriptors is done in a batch which starts after setting
+ * any configuration options and initiating the read process.</p>
+ *
+ * <p>Code sample:</p>
+ * <pre>{@code
+ * DescriptorReader descriptorReader =
+ * DescriptorSourceFactory.createDescriptorReader();
+ * // Read descriptors from local directory called in/.
+ * descriptorReader.addDirectory(new File("in"));
+ * Iterator<DescriptorFile> descriptorFiles =
+ * descriptorReader.readDescriptors();
+ * while (descriptorFiles.hasNext()) {
+ * DescriptorFile descriptorFile = descriptorFiles.next();
+ * for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ * if ((descriptor instanceof RelayNetworkStatusConsensus)) {
+ * // Only process network status consensuses, ignore the rest.
+ * RelayNetworkStatusConsensus consensus =
+ * (RelayNetworkStatusConsensus) descriptor;
+ * processConsensus(consensus);
+ * }
+ * }
+ * }}</pre>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorReader {
+
+ /**
+ * Add a local directory to read descriptors from, which may contain
+ * descriptor files or tarballs containing descriptor files.
+ *
+ * @since 1.0.0
+ */
+ public void addDirectory(File directory);
+
+ /**
+ * Add a tarball to read descriptors from, which may be uncompressed,
+ * bz2-compressed, or xz-compressed.
+ *
+ * @since 1.0.0
+ */
+ public void addTarball(File tarball);
+
+ /**
+ * Exclude files that are listed in the given history file and that
+ * haven't changed since they have last been read.
+ *
+ * <p>Add a new line for each descriptor that is read in this execution
+ * and remove lines for files that don't exist anymore.</p>
+ *
+ * <p>Lines in the history file contain the last modified time in
+ * milliseconds since the epoch and the absolute path of a file.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setExcludeFiles(File historyFile);
+
+ /**
+ * Exclude files if they haven't changed since the corresponding last
+ * modified timestamps.
+ *
+ * <p>Can be used instead of (or in addition to) a history file.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setExcludedFiles(SortedMap<String, Long> excludedFiles);
+
+ /**
+ * Return files and last modified timestamps of files that exist in the
+ * input directory or directories, but that have been excluded from
+ * parsing, because they haven't changed since they were last read.
+ *
+ * <p>Can be used instead of (or in addition to) a history file when
+ * combined with the set of parsed files.</p>
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExcludedFiles();
+
+ /**
+ * Return files and last modified timestamps of files that exist in the
+ * input directory or directories and that have been parsed.
+ *
+ * <p>Can be used instead of (or in addition to) a history file when
+ * combined with the set of excluded files.</p>
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getParsedFiles();
+
+ /**
+ * Fail descriptor parsing when encountering an unrecognized line.
+ *
+ * <p>This option is not set by default, because the Tor specifications
+ * allow for new lines to be added that shall be ignored by older Tor
+ * versions. But some applications may want to handle unrecognized
+ * descriptor lines explicitly.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setFailUnrecognizedDescriptorLines();
+
+ /**
+ * Don't keep more than this number of parsed descriptor files in the
+ * queue.
+ *
+ * <p>The default is 100, but if descriptor files contain hundreds or
+ * even thousands of descriptors, that default may be too high.</p>
+ *
+ * @since 1.0.0
+ */
+ public void setMaxDescriptorFilesInQueue(int max);
+
+ /**
+ * Read the previously configured descriptors and make them available
+ * via the returned blocking iterator.
+ *
+ * <p>Whenever the reader runs out of descriptors and expects to provide
+ * more shortly after, it blocks the caller. This method can only be
+ * run once.</p>
+ *
+ * @since 1.0.0
+ */
+ public Iterator<DescriptorFile> readDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorRequest.java b/src/main/java/org/torproject/descriptor/DescriptorRequest.java
new file mode 100644
index 0000000..c36c0c0
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorRequest.java
@@ -0,0 +1,100 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Container for descriptors downloaded from a directory authority or
+ * mirror.
+ *
+ * <p>When the {@link DescriptorDownloader} downloads descriptors from
+ * directory authorities or mirrors it provides an iterator over these
+ * containers which in turn contain references to classes implementing the
+ * {@link Descriptor} interface. This container also stores potentially
+ * useful meta-data about the descriptor request.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DescriptorRequest {
+
+ /**
+ * Return the request URL that was used in this request.
+ *
+ * @since 1.0.0
+ */
+ public String getRequestUrl();
+
+ /**
+ * Return the nickname of the directory mirror or authority as
+ * previously configured.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectoryNickname();
+
+ /**
+ * Return the first exception that was thrown when making this request
+ * or parsing the response, or null if no exception was thrown.
+ *
+ * @since 1.0.0
+ */
+ public Exception getException();
+
+ /**
+ * Return the response code that the directory mirror or authority
+ * returned.
+ *
+ * @since 1.0.0
+ */
+ public int getResponseCode();
+
+ /**
+ * Return the time in milliseconds since the epoch when this request
+ * was started.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestStart();
+
+ /**
+ * Return the time in milliseconds since the epoch when this request
+ * ended.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestEnd();
+
+ /**
+ * Return whether this request ended, because the connect timeout has
+ * expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean connectTimeoutHasExpired();
+
+ /**
+ * Return whether this request ended, because the read timeout has
+ * expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean readTimeoutHasExpired();
+
+ /**
+ * Return whether this request ended, because the global timeout for
+ * all requests has expired.
+ *
+ * @since 1.0.0
+ */
+ public boolean globalTimeoutHasExpired();
+
+ /**
+ * Return the descriptors contained in the reply.
+ *
+ * @since 1.0.0
+ */
+ public List<Descriptor> getDescriptors();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
new file mode 100644
index 0000000..af13f39
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DescriptorSourceFactory.java
@@ -0,0 +1,187 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Factory for descriptor sources which in turn produce descriptors.
+ *
+ * <p>Descriptor sources are the only producers of classes implementing
+ * the {@link Descriptor} superinterface. There exist descriptor sources
+ * for obtaining remote descriptor data ({@link DescriptorDownloader} and
+ * {@link DescriptorCollector}) and descriptor sources for processing
+ * local descriptor data ({@link DescriptorReader} and
+ * {@link DescriptorParser}).</p>
+ *
+ * <p>By default, this factory returns implementations from the library's
+ * own impl package. This may be overridden by setting Java properties,
+ * though most users will simply use the default implementations.</p>
+ *
+ * <p>These properties can be used for setting the implementation:</p>
+ * <ul>
+ * <li>{@code descriptor.collector}</li>
+ * <li>{@code descriptor.downloader}</li>
+ * <li>{@code descriptor.parser}</li>
+ * <li>{@code descriptor.reader}</li>
+ * </ul>
+ *
+ * <p>Assuming the classpath contains the special implementation
+ * referenced, your application classes as well as a descriptor API jar
+ * the following is an example for using a different implementation of the
+ * descriptor downloader:</p>
+ *
+ * <p><code>
+ * java -Ddescriptor.downloader=my.special.descriptorimpl.Downloader my.app.Mainclass
+ * </code></p>
+ *
+ * @since 1.0.0
+ */
+public final class DescriptorSourceFactory {
+
+ /**
+ * Default implementation of the {@link DescriptorDownloader}
+ * descriptor source.
+ *
+ * @since 1.0.0
+ */
+ public final static String DOWNLOADER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorDownloaderImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorParser} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String PARSER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorParserImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorReader} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String READER_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorReaderImpl";
+
+ /**
+ * Default implementation of the {@link DescriptorCollector} descriptor
+ * source.
+ *
+ * @since 1.0.0
+ */
+ public final static String COLLECTOR_DEFAULT =
+ "org.torproject.descriptor.impl.DescriptorCollectorImpl";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorParser} descriptor source, which is by default set
+ * to the class in {@link #PARSER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String PARSER_PROPERTY = "descriptor.parser";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorReader} descriptor source, which is by default set
+ * to the class in {@link #READER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String READER_PROPERTY = "descriptor.reader";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorDownloader} descriptor source, which is by default
+ * set to the class in {@link #DOWNLOADER_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String DOWNLOADER_PROPERTY =
+ "descriptor.downloader";
+
+ /**
+ * Property name for overriding the implementation of the
+ * {@link DescriptorCollector} descriptor source, which is by default
+ * set to the class in {@link #COLLECTOR_DEFAULT}.
+ *
+ * @since 1.0.0
+ */
+ public final static String COLLECTOR_PROPERTY = "descriptor.collector";
+
+ /**
+ * Create a new {@link DescriptorParser} by instantiating the class in
+ * {@link #PARSER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorParser createDescriptorParser() {
+ return (DescriptorParser) retrieve(PARSER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorReader} by instantiating the class in
+ * {@link #READER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorReader createDescriptorReader() {
+ return (DescriptorReader) retrieve(READER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorDownloader} by instantiating the class
+ * in {@link #DOWNLOADER_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorDownloader createDescriptorDownloader() {
+ return (DescriptorDownloader) retrieve(DOWNLOADER_PROPERTY);
+ }
+
+ /**
+ * Create a new {@link DescriptorCollector} by instantiating the class
+ * in {@link #COLLECTOR_PROPERTY}.
+ *
+ * @since 1.0.0
+ */
+ public final static DescriptorCollector createDescriptorCollector() {
+ return (DescriptorCollector) retrieve(COLLECTOR_PROPERTY);
+ }
+
+ private final static <T> Object retrieve(String type) {
+ Object object;
+ String clazzName = null;
+ try {
+ switch (type) {
+ case PARSER_PROPERTY:
+ clazzName = System.getProperty(type, PARSER_DEFAULT);
+ break;
+ case DOWNLOADER_PROPERTY:
+ clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
+ break;
+ case READER_PROPERTY:
+ clazzName = System.getProperty(type, READER_DEFAULT);
+ break;
+ case COLLECTOR_PROPERTY:
+ clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
+ break;
+ }
+ object = ClassLoader.getSystemClassLoader().loadClass(clazzName).
+ newInstance();
+ } catch (ClassNotFoundException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ } catch (InstantiationException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ } catch (IllegalAccessException ex) {
+ throw new ImplementationNotAccessibleException("Cannot load class "
+ + clazzName + "for type " + type, ex);
+ }
+ return object;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirSourceEntry.java b/src/main/java/org/torproject/descriptor/DirSourceEntry.java
new file mode 100644
index 0000000..96d81ee
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirSourceEntry.java
@@ -0,0 +1,96 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains details about an authority and its vote that contributed to a
+ * consensus.
+ *
+ * <p>A directory source entry is not a descriptor type of its own but is
+ * part of a network status consensus
+ * ({@link RelayNetworkStatusConsensus}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirSourceEntry {
+
+ /**
+ * Return the raw directory source entry bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getDirSourceEntryBytes();
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.2.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getIp();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return the TCP port where this authority accepts TLS connections for
+ * the main OR protocol.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return whether this directory source entry was created using a
+ * legacy key.
+ *
+ * @since 1.0.0
+ */
+ public boolean isLegacy();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the SHA-1 vote digest, encoded as 40 lower-case hexadecimal
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getVoteDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java b/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java
new file mode 100644
index 0000000..07211ef
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirectoryKeyCertificate.java
@@ -0,0 +1,109 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a key certificate in the version 3 directory protocol.
+ *
+ * <p>Every directory authority in the version 3 directory protocol uses
+ * two keys: a medium-term signing key, and a long-term authority identity
+ * key. (Authorities also have a relay identity key used in their role as
+ * a relay and by earlier versions of the directory protocol.) The
+ * identity key is used from time to time to sign new key certificates
+ * containing signing keys. The contained signing key is used to sign key
+ * certificates and status documents.</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirectoryKeyCertificate extends Descriptor {
+
+ /**
+ * Return the version of this descriptor, which must be 3 or higher.
+ *
+ * @since 1.0.0
+ */
+ public int getDirKeyCertificateVersion();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the certificate does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or -1 if the certificate does not contain a port.
+ *
+ * @since 1.0.0
+ */
+ public int getPort();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the authority's identity key in PEM format.
+ *
+ * @since 1.0.0
+ */
+ public String getDirIdentityKey();
+
+ /**
+ * Return the time in milliseconds since the epoch when the authority's
+ * signing key and this key certificate were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch after which the
+ * authority's signing key is no longer valid.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyExpiresMillis();
+
+ /**
+ * Return the authority's signing key in PEM format.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return the signature of the authority's identity key made using the
+ * authority's signing key, or null if the certificate does not contain
+ * such a signature.
+ *
+ * @since 1.0.0
+ */
+ public String getDirKeyCrosscert();
+
+ /**
+ * Return the certificate signature from the initial item
+ * "dir-key-certificate-version" until the final item
+ * "dir-key-certification", signed with the authority identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirKeyCertification();
+
+ /**
+ * Return the SHA-1 certificate digest, encoded as 40 lower-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getCertificateDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/DirectorySignature.java b/src/main/java/org/torproject/descriptor/DirectorySignature.java
new file mode 100644
index 0000000..8877a4e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/DirectorySignature.java
@@ -0,0 +1,52 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains the signature of a network status consensus or vote.
+ *
+ * <p>A directory signature is not a descriptor type of its own but is
+ * part of a network status consensus
+ * ({@link RelayNetworkStatusConsensus}) or vote
+ * ({@link RelayNetworkStatusVote}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface DirectorySignature {
+
+ /**
+ * Return the digest algorithm, which is "sha1" by default and which
+ * can be "sha256" or another digest algorithm.
+ *
+ * @since 1.0.0
+ */
+ public String getAlgorithm();
+
+ /**
+ * Return the SHA-1 digest of the authority's long-term identity key in
+ * the version 3 directory protocol, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the SHA-1 digest of the authority's medium-term signing key
+ * in the version 3 directory protocol, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKeyDigest();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key in the version 3 directory protocol.
+ *
+ * @since 1.0.0
+ */
+ public String getSignature();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExitList.java b/src/main/java/org/torproject/descriptor/ExitList.java
new file mode 100644
index 0000000..2a5cb2e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExitList.java
@@ -0,0 +1,92 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Contains an exit list containing the IP addresses of relays that the
+ * exit list service TorDNSEL found when exiting through them.
+ *
+ * @since 1.0.0
+ */
+public interface ExitList extends Descriptor {
+
+ /**
+ * End-of-line character expected in exit lists.
+ *
+ * @since 1.0.0
+ */
+ public final static String EOL = "\n";
+
+ /**
+ * Exit list entry containing results from a single exit scan.
+ *
+ * @since 1.1.0
+ */
+ public interface Entry {
+
+ /**
+ * Return the scanned relay's fingerprint, which is a SHA-1 digest of
+ * the relays's public identity key, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.1.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when the scanned
+ * relay's last known descriptor was published.
+ *
+ * @since 1.1.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the network
+ * status that this scan was based on was published.
+ *
+ * @since 1.1.0
+ */
+ public long getLastStatusMillis();
+
+ /**
+ * Return the IP addresses that were determined in the scan with map
+ * keys being IPv4 addresses in dotted-quad format and map values
+ * being scan times in milliseconds since the epoch.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Long> getExitAddresses();
+ }
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was downloaded.
+ *
+ * @since 1.0.0
+ */
+ public long getDownloadedMillis();
+
+ /**
+ * Return the unordered set of exit scan results.
+ *
+ * @since 1.0.0
+ * @deprecated The {@link ExitListEntry} type has been deprecated and
+ * superseded by {@link ExitList.Entry} which is returned by
+ * {@link #getEntries()}.
+ */
+ @Deprecated
+ public Set<ExitListEntry> getExitListEntries();
+
+ /**
+ * Return the unordered set of exit scan results.
+ *
+ * @since 1.1.0
+ */
+ public Set<ExitList.Entry> getEntries();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExitListEntry.java b/src/main/java/org/torproject/descriptor/ExitListEntry.java
new file mode 100644
index 0000000..2a3d79f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExitListEntry.java
@@ -0,0 +1,55 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Exit list entry containing results from a single exit scan.
+ *
+ * @since 1.0.0
+ * @deprecated Superseded by {@link ExitList.Entry}.
+ */
+@Deprecated
+public interface ExitListEntry extends ExitList.Entry {
+
+ /**
+ * Return the scanned relay's fingerprint, which is a SHA-1 digest of
+ * the relays's public identity key, encoded as 40 upper-case
+ * hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when the scanned
+ * relay's last known descriptor was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the network
+ * status that this scan was based on was published.
+ *
+ * @since 1.0.0
+ */
+ public long getLastStatusMillis();
+
+ /**
+ * Return the IPv4 address in dotted-quad format that was determined in
+ * the scan.
+ *
+ * @since 1.0.0
+ */
+ public String getExitAddress();
+
+ /**
+ * Return the scan time in milliseconds since the epoch.
+ *
+ * @since 1.0.0
+ */
+ public long getScanMillis();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
new file mode 100644
index 0000000..49efbf3
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
@@ -0,0 +1,646 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * Contains a relay or sanitized bridge extra-info descriptor.
+ *
+ * <p>Relays publish extra-info descriptors as an addendum to server
+ * descriptors ({@link ServerDescriptor}) to report extraneous information
+ * to the directory authorities that clients do not need to download in
+ * order to function. This information primarily consists of statistics
+ * gathered by the relay about its usage and can take up a lot of
+ * descriptor space. The separation of server descriptors and extra-info
+ * descriptors has become less relevant with the introduction of
+ * microdescriptors ({@link Microdescriptor}) that are derived from server
+ * descriptors by the directory authority and which clients download
+ * instead of server descriptors, but it persists.</p>
+ *
+ * <p>Bridges publish extra-info descriptors to the bridge authority for
+ * the same reason, to include statistics about their usage without
+ * increasing the directory protocol overhead for bridge clients. In this
+ * case, the separation of server descriptors and extra-info descriptors
+ * is slightly more relevant, because there are no microdescriptors for
+ * bridges, so that bridge clients still download server descriptors of
+ * bridges they're using. Another reason is that bridges need to include
+ * information like details of all the transports they support in their
+ * descriptors, and bridge clients using one such transport are not
+ * supposed to learn the details of the other transports.</p>
+ *
+ * <p>It's worth noting that all contents of extra-info descriptors are
+ * written and signed by relays and bridges without a third party
+ * verifying their correctness. The (bridge) directory authorities may
+ * decide to exclude dishonest servers from the network statuses they
+ * produce, but that wouldn't be reflected in extra-info descriptors.</p>
+ *
+ * @since 1.0.0
+ */
+public interface ExtraInfoDescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
+ * descriptors) or upper-case (bridge descriptors) hexadecimal
+ * characters, that is used to reference this descriptor from a server
+ * descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getExtraInfoDigest();
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that may be used to reference
+ * this descriptor from a server descriptor.
+ *
+ * @since 1.1.0
+ */
+ public String getExtraInfoDigestSha256();
+
+ /**
+ * Return the server's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the server's public identity key, encoded
+ * as 40 upper-case hexadecimal characters, that is typically used to
+ * uniquely identify the server.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * and the corresponding server descriptor were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the server's history of read bytes, or null if the descriptor
+ * does not contain a bandwidth history; older Tor versions included
+ * bandwidth histories in their server descriptors
+ * ({@link ServerDescriptor#getReadHistory()}).
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getReadHistory();
+
+ /**
+ * Return the server's history of written bytes, or null if the
+ * descriptor does not contain a bandwidth history; older Tor versions
+ * included bandwidth histories in their server descriptors
+ * ({@link ServerDescriptor#getWriteHistory()}).
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getWriteHistory();
+
+ /**
+ * Return a SHA-1 digest of the GeoIP database file used by this server
+ * to resolve client IP addresses to country codes, encoded as 40
+ * upper-case hexadecimal characters, or null if no GeoIP database
+ * digest is included.
+ *
+ * @since 1.0.0
+ */
+ public String getGeoipDbDigest();
+
+ /**
+ * Return a SHA-1 digest of the GeoIPv6 database file used by this
+ * server to resolve client IP addresses to country codes, encoded as 40
+ * upper-case hexadecimal characters, or null if no GeoIPv6 database
+ * digest is included.
+ *
+ * @since 1.0.0
+ */
+ public String getGeoip6DbDigest();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * directory request statistics interval ended, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getDirreqStatsEndMillis();
+
+ /**
+ * Return the interval length of the included directory request
+ * statistics in seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getDirreqStatsIntervalLength();
+
+ /**
+ * Return statistics on unique IP addresses requesting v2 network
+ * statuses with map keys being country codes and map values being
+ * numbers of unique IP addresses rounded up to the nearest multiple of
+ * 8, or null if no such statistics are included (which is the case with
+ * recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Ips();
+
+ /**
+ * Return statistics on unique IP addresses requesting v3 network
+ * status consensuses of any flavor with map keys being country codes
+ * and map values being numbers of unique IP addresses rounded up to the
+ * nearest multiple of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Ips();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses with
+ * map keys being country codes and map values being request numbers
+ * rounded up to the nearest multiple of 8, or null if no such
+ * statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Reqs();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor with map keys being country codes and map
+ * values being request numbers rounded up to the nearest multiple of 8,
+ * or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Reqs();
+
+ /**
+ * Return the share of requests for v2 network statuses that the server
+ * expects to receive from clients, or -1.0 if this share is not
+ * included (which is the case with recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public double getDirreqV2Share();
+
+ /**
+ * Return the share of requests for v3 network status consensuses of
+ * any flavor that the server expects to receive from clients, or -1.0
+ * if this share is not included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public double getDirreqV3Share();
+
+ /**
+ * Return statistics on responses to directory requests for v2 network
+ * statuses with map keys being response strings and map values being
+ * response numbers rounded up to the nearest multiple of 4, or null if
+ * no such statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2Resp();
+
+ /**
+ * Return statistics on responses to directory requests for v3 network
+ * status consensuses of any flavor with map keys being response strings
+ * and map values being response numbers rounded up to the nearest
+ * multiple of 4, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3Resp();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses to
+ * the server's directory port with map keys being statistic keys and
+ * map values being statistic values like counts or quantiles, or null
+ * if no such statistics are included (which is the case with recent Tor
+ * versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2DirectDl();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor to the server's directory port with map
+ * keys being statistic keys and map values being statistic values like
+ * counts or quantiles, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3DirectDl();
+
+ /**
+ * Return statistics on directory requests for v2 network statuses
+ * tunneled through a circuit with map keys being statistic keys and map
+ * values being statistic values, or null if no such statistics are
+ * included (which is the case with recent Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV2TunneledDl();
+
+ /**
+ * Return statistics on directory requests for v3 network status
+ * consensuses of any flavor tunneled through a circuit with map keys
+ * being statistic keys and map values being statistic values, or null
+ * if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getDirreqV3TunneledDl();
+
+ /**
+ * Return the directory request read history contained in this
+ * descriptor, or null if no such history is contained.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getDirreqReadHistory();
+
+ /**
+ * Return the directory request write history contained in this
+ * descriptor, or null if no such history is contained.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getDirreqWriteHistory();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * entry statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getEntryStatsEndMillis();
+
+ /**
+ * Return the interval length of the included entry statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getEntryStatsIntervalLength();
+
+ /**
+ * Return statistics on client IP addresses with map keys being country
+ * codes and map values being the number of unique IP addresses that
+ * have connected from that country rounded up to the nearest multiple
+ * of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getEntryIps();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * cell statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getCellStatsEndMillis();
+
+ /**
+ * Return the interval length of the included cell statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getCellStatsIntervalLength();
+
+ /**
+ * Return the mean number of processed cells per circuit by circuit
+ * decile starting with the loudest decile at index 0 and the quietest
+ * decile at index 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCellProcessedCells();
+
+ /**
+ * Return the mean number of cells contained in circuit queues by
+ * circuit decile starting with the loudest decile at index 0 and the
+ * quietest decile at index 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public List<Double> getCellQueuedCells();
+
+ /**
+ * Return the mean times in milliseconds that cells spend in circuit
+ * queues by circuit decile starting with the loudest decile at index 0
+ * and the quietest decile at index 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCellTimeInQueue();
+
+ /**
+ * Return the mean number of circuits included in any of the cell
+ * statistics deciles, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getCellCircuitsPerDecile();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * statistics on bi-directional connection usage ended, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getConnBiDirectStatsEndMillis();
+
+ /**
+ * Return the interval length of the included statistics on
+ * bi-directional connection usage in seconds, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getConnBiDirectStatsIntervalLength();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * less than 2 KiB/s in a 10-second interval, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectBelow();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval and at least 10 times more
+ * in read direction than in write direction, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectRead();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval and at least 10 times more
+ * in write direction than in read direction, or -1 if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectWrite();
+
+ /**
+ * Return the number of connections on which this server read and wrote
+ * at least 2 KiB/s in a 10-second interval but not 10 times more in
+ * either direction, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public int getConnBiDirectBoth();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * exit statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getExitStatsEndMillis();
+
+ /**
+ * Return the interval length of the included exit statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getExitStatsIntervalLength();
+
+ /**
+ * Return statistics on KiB written to streams exiting the Tor network
+ * by target TCP port with map keys being string representations of
+ * ports (or {@code "other"}) and map values being KiB rounded up to the
+ * next full KiB, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitKibibytesWritten();
+
+ /**
+ * Return statistics on KiB read from streams exiting the Tor network
+ * by target TCP port with map keys being string representations of
+ * ports (or {@code "other"}) and map values being KiB rounded up to the
+ * next full KiB, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitKibibytesRead();
+
+ /**
+ * Return statistics on opened streams exiting the Tor network by
+ * target TCP port with map keys being string representations of ports
+ * (or {@code "other"}) and map values being the number of opened
+ * streams, rounded up to the nearest multiple of 4, or null if no such
+ * statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Long> getExitStreamsOpened();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * "geoip" statistics interval started, or -1 if no such statistics are
+ * included (which is the case except for very old Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public long getGeoipStartTimeMillis();
+
+ /**
+ * Return statistics on the origin of client IP addresses with map keys
+ * being country codes and map values being the number of unique IP
+ * addresses that have connected from that country between the start of
+ * the statistics interval and the descriptor publication time rounded
+ * up to the nearest multiple of 8, or null if no such statistics are
+ * included (which is the case except for very old Tor versions).
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getGeoipClientOrigins();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * bridge statistics interval ended, or -1 if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public long getBridgeStatsEndMillis();
+
+ /**
+ * Return the interval length of the included bridge statistics in
+ * seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public long getBridgeStatsIntervalLength();
+
+ /**
+ * Return statistics on bridge client IP addresses by country with map
+ * keys being country codes and map values being the number of unique IP
+ * addresses that have connected from that country rounded up to the
+ * nearest multiple of 8, or null if no such statistics are included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIps();
+
+ /**
+ * Return statistics on bridge client IP addresses by IP version with
+ * map keys being protocol families, e.g., {@code "v4"} or {@code "v6"},
+ * and map values being the number of unique IP addresses rounded up to
+ * the nearest multiple of 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIpVersions();
+
+ /**
+ * Return statistics on bridge client IP addresses by transport with
+ * map keys being pluggable transport names, e.g., {@code "obfs2"} or
+ * {@code "obfs3"} for known transports, {@code "<OR>"} for the default
+ * onion routing protocol, or {@code "<??>"} for an unknown transport,
+ * and map values being the number of unique IP addresses rounded up to
+ * the nearest multiple of 8, or null if no such statistics are
+ * included.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBridgeIpTransports();
+
+ /**
+ * Return the (possibly empty) list of pluggable transports supported
+ * by this server.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getTransports();
+
+ /**
+ * Return the time in milliseconds since the epoch when the included
+ * hidden-service statistics interval ended, or -1 if no such statistics
+ * are included.
+ *
+ * @since 1.1.0
+ */
+ public long getHidservStatsEndMillis();
+
+ /**
+ * Return the interval length of the included hidden-service statistics
+ * in seconds, or -1 if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public long getHidservStatsIntervalLength();
+
+ /**
+ * Return the approximate number of RELAY cells seen in either
+ * direction on a circuit after receiving and successfully processing a
+ * RENDEZVOUS1 cell, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Double getHidservRendRelayedCells();
+
+ /**
+ * Return the obfuscation parameters applied to the original
+ * measurement value of RELAY cells seen in either direction on a
+ * circuit after receiving and successfully processing a RENDEZVOUS1
+ * cell, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Double> getHidservRendRelayedCellsParameters();
+
+ /**
+ * Return the approximate number of unique hidden-service identities
+ * seen in descriptors published to and accepted by this hidden-service
+ * directory, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Double getHidservDirOnionsSeen();
+
+ /**
+ * Return the obfuscation parameters applied to the original
+ * measurement value of unique hidden-service identities seen in
+ * descriptors published to and accepted by this hidden-service
+ * directory, or null if no such statistics are included.
+ *
+ * @since 1.1.0
+ */
+ public Map<String, Double> getHidservDirOnionsSeenParameters();
+
+ /**
+ * 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
+ * a signature (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignature();
+
+ /**
+ * Return the Ed25519 certificate in PEM format, or null if the
+ * descriptor doesn't contain one.
+ *
+ * @since 1.1.0
+ */
+ public String getIdentityEd25519();
+
+ /**
+ * Return the Ed25519 master key, encoded as 43 base64 characters
+ * without padding characters, which was either parsed from the optional
+ * {@code "master-key-ed25519"} line or derived from the (likewise
+ * optional) Ed25519 certificate following the
+ * {@code "identity-ed25519"} line, or null if the descriptor contains
+ * neither Ed25519 master key nor Ed25519 certificate.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+
+ /**
+ * Return the Ed25519 signature of the SHA-256 digest of the entire
+ * descriptor, encoded as 86 base64 characters without padding
+ * characters, from the first character up to and including the first
+ * space after the {@code "router-sig-ed25519"} string, prefixed with
+ * the string {@code "Tor router descriptor signature v1"}.
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignatureEd25519();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java b/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java
new file mode 100644
index 0000000..c54e48f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ImplementationNotAccessibleException.java
@@ -0,0 +1,22 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Thrown if a descriptor source implementation class cannot be found,
+ * instantiated, or accessed.
+ *
+ * @see DescriptorSourceFactory
+ * @since 1.0.0
+ */
+@SuppressWarnings("serial")
+public class ImplementationNotAccessibleException
+ extends RuntimeException {
+
+ public ImplementationNotAccessibleException(String string,
+ Throwable ex) {
+ super(string, ex);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/Microdescriptor.java b/src/main/java/org/torproject/descriptor/Microdescriptor.java
new file mode 100644
index 0000000..f19b7df
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/Microdescriptor.java
@@ -0,0 +1,135 @@
+/* Copyright 2014--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a relay microdescriptor.
+ *
+ * <p>A microdescriptor is a stripped-down version of a relay server
+ * descriptor ({@link RelayServerDescriptor}) generated by the directory
+ * authorities by extracting and/or transforming relay server descriptor
+ * contents following strict rules without adding the authority's opinion
+ * about the relay. Microdescriptors are referenced from microdescriptor
+ * consensuses ({@link RelayNetworkStatusConsensus}) and downloaded by
+ * clients to make path-selection decisions and to build circuits.
+ * Microdescriptors contain only the most relevant parts that clients care
+ * about. Microdescriptors are expected to be relatively static and only
+ * change about once per week.</p>
+ *
+ * @since 1.0.0
+ */
+public interface Microdescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that is used to reference this
+ * descriptor from a vote or microdescriptor consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getMicrodescriptorDigest();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used to encrypt CREATE
+ * cells for this server, or null if the descriptor doesn't contain an
+ * onion key.
+ *
+ * @since 1.0.0
+ */
+ public String getOnionKey();
+
+ /**
+ * Return the curve25519 public key, encoded as 43 base64 characters
+ * without padding characters, that is used for the ntor circuit
+ * extended handshake, or null if the descriptor didn't contain an
+ * ntor-onion-key line.
+ *
+ * @since 1.0.0
+ */
+ public String getNtorOnionKey();
+
+ /**
+ * Return IP addresses and TCP ports where this server accepts TLS
+ * connections for the main OR protocol, or an empty list if the server
+ * does not support additional addresses or ports; entries are given in
+ * the order as they are listed in the descriptor; IPv4 addresses are
+ * given in dotted-quad format, IPv6 addresses use the colon-separated
+ * hexadecimal format surrounded by square brackets, and TCP ports are
+ * separated from the IP address using a colon.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return nicknames, $-prefixed identity fingerprints, or tuples of the
+ * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
+ * of servers contained in this server's family, or null if the
+ * descriptor does not contain a family line.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getFamilyEntries();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv4 port summary, or null if the descriptor didn't contain an
+ * IPv4 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv4 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getDefaultPolicy();
+
+ /**
+ * Return the port list of the IPv4 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv4 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv4 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getPortList();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv6 port summary, or null if the descriptor didn't contain an
+ * IPv6 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6DefaultPolicy();
+
+ /**
+ * Return the port list of the IPv6 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv6 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6PortList();
+
+ /**
+ * Return a SHA-1 digest of the server's RSA-1024 identity key, encoded
+ * as 27 base64 characters without padding characters, that is only
+ * included to prevent collisions between microdescriptors, or null if
+ * no such digest is included.
+ *
+ * @since 1.1.0
+ */
+ public String getRsa1024Identity();
+
+ /**
+ * Return a SHA-256 digest of the server's Ed25519 identity key,
+ * encoded as 43 base64 characters without padding characters, that is
+ * only included to prevent collisions between microdescriptors, or null
+ * if no such digest is included.
+ *
+ * @since 1.1.0
+ */
+ public String getEd25519Identity();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
new file mode 100644
index 0000000..43b3175
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/NetworkStatusEntry.java
@@ -0,0 +1,177 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * Contains an entry in a network status in the version 2 or 3 directory
+ * protocol or in a bridge network status.
+ *
+ * <p>A network status entry is not a descriptor type of its own but is
+ * part of a network status in the version 2 directory protocol
+ * ({@link RelayNetworkStatus}), a vote ({@link RelayNetworkStatusVote})
+ * or flavored/unflavored consensus (@link RelayNetworkStatusConsensus})
+ * in the version 3 directory protocol, or a bridge network status
+ * ({@link BridgeNetworkStatus}). Entries in signed directories in the
+ * version 1 directory protocol are represented by router status entries
+ * ({@link RouterStatusEntry}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface NetworkStatusEntry {
+
+ /**
+ * Return the raw network status entry bytes.
+ *
+ * @since 1.0.0
+ */
+ public byte[] getStatusEntryBytes();
+
+ /**
+ * Return the server nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the server's identity key, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the SHA-1 digest of the server descriptor, or null if the
+ * containing network status does not contain server descriptor
+ * references, like a microdesc consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getDescriptor();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the server's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this server accepts TLS connections for
+ * the main OR protocol.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return the TCP port where this server accepts directory-related HTTP
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return the (possibly empty) set of microdescriptor digests if the
+ * containing network status is a vote or microdesc consensus, or null
+ * otherwise.
+ *
+ * @since 1.0.0
+ */
+ public Set<String> getMicrodescriptorDigests();
+
+ /**
+ * Return additional IP addresses and TCP ports where this server
+ * accepts TLS connections for the main OR protocol, or an empty list if
+ * the network status doesn't contain any such additional addresses and
+ * ports.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return the relay flags assigned to this server, or null if the
+ * status entry didn't contain any relay flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getFlags();
+
+ /**
+ * Return the Tor software version, or null if the status entry didn't
+ * contain version information.
+ *
+ * @since 1.0.0
+ */
+ public String getVersion();
+
+ /**
+ * Return the bandwidth weight of this server or -1 if the status entry
+ * didn't contain a bandwidth line.
+ *
+ * @since 1.0.0
+ */
+ public long getBandwidth();
+
+ /**
+ * Return the measured bandwidth or -1 if the status entry either
+ * didn't contain bandwidth information or didn't contain an indication
+ * that this information is based on measured bandwidth.
+ *
+ * @since 1.0.0
+ */
+ public long getMeasured();
+
+ /**
+ * Return whether the status entry is yet unmeasured by the bandwidth
+ * authorities; only included in consensuses using method 17 or higher.
+ *
+ * @since 1.0.0
+ */
+ public boolean getUnmeasured();
+
+ /**
+ * Return the default policy of the port summary, which can be either
+ * {@code "accept"} or {@code "reject"}, or null if the status entry
+ * didn't contain an exit policy summary.
+ *
+ * @since 1.0.0
+ */
+ public String getDefaultPolicy();
+
+ /**
+ * Return the list of ports or port intervals of the exit port summary,
+ * or null if the status entry didn't contain an exit policy summary.
+ *
+ * @since 1.0.0
+ */
+ public String getPortList();
+
+ /**
+ * Return the server's Ed25519 master key, encoded as 43 base64
+ * characters without padding characters, "none" if the relay doesn't
+ * have an Ed25519 identity, or null if the status entry didn't contain
+ * this information or if the status is not a vote.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayDirectory.java b/src/main/java/org/torproject/descriptor/RelayDirectory.java
new file mode 100644
index 0000000..8f3e58b
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayDirectory.java
@@ -0,0 +1,104 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a signed directory in the version 1 directory protocol.
+ *
+ * <p>Directory authorities in the (long outdated) version 1 of the
+ * directory protocol served signed directory documents containing a list
+ * of signed server descriptors ({@link ServerDescriptor}) along with
+ * short summaries of the status of each server
+ * ({@link RouterStatusEntry}).</p>
+ *
+ * <p>Clients in that version of the directory protocol would fetch this
+ * signed directory to get up-to-date information on the state of the
+ * network and be certain that the list was attested by a trusted
+ * directory authority.</p>
+ *
+ * <p>Signed directories in the version 1 directory protocol have first
+ * been superseded by network status documents in the version 2 directory
+ * protocol ({@link RelayNetworkStatus}) and later by network status
+ * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
+ * directory protocol.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayDirectory extends Descriptor {
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this authority
+ * as long-term identity key and to sign network statuses, or null if
+ * this key is not included in the descriptor header.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return recommended Tor versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedSoftware();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectorySignature();
+
+ /**
+ * Return router status entries, one for each contained relay.
+ *
+ * @since 1.0.0
+ */
+ public List<RouterStatusEntry> getRouterStatusEntries();
+
+ /**
+ * Return a list of server descriptors contained in the signed
+ * directory.
+ *
+ * @since 1.0.0
+ */
+ public List<ServerDescriptor> getServerDescriptors();
+
+ /**
+ * Return a (very likely empty) list of exceptions from parsing the
+ * contained server descriptors.
+ *
+ * @since 1.0.0
+ */
+ public List<Exception> getServerDescriptorParseExceptions();
+
+ /**
+ * Return the directory nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the SHA-1 directory digest, encoded as 40 lower-case
+ * hexadecimal characters, that the directory authority used to sign the
+ * directory.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectoryDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java b/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java
new file mode 100644
index 0000000..73f8438
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayExtraInfoDescriptor.java
@@ -0,0 +1,21 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a relay extra-info descriptor.
+ *
+ * <p>Relay extra-info descriptors share many contents with sanitized
+ * bridge extra-info descriptors ({@link BridgeExtraInfoDescriptor}),
+ * which is why they share a common superinterface
+ * ({@link ExtraInfoDescriptor}). The main purpose of having two
+ * subinterfaces is being able to distinguish descriptor types more
+ * easily.</p>
+ *
+ * @since 1.1.0
+ */
+public interface RelayExtraInfoDescriptor extends ExtraInfoDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java
new file mode 100644
index 0000000..db3ddac
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatus.java
@@ -0,0 +1,176 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status document in the version 2 directory protocol.
+ *
+ * <p>Directory authorities in the (outdated) version 2 of the directory
+ * protocol published signed network status documents. Each network
+ * status listed, for every relay in the network
+ * ({@link NetworkStatusEntry}): a hash of its identity key, a hash of its
+ * most recent server descriptor, and a summary of what the authority
+ * believed about its status.</p>
+ *
+ * <p>Clients would download the authorities' network status documents in
+ * turn, and believe statements about routers iff they were attested to by
+ * more than half of the authorities.</p>
+ *
+ * <p>Network status documents in the version 2 directory protocol
+ * supersede signed directories in the version 1 directory protocol
+ * ({@link RelayDirectory}) and have been superseded by network status
+ * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
+ * directory protocol.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatus extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 2.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.0.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the descriptor does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirport();
+
+ /**
+ * Return a SHA-1 digest of the authority's public identity key,
+ * encoded as 40 upper-case hexadecimal characters, which is also used
+ * to sign network statuses.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this authority
+ * as long-term identity key and to sign network statuses.
+ *
+ * @since 1.0.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * authority does not recommend server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * authority does not recommend client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the set of flags that this directory assigns to relays, or
+ * null if the status does not assign such flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getDirOptions();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the directory signature string made with the authority's
+ * identity key.
+ *
+ * @since 1.0.0
+ */
+ public String getDirectorySignature();
+
+ /**
+ * Return the SHA-1 status digest, encoded as 40 lower-case hexadecimal
+ * characters, that the directory authority used to sign the network
+ * status.
+ *
+ * @since 1.0.0
+ */
+ public String getStatusDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
new file mode 100644
index 0000000..15fdaca
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -0,0 +1,223 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status consensus in the version 3 directory protocol.
+ *
+ * <p>Directory authorities in the version 3 of the directory protocol
+ * periodically generate a view of the current descriptors and status for
+ * known relays and send a signed summary of this view to the other
+ * authorities ({@link RelayNetworkStatusVote}). The authorities compute
+ * the result of this vote and sign a network status consensus containing
+ * the result of the vote, which is this document.</p>
+ *
+ * <p>Clients use consensus documents to find out when their list of
+ * relays is out-of-date by looking at the contained network status
+ * entries ({@link NetworkStatusEntry}). If it is, they download any
+ * missing server descriptors ({@link ServerDescriptor}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatusConsensus extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 3 or
+ * higher.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the consensus flavor name, which denotes the variant of the
+ * original, unflavored consensus, encoded as a string of alphanumeric
+ * characters and dashes, or null if this descriptor is the unflavored
+ * consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getConsensusFlavor();
+
+ /**
+ * Return the consensus method number of this descriptor, which is the
+ * highest consensus method supported by more than 2/3 of voting
+ * authorities, or 0 if no consensus method is contained in the
+ * descriptor.
+ *
+ * @since 1.0.0
+ */
+ public int getConsensusMethod();
+
+ /**
+ * Return the time in milliseconds since the epoch at which this
+ * descriptor became valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidAfterMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which this
+ * descriptor is the freshest that is available.
+ *
+ * @since 1.0.0
+ */
+ public long getFreshUntilMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which this
+ * descriptor was valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidUntilMillis();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect votes from the other authorities when producing the
+ * next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getVoteSeconds();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect signatures from the other authorities when producing
+ * the next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getDistSeconds();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * consensus does not contain an opinion about server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * consensus does not contain an opinion about client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * 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
+ * authorities and agreed upon by the majority of directory authorities,
+ * or null if the consensus does not contain package information.
+ *
+ * @since 1.3.0
+ */
+ public List<String> getPackageLines();
+
+ /**
+ * Return known relay flags in this descriptor that were contained in
+ * enough votes for this consensus to be an authoritative opinion for
+ * these relay flags.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getKnownFlags();
+
+ /**
+ * Return consensus parameters contained in this descriptor with map
+ * keys being case-sensitive parameter identifiers and map values being
+ * parameter values, or null if the consensus doesn't contain consensus
+ * parameters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getConsensusParams();
+
+ /**
+ * Return directory source entries for each directory authority that
+ * contributed to the consensus, with map keys being SHA-1 digests of
+ * the authorities' identity keys in the version 3 directory protocol,
+ * encoded as 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirSourceEntry> getDirSourceEntries();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return directory signatures of this consensus, with map keys being
+ * SHA-1 digests of the authorities' identity keys in the version 3
+ * directory protocol, encoded as 40 upper-case hexadecimal characters.
+ *
+ * @deprecated Replaced by {@link #getSignatures()} which permits an
+ * arbitrary number of signatures made by an authority using the same
+ * identity key digest and different algorithms.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirectorySignature> getDirectorySignatures();
+
+ /**
+ * Return the list of signatures contained in this consensus.
+ *
+ * @since 1.3.0
+ */
+ public List<DirectorySignature> getSignatures();
+
+ /**
+ * Return optional weights to be applied to router bandwidths during
+ * path selection with map keys being case-sensitive weight identifiers
+ * and map values being weight values, or null if the consensus doesn't
+ * contain such weights.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getBandwidthWeights();
+
+ /**
+ * Return the SHA-1 digest of this consensus, encoded as 40 upper-case
+ * hexadecimal characters that directory authorities use to sign the
+ * consensus.
+ *
+ * @since 1.0.0
+ */
+ public String getConsensusDigest();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
new file mode 100644
index 0000000..1f77db6
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -0,0 +1,408 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * Contains a network status vote in the version 3 directory protocol.
+ *
+ * <p>Directory authorities in the version 3 of the directory protocol
+ * periodically generate a view of the current descriptors and status for
+ * known relays and send a signed summary of this view to the other
+ * authorities, which is this document. The authorities compute the
+ * result of this vote and sign a network status consensus containing the
+ * result of the vote ({@link RelayNetworkStatusConsensus}).</p>
+ *
+ * @since 1.0.0
+ */
+public interface RelayNetworkStatusVote extends Descriptor {
+
+ /**
+ * Return the document format version of this descriptor which is 3 or
+ * higher.
+ *
+ * @since 1.0.0
+ */
+ public int getNetworkStatusVersion();
+
+ /**
+ * Return the list of consensus method numbers supported by this
+ * authority, or null if the descriptor doesn't say so, which would mean
+ * that only method 1 is supported.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getConsensusMethods();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * was published.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch at which the
+ * consensus is supposed to become valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidAfterMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which the
+ * consensus is supposed to be the freshest that is available.
+ *
+ * @since 1.0.0
+ */
+ public long getFreshUntilMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch until which the
+ * consensus is supposed to be valid.
+ *
+ * @since 1.0.0
+ */
+ public long getValidUntilMillis();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect votes from the other authorities when producing the
+ * next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getVoteSeconds();
+
+ /**
+ * Return the number of seconds that the directory authorities will
+ * allow to collect signatures from the other authorities when producing
+ * the next consensus.
+ *
+ * @since 1.0.0
+ */
+ public long getDistSeconds();
+
+ /**
+ * Return recommended Tor versions for server usage, or null if the
+ * authority does not recommend server versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedServerVersions();
+
+ /**
+ * Return recommended Tor versions for client usage, or null if the
+ * authority does not recommend client versions.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getRecommendedClientVersions();
+
+ /**
+ * 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
+ * null if this descriptor does not contain package information.
+ *
+ * @since 1.3.0
+ */
+ public List<String> getPackageLines();
+
+ /**
+ * Return known relay flags by this authority.
+ *
+ * @since 1.0.0
+ */
+ public SortedSet<String> getKnownFlags();
+
+ /**
+ * Return the minimum uptime in seconds that this authority requires
+ * for assigning the Stable flag, or -1 if the authority doesn't report
+ * this value.
+ *
+ * @since 1.0.0
+ */
+ public long getStableUptime();
+
+ /**
+ * Return the minimum MTBF (mean time between failure) that this
+ * authority requires for assigning the Stable flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getStableMtbf();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Fast flag, or -1 if the authority doesn't report this
+ * value.
+ *
+ * @since 1.0.0
+ */
+ public long getFastBandwidth();
+
+ /**
+ * Return the minimum WFU (weighted fractional uptime) in percent that
+ * this authority requires for assigning the Guard flag, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public double getGuardWfu();
+
+ /**
+ * Return the minimum weighted time in seconds that this authority
+ * needs to know about a relay before assigning the Guard flag, or -1 if
+ * the authority doesn't report this information.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardTk();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardBandwidthIncludingExits();
+
+ /**
+ * Return the minimum bandwidth that this authority requires for
+ * assigning the Guard flag if exits can not be guards, or -1 if the
+ * authority doesn't report this value.
+ *
+ * @since 1.0.0
+ */
+ public long getGuardBandwidthExcludingExits();
+
+ /**
+ * Return 1 if the authority has measured enough MTBF info to use the
+ * MTBF requirement instead of the uptime requirement for assigning the
+ * Stable flag, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public int getEnoughMtbfInfo();
+
+ /**
+ * Return 1 if the authority has enough measured bandwidths that it'll
+ * ignore the advertised bandwidth claims of routers without measured
+ * bandwidth, 0 if not, or -1 if the authority doesn't report this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getIgnoringAdvertisedBws();
+
+ /**
+ * Return consensus parameters contained in this descriptor with map
+ * keys being case-sensitive parameter identifiers and map values being
+ * parameter values, or null if the authority doesn't include consensus
+ * parameters in its vote.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, Integer> getConsensusParams();
+
+ /**
+ * Return the authority's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the authority's long-term authority
+ * identity key used for the version 3 directory protocol, encoded as
+ * 40 upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getIdentity();
+
+ /**
+ * Return the authority's hostname.
+ *
+ * @since 1.2.0
+ */
+ public String getHostname();
+
+ /**
+ * Return the authority's primary IPv4 address in dotted-quad format,
+ * or null if the descriptor does not contain an address.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this authority accepts directory-related
+ * HTTP connections, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirport();
+
+ /**
+ * Return the TCP port where this authority accepts TLS connections for
+ * the main OR protocol, or 0 if the authority does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getOrport();
+
+ /**
+ * Return the contact information for this authority, which may contain
+ * non-ASCII characters, or null if no contact information is included
+ * in the descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getContactLine();
+
+ /**
+ * Return the version of the directory key certificate used by this
+ * authority, which must be 3 or higher.
+ *
+ * @since 1.0.0
+ */
+ public int getDirKeyCertificateVersion();
+
+ /**
+ * Return the SHA-1 digest for an obsolete authority identity key still
+ * used by this authority to keep older clients working, or null if this
+ * authority does not use such a key.
+ *
+ * @since 1.0.0
+ */
+ public String getLegacyDirKey();
+
+ /**
+ * Return the authority's identity key in PEM format.
+ *
+ * @since 1.2.0
+ */
+ public String getDirIdentityKey();
+
+ /**
+ * Return the time in milliseconds since the epoch when the authority's
+ * signing key and corresponding key certificate were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyPublishedMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch after which the
+ * authority's signing key is no longer valid.
+ *
+ * @since 1.0.0
+ */
+ public long getDirKeyExpiresMillis();
+
+ /**
+ * Return the authority's signing key in PEM format.
+ *
+ * @since 1.2.0
+ */
+ public String getDirSigningKey();
+
+ /**
+ * Return the SHA-1 digest of the authority's signing key, encoded as
+ * 40 upper-case hexadecimal characters, or null if this digest cannot
+ * be obtained from the directory signature.
+ *
+ * @deprecated Removed in order to be more explicit that authorities may
+ * use different digest algorithms than "sha1"; see
+ * {@link #getSignatures()} and
+ * {@link DirectorySignature#getSigningKeyDigest()} for
+ * alternatives.
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKeyDigest();
+
+ /**
+ * Return the signature of the authority's identity key made using the
+ * authority's signing key, or null if the vote does not contain such a
+ * signature.
+ *
+ * @since 1.2.0
+ */
+ public String getDirKeyCrosscert();
+
+ /**
+ * Return the certificate signature from the initial item
+ * "dir-key-certificate-version" until the final item
+ * "dir-key-certification", signed with the authority identity key.
+ *
+ * @since 1.2.0
+ */
+ public String getDirKeyCertification();
+
+ /**
+ * Return status entries for each contained server, with map keys being
+ * SHA-1 digests of the servers' public identity keys, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries();
+
+ /**
+ * Return whether a status entry with the given relay fingerprint
+ * (SHA-1 digest of the server's public identity key, encoded as 40
+ * upper-case hexadecimal characters) exists; convenience method for
+ * {@code getStatusEntries().containsKey(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public boolean containsStatusEntry(String fingerprint);
+
+ /**
+ * Return a status entry by relay fingerprint (SHA-1 digest of the
+ * server's public identity key, encoded as 40 upper-case hexadecimal
+ * characters), or null if no such status entry exists; convenience
+ * method for {@code getStatusEntries().get(fingerprint)}.
+ *
+ * @since 1.0.0
+ */
+ public NetworkStatusEntry getStatusEntry(String fingerprint);
+
+ /**
+ * Return the directory signature of this vote, with the single map key
+ * being the SHA-1 digest of the authority's identity key in the version
+ * 3 directory protocol, encoded as 40 upper-case hexadecimal
+ * characters.
+ *
+ * @deprecated Replaced by {@link #getSignatures()} which permits an
+ * arbitrary number of signatures made by the authority using the same
+ * identity key digest and different algorithms.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<String, DirectorySignature> getDirectorySignatures();
+
+ /**
+ * Return a list of signatures contained in this vote, which is
+ * typically a single signature made by the authority but which may also
+ * be more than one signature made with different keys or algorithms.
+ *
+ * @since 1.3.0
+ */
+ public List<DirectorySignature> getSignatures();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java b/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java
new file mode 100644
index 0000000..6ef3140
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RelayServerDescriptor.java
@@ -0,0 +1,20 @@
+/* Copyright 2015--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a relay server descriptor.
+ *
+ * <p>Relay server descriptors share many contents with sanitized bridge
+ * server descriptors ({@link BridgeServerDescriptor}), which is why they
+ * share a common superinterface ({@link ServerDescriptor}). The main
+ * purpose of having two subinterfaces is being able to distinguish
+ * descriptor types more easily.</p>
+ *
+ * @since 1.1.0
+ */
+public interface RelayServerDescriptor extends ServerDescriptor {
+
+}
+
diff --git a/src/main/java/org/torproject/descriptor/RouterStatusEntry.java b/src/main/java/org/torproject/descriptor/RouterStatusEntry.java
new file mode 100644
index 0000000..f9a56db
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/RouterStatusEntry.java
@@ -0,0 +1,51 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+/**
+ * Contains a router status entry contained in a signed directory in the
+ * version 1 directory protocol.
+ *
+ * <p>Directory authorities in the (long outdated) version 1 of the
+ * directory protocol included router status entries with short summaries
+ * of the status of each server in the signed directories they produced
+ * ({@link RelayDirectory}). These entries contained references to server
+ * descriptors published by relays together with the authorities' opinion
+ * on whether relays were verified and live.</p>
+ *
+ * @since 1.0.0
+ */
+public interface RouterStatusEntry {
+
+ /**
+ * Return the relay nickname consisting of 1 to 19 alphanumeric
+ * characters, or null if the relay is unverified.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return a SHA-1 digest of the relay's identity key, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return whether the relay is verified.
+ *
+ * @since 1.0.0
+ */
+ public boolean isVerified();
+
+ /**
+ * Return whether the relay is live.
+ *
+ * @since 1.0.0
+ */
+ public boolean isLive();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/ServerDescriptor.java b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
new file mode 100644
index 0000000..d1af421
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
@@ -0,0 +1,435 @@
+/* Copyright 2011--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+
+/**
+ * Contains a relay or sanitized bridge server descriptor.
+ *
+ * <p>Relays publish server descriptors to the directory authorities to
+ * register in the network. Server descriptors contain information about
+ * the capabilities of a server, like their exit policy, that clients use
+ * to select servers for their circuits (along with information provided
+ * by directory authorities on reachability, stability, and capacity of
+ * servers). Server descriptors also contain network addresses and
+ * cryptographic material that clients use to build circuits.</p>
+ *
+ * <p>Prior to the introduction of microdescriptors
+ * ({@link Microdescriptor}), the directory authorities included
+ * cryptographic digests of server descriptors in network statuses
+ * ({@link RelayNetworkStatusConsensus}) and clients downloaded all
+ * referenced server descriptors. Nowadays, the directory authorities
+ * derive microdescriptors from server descriptors and reference those
+ * in network statuses, and clients only download microdescriptors instead
+ * of server descriptors.</p>
+ *
+ * <p>Bridges publish server descriptors to the bridge directory
+ * authority, also to announce themselves in the network. The bridge
+ * directory authority compiles a list of available bridges
+ * ({@link BridgeNetworkStatus}) for the bridge distribution service
+ * BridgeDB. There are no microdescriptors for bridges, so that bridge
+ * clients still rely on downloading bridge server descriptors directly
+ * from the bridge they're connecting to.</p>
+ *
+ * <p>It's worth noting that all contents of server descriptors are
+ * written and signed by relays and bridges without a third party
+ * verifying their correctness. The (bridge) directory authorities may
+ * decide to exclude dishonest servers from the network statuses they
+ * produce, but that wouldn't be reflected in server descriptors.</p>
+ *
+ * @since 1.0.0
+ */
+public interface ServerDescriptor extends Descriptor {
+
+ /**
+ * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
+ * descriptors) or upper-case (bridge descriptors) hexadecimal
+ * characters, that is used to reference this descriptor from a network
+ * status descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getServerDescriptorDigest();
+
+ /**
+ * Return the SHA-256 descriptor digest, encoded as 43 base64
+ * characters without padding characters, that may be used to reference
+ * this server descriptor from a network status descriptor.
+ *
+ * @since 1.1.0
+ */
+ public String getServerDescriptorDigestSha256();
+
+ /**
+ * Return the server's nickname consisting of 1 to 19 alphanumeric
+ * characters.
+ *
+ * @since 1.0.0
+ */
+ public String getNickname();
+
+ /**
+ * Return the server's primary IPv4 address in dotted-quad format.
+ *
+ * @since 1.0.0
+ */
+ public String getAddress();
+
+ /**
+ * Return the TCP port where this server accepts TLS connections for
+ * the main OR protocol, or 0 if the server does not accept such
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public int getOrPort();
+
+ /**
+ * Return the TCP port where this server accepts SOCKS connections,
+ * which is deprecated and should always be 0.
+ *
+ * @since 1.0.0
+ */
+ public int getSocksPort();
+
+ /**
+ * Return the TCP port where this server accepts directory-related HTTP
+ * connections, or 0 if the server does not accept such connections.
+ *
+ * @since 1.0.0
+ */
+ public int getDirPort();
+
+ /**
+ * Return IP addresses and TCP ports where this server accepts TLS
+ * connections for the main OR protocol, or an empty list if the server
+ * does not support additional addresses or ports; entries are given in
+ * the order as they are listed in the descriptor; IPv4 addresses are
+ * given in dotted-quad format, IPv6 addresses use the colon-separated
+ * hexadecimal format surrounded by square brackets, and TCP ports are
+ * separated from the IP address using a colon.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getOrAddresses();
+
+ /**
+ * Return the average bandwidth in bytes per second that the server is
+ * willing to sustain over long periods.
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthRate();
+
+ /**
+ * Return the burst bandwidth in bytes per second that the server is
+ * willing to sustain in very short intervals.
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthBurst();
+
+ /**
+ * Return the observed bandwidth in bytes per second as an estimate of
+ * the capacity that the server can handle, or -1 if the descriptor
+ * doesn't contain an observed bandwidth value (which is the case for
+ * Tor 0.0.8 or older).
+ *
+ * @since 1.0.0
+ */
+ public int getBandwidthObserved();
+
+ /**
+ * Return a human-readable string describing the Tor software version
+ * and the operating system of this server, which may contain non-ASCII
+ * characters, typically written as {@code "Tor $version on $system"},
+ * or null if this descriptor does not contain a platform line.
+ *
+ * @since 1.0.0
+ */
+ public String getPlatform();
+
+ /**
+ * Return the time in milliseconds since the epoch when this descriptor
+ * and the corresponding extra-info descriptor were generated.
+ *
+ * @since 1.0.0
+ */
+ public long getPublishedMillis();
+
+ /**
+ * Return a SHA-1 digest of the server's public identity key, encoded
+ * as 40 upper-case hexadecimal characters (without spaces after every 4
+ * characters as opposed to the encoding in the descriptor), that is
+ * typically used to uniquely identify the server, or null if this
+ * descriptor does not contain a fingerprint line.
+ *
+ * @since 1.0.0
+ */
+ public String getFingerprint();
+
+ /**
+ * Return whether the server was hibernating when this descriptor was
+ * published and should not be used to build circuits.
+ *
+ * @since 1.0.0
+ */
+ public boolean isHibernating();
+
+ /**
+ * Return the number of seconds that the server process has been
+ * running (which might even be negative in a few descriptors due to a
+ * bug that was fixed in Tor 0.1.2.7-alpha), or null if the descriptor
+ * does not contain an uptime line.
+ *
+ * @since 1.0.0
+ */
+ public Long getUptime();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used to encrypt CREATE
+ * cells for this server, or null if the descriptor doesn't contain an
+ * onion key (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getOnionKey();
+
+ /**
+ * Return the RSA-1024 public key in PEM format used by this server as
+ * long-term identity key, or null if the descriptor doesn't contain a
+ * signing key (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getSigningKey();
+
+ /**
+ * Return the server's exit policy consisting of one or more accept or
+ * reject rules that the server follows when deciding whether to allow a
+ * new stream to a given IP address and TCP port.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getExitPolicyLines();
+
+ /**
+ * 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
+ * a signature (which is the case in sanitized bridge descriptors).
+ *
+ * @since 1.0.0
+ */
+ public String getRouterSignature();
+
+ /**
+ * Return the contact information for this server, which may contain
+ * non-ASCII characters, or null if no contact information is included
+ * in the descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getContact();
+
+ /**
+ * Return nicknames, $-prefixed identity fingerprints, or tuples of the
+ * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
+ * of servers contained in this server's family, or null if the
+ * descriptor does not contain a family line.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getFamilyEntries();
+
+ /**
+ * Return the server's history of read bytes, or null if the descriptor
+ * does not contain a bandwidth history; current Tor versions include
+ * bandwidth histories in their extra-info descriptors
+ * ({@link ExtraInfoDescriptor#getReadHistory()}), not in their server
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getReadHistory();
+
+ /**
+ * Return the server's history of written bytes, or null if the
+ * descriptor does not contain a bandwidth history; current Tor versions
+ * include bandwidth histories in their extra-info descriptors
+ * ({@link ExtraInfoDescriptor#getWriteHistory()}), not in their server
+ * descriptors.
+ *
+ * @since 1.0.0
+ */
+ public BandwidthHistory getWriteHistory();
+
+ /**
+ * Return true if the server uses the enhanced DNS logic, or false if
+ * doesn't use it or doesn't include an eventdns line in its
+ * descriptor; current Tor versions should be presumed to have the evdns
+ * backend.
+ *
+ * @since 1.0.0
+ */
+ public boolean getUsesEnhancedDnsLogic();
+
+ /**
+ * Return whether this server is a directory cache that provides
+ * extra-info descriptors.
+ *
+ * @since 1.0.0
+ */
+ public boolean getCachesExtraInfo();
+
+ /**
+ * Return the SHA-1 digest of the server's extra-info descriptor,
+ * encoded as 40 upper-case hexadecimal characters, or null if the
+ * server did not upload a corresponding extra-info descriptor.
+ *
+ * @since 1.0.0
+ */
+ public String getExtraInfoDigest();
+
+ /**
+ * Return the SHA-256 digest of the server's extra-info descriptor,
+ * encoded as 43 base64 characters without padding characters, or null
+ * if the server either did not upload a corresponding extra-info
+ * descriptor or did not refer to it using a SHA-256 digest.
+ *
+ * @since 1.1.0
+ */
+ public String getExtraInfoDigestSha256();
+
+ /**
+ * Return the list of hidden service descriptor version numbers that
+ * this server stores and serves, or null if it doesn't store and serve
+ * any hidden service descriptors.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getHiddenServiceDirVersions();
+
+ /**
+ * Return the list of link protocol versions that this server
+ * supports.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getLinkProtocolVersions();
+
+ /**
+ * Return the list of circuit protocol versions that this server
+ * supports.
+ *
+ * @since 1.0.0
+ */
+ public List<Integer> getCircuitProtocolVersions();
+
+ /**
+ * Return whether this server allows single-hop circuits to make exit
+ * connections.
+ *
+ * @since 1.0.0
+ */
+ public boolean getAllowSingleHopExits();
+
+ /**
+ * Return the default policy, {@code "accept"} or {@code "reject"}, of
+ * the IPv6 port summary, or null if the descriptor didn't contain an
+ * IPv6 exit-policy summary line which is equivalent to rejecting all
+ * streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6DefaultPolicy();
+
+ /**
+ * Return the port list of the IPv6 exit-policy summary, or null if the
+ * descriptor didn't contain an IPv6 exit-policy summary line which is
+ * equivalent to rejecting all streams to IPv6 targets.
+ *
+ * @since 1.0.0
+ */
+ public String getIpv6PortList();
+
+ /**
+ * Return the curve25519 public key, encoded as 43 base64 characters
+ * without padding characters, that is used for the ntor circuit
+ * extended handshake, or null if the descriptor didn't contain an
+ * ntor-onion-key line. */
+ public String getNtorOnionKey();
+
+ /**
+ * Return the Ed25519 certificate in PEM format, or null if the
+ * descriptor doesn't contain one.
+ *
+ * @since 1.1.0
+ */
+ public String getIdentityEd25519();
+
+ /**
+ * Return the Ed25519 master key, encoded as 43 base64 characters
+ * without padding characters, which was either parsed from the optional
+ * {@code "master-key-ed25519"} line or derived from the (likewise
+ * optional) Ed25519 certificate following the
+ * {@code "identity-ed25519"} line, or null if the descriptor contains
+ * neither Ed25519 master key nor Ed25519 certificate.
+ *
+ * @since 1.1.0
+ */
+ public String getMasterKeyEd25519();
+
+ /**
+ * Return the Ed25519 signature of the SHA-256 digest of the entire
+ * descriptor, encoded as 86 base64 characters without padding
+ * characters, from the first character up to and including the first
+ * space after the {@code "router-sig-ed25519"} string, prefixed with
+ * the string {@code "Tor router descriptor signature v1"}.
+ *
+ * @since 1.1.0
+ */
+ public String getRouterSignatureEd25519();
+
+ /**
+ * Return an RSA-1024 signature in PEM format, generated using the
+ * server's onion key, that proves that the party creating the
+ * descriptor had control over the private key corresponding to the
+ * onion key, or null if the descriptor does not contain such a
+ * signature.
+ *
+ * @since 1.1.0
+ */
+ public String getOnionKeyCrosscert();
+
+ /**
+ * Return an Ed25519 signature in PEM format, generated using the
+ * server's ntor onion key, that proves that the party creating the
+ * descriptor had control over the private key corresponding to the ntor
+ * onion key, or null if the descriptor does not contain such a
+ * signature.
+ *
+ * @since 1.1.0
+ */
+ public String getNtorOnionKeyCrosscert();
+
+ /**
+ * Return the sign of the Ed25519 public key corresponding to the ntor
+ * onion key as 0 or 1, or -1 if the descriptor does not contain this
+ * information.
+ *
+ * @since 1.1.0
+ */
+ public int getNtorOnionKeyCrosscertSign();
+
+ /**
+ * Return whether the server accepts "tunneled" directory requests using
+ * a BEGIN_DIR cell over the server's OR port.
+ *
+ * @since 1.3.0
+ */
+ public boolean getTunnelledDirServer();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/TorperfResult.java b/src/main/java/org/torproject/descriptor/TorperfResult.java
new file mode 100644
index 0000000..188200b
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/TorperfResult.java
@@ -0,0 +1,215 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.descriptor;
+
+import java.util.List;
+import java.util.SortedMap;
+
+/**
+ * Contains performance measurement results from making simple HTTP
+ * requests over the Tor network.
+ *
+ * <p>The performance measurement service Torperf publishes performance
+ * data from making simple HTTP requests over the Tor network. Torperf
+ * uses a trivial SOCKS client to download files of various sizes over the
+ * Tor network and notes how long substeps take.</p>
+ *
+ * @since 1.0.0
+ */
+public interface TorperfResult extends Descriptor {
+
+ /**
+ * Return all unrecognized keys together with their values, or null if
+ * all keys were recognized.
+ *
+ * @since 1.2.0
+ */
+ public SortedMap<String, String> getUnrecognizedKeys();
+
+ /**
+ * Return the configured name of the data source.
+ *
+ * @since 1.0.0
+ */
+ public String getSource();
+
+ /**
+ * Return the configured file size in bytes.
+ *
+ * @since 1.0.0
+ */
+ public int getFileSize();
+
+ /**
+ * Return the time in milliseconds since the epoch when the connection
+ * process started.
+ *
+ * @since 1.0.0
+ */
+ public long getStartMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the socket was
+ * created.
+ *
+ * @since 1.0.0
+ */
+ public long getSocketMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the socket was
+ * connected.
+ *
+ * @since 1.0.0
+ */
+ public long getConnectMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when SOCKS 5
+ * authentication methods have been negotiated.
+ *
+ * @since 1.0.0
+ */
+ public long getNegotiateMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the SOCKS
+ * request was sent.
+ *
+ * @since 1.0.0
+ */
+ public long getRequestMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the SOCKS
+ * response was received.
+ *
+ * @since 1.0.0
+ */
+ public long getResponseMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the HTTP
+ * request was written.
+ *
+ * @since 1.0.0
+ */
+ public long getDataRequestMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the first
+ * response was received.
+ *
+ * @since 1.0.0
+ */
+ public long getDataResponseMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the payload was
+ * complete.
+ *
+ * @since 1.0.0
+ */
+ public long getDataCompleteMillis();
+
+ /**
+ * Return the total number of bytes written.
+ *
+ * @since 1.0.0
+ */
+ public int getWriteBytes();
+
+ /**
+ * Return the total number of bytes read.
+ *
+ * @since 1.0.0
+ */
+ public int getReadBytes();
+
+ /**
+ * Return whether the request timed out (as opposed to failing), or
+ * null if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public Boolean didTimeout();
+
+ /**
+ * Return the times in milliseconds since the epoch when {@code x%} of
+ * expected bytes were read for {@code 0 <= x <= 100}, or null if the
+ * torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public SortedMap<Integer, Long> getDataPercentiles();
+
+ /**
+ * Return the time in milliseconds since the epoch when the circuit was
+ * launched, or -1 if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public long getLaunchMillis();
+
+ /**
+ * Return the time in milliseconds since the epoch when the circuit was
+ * used, or -1 if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public long getUsedAtMillis();
+
+ /**
+ * Return a list of fingerprints of the relays in the circuit, or null
+ * if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public List<String> getPath();
+
+ /**
+ * Return a list of times in milliseconds since the epoch when circuit
+ * hops were built, or null if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public List<Long> getBuildTimes();
+
+ /**
+ * Return the circuit build timeout that the Tor client used when
+ * building this circuit, or -1 if the torperf line didn't contain that
+ * information.
+ *
+ * @since 1.0.0
+ */
+ public long getTimeout();
+
+ /**
+ * Return the circuit build time quantile that the Tor client uses to
+ * determine its circuit-build timeout, or -1 if the torperf line
+ * didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public double getQuantile();
+
+ /**
+ * Return the identifier of the circuit used for this measurement, or
+ * -1 if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public int getCircId();
+
+ /**
+ * Return the identifier of the stream used for this measurement, or -1
+ * if the torperf line didn't contain that information.
+ *
+ * @since 1.0.0
+ */
+ public int getUsedBy();
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
new file mode 100644
index 0000000..295e0a4
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
@@ -0,0 +1,100 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.BandwidthHistory;
+
+public class BandwidthHistoryImpl implements BandwidthHistory {
+
+ protected BandwidthHistoryImpl(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ boolean isValid = false;
+ this.line = line;
+ if (partsNoOpt.length == 5 || partsNoOpt.length == 6) {
+ try {
+ this.historyEndMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ if (partsNoOpt[3].startsWith("(") &&
+ partsNoOpt[4].startsWith("s)")) {
+ this.intervalLength = Long.parseLong(partsNoOpt[3].
+ substring(1));
+ if (this.intervalLength <= 0L) {
+ throw new DescriptorParseException("Only positive interval "
+ + "lengths are allowed in line '" + line + "'.");
+ }
+ String[] values = null;
+ if (partsNoOpt.length == 5 &&
+ partsNoOpt[4].equals("s)")) {
+ /* There are no bandwidth values to parse. */
+ isValid = true;
+ } else if (partsNoOpt.length == 6) {
+ /* There are bandwidth values to parse. */
+ values = partsNoOpt[5].split(",", -1);
+ } else if (partsNoOpt[4].length() > 2) {
+ /* There are bandwidth values to parse, but there is no space
+ * between "s)" and "0,0,0,0". Very old Tor versions around
+ * Tor 0.0.8 wrote such history lines, and even though
+ * dir-spec.txt implies a space here, the old format isn't
+ * totally broken. Let's pretend there's a space. */
+ values = partsNoOpt[4].substring(2).split(",", -1);
+ }
+ if (values != null) {
+ this.bandwidthValues = new long[values.length];
+ for (int i = values.length - 1; i >= 0; i--) {
+ long bandwidthValue = Long.parseLong(values[i]);
+ if (bandwidthValue < 0L) {
+ throw new DescriptorParseException("Negative bandwidth "
+ + "values are not allowed in line '" + line + "'.");
+ }
+ this.bandwidthValues[i] = bandwidthValue;
+ }
+ isValid = true;
+ }
+ }
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Invalid bandwidth-history line "
+ + "'" + line + "'.");
+ }
+ }
+
+ private String line;
+ @Override
+ public String getLine() {
+ return this.line;
+ }
+
+ private long historyEndMillis;
+ @Override
+ public long getHistoryEndMillis() {
+ return this.historyEndMillis;
+ }
+
+ private long intervalLength;
+ @Override
+ public long getIntervalLength() {
+ return this.intervalLength;
+ }
+
+ private long[] bandwidthValues;
+ @Override
+ public SortedMap<Long, Long> getBandwidthValues() {
+ SortedMap<Long, Long> result = new TreeMap<>();
+ if (this.bandwidthValues != null) {
+ long endMillis = this.historyEndMillis;
+ for (int i = this.bandwidthValues.length - 1; i >= 0; i--) {
+ result.put(endMillis, bandwidthValues[i]);
+ endMillis -= this.intervalLength * 1000L;
+ }
+ }
+ return result;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
new file mode 100644
index 0000000..66426d8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
@@ -0,0 +1,98 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+
+/* Provide an iterator for a queue of objects and block when there are
+ * currently no objects in the queue. Allow the producer to signal that
+ * there won't be further objects and unblock any waiting consumers. */
+public class BlockingIteratorImpl<T> implements Iterator<T> {
+
+ /* Queue containing produced elemnts waiting for consumers. */
+ private Queue<T> queue = new LinkedList<>();
+
+ /* Maximum number of elements in queue. */
+ private int maxQueueSize = 100;
+
+ /* Restrict object construction to the impl package. */
+ protected BlockingIteratorImpl() {
+ }
+
+ /* Create instance with maximum queue size. */
+ protected BlockingIteratorImpl(int maxQueueSize) {
+ this.maxQueueSize = maxQueueSize;
+ }
+
+ /* Add an object to the queue if there's still room. */
+ protected synchronized void add(T object) {
+ if (this.outOfDescriptors) {
+ throw new IllegalStateException("Internal error: Adding results to "
+ + "descriptor queue not allowed after sending end-of-stream "
+ + "object.");
+ }
+ while (this.queue.size() >= this.maxQueueSize) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ this.queue.offer(object);
+ notifyAll();
+ }
+
+ /* Signalize that there won't be any further objects to be enqueued. */
+ private boolean outOfDescriptors = false;
+ protected synchronized void setOutOfDescriptors() {
+ if (this.outOfDescriptors) {
+ throw new IllegalStateException("Internal error: Sending "
+ + "end-of-stream object only permitted once.");
+ }
+ this.outOfDescriptors = true;
+ notifyAll();
+ }
+
+ /* Return whether there are more objects. Block if there are currently
+ * no objects, but the producer hasn't signalized that there won't be
+ * further objects. */
+ @Override
+ public synchronized boolean hasNext() {
+ while (!this.outOfDescriptors && this.queue.isEmpty()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ return this.queue.peek() != null;
+ }
+
+ /* Return the next object in the queue or throw an exception when there
+ * are no further objects. Block if there are currently no objects, but
+ * the producer hasn't signalized that there won't be further
+ * objects. */
+ @Override
+ public synchronized T next() {
+ while (!this.outOfDescriptors && this.queue.isEmpty()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (this.queue.peek() == null) {
+ throw new NoSuchElementException();
+ }
+ notifyAll();
+ return this.queue.remove();
+ }
+
+ /* Don't support explicitly removing objects. They are removed
+ * anyway. */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..15d40d8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.BridgeExtraInfoDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+
+public class BridgeExtraInfoDescriptorImpl
+ extends ExtraInfoDescriptorImpl implements BridgeExtraInfoDescriptor {
+
+ protected static List<ExtraInfoDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "extra-info ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ExtraInfoDescriptor parsedDescriptor =
+ new BridgeExtraInfoDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgeExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
new file mode 100644
index 0000000..bf3804d
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -0,0 +1,230 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedMap;
+import java.util.TimeZone;
+
+import org.torproject.descriptor.BridgeNetworkStatus;
+
+/* Contains a bridge network status. */
+public class BridgeNetworkStatusImpl extends NetworkStatusImpl
+ implements BridgeNetworkStatus {
+
+ protected BridgeNetworkStatusImpl(byte[] statusBytes,
+ String fileName, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(statusBytes, failUnrecognizedDescriptorLines, false, false);
+ this.setPublishedMillisFromFileName(fileName);
+ }
+
+ private void setPublishedMillisFromFileName(String fileName)
+ throws DescriptorParseException {
+ if (this.publishedMillis != 0L) {
+ /* We already learned the publication timestamp from parsing the
+ * "published" line. */
+ return;
+ }
+ if (fileName.length() ==
+ "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D".
+ length()) {
+ String publishedString = fileName.substring(0,
+ "yyyyMMdd-HHmmss".length());
+ try {
+ SimpleDateFormat fileNameFormat = new SimpleDateFormat(
+ "yyyyMMdd-HHmmss");
+ fileNameFormat.setLenient(false);
+ fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.publishedMillis = fileNameFormat.parse(publishedString).
+ getTime();
+ } catch (ParseException e) {
+ }
+ }
+ if (this.publishedMillis == 0L) {
+ throw new DescriptorParseException("Unrecognized bridge network "
+ + "status file name '" + fileName + "'.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ /* Initialize flag-thresholds values here for the case that the status
+ * doesn't contain those values. Initializing them in the constructor
+ * or when declaring variables wouldn't work, because those parts are
+ * evaluated later and would overwrite everything we parse here. */
+ this.stableUptime = -1L;
+ this.stableMtbf = -1L;
+ this.fastBandwidth = -1L;
+ this.guardWfu = -1.0;
+ this.guardTk = -1L;
+ this.guardBandwidthIncludingExits = -1L;
+ this.guardBandwidthExcludingExits = -1L;
+ this.enoughMtbfInfo = -1;
+ this.ignoringAdvertisedBws = -1;
+
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in bridge network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFlagThresholdsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No flag thresholds in line '"
+ + line + "'.");
+ }
+ SortedMap<String, String> flagThresholds =
+ ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+ try {
+ for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+ switch (e.getKey()) {
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue().
+ replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
+ }
+ }
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+
+ protected void parseDirSource(byte[] dirSourceBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory source expected in "
+ + "bridge network status.");
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory footer expected in "
+ + "bridge network status.");
+ }
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory signature expected "
+ + "in bridge network status.");
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long stableUptime;
+ @Override
+ public long getStableUptime() {
+ return this.stableUptime;
+ }
+
+ private long stableMtbf;
+ @Override
+ public long getStableMtbf() {
+ return this.stableMtbf;
+ }
+
+ private long fastBandwidth;
+ @Override
+ public long getFastBandwidth() {
+ return this.fastBandwidth;
+ }
+
+ private double guardWfu;
+ @Override
+ public double getGuardWfu() {
+ return this.guardWfu;
+ }
+
+ private long guardTk;
+ @Override
+ public long getGuardTk() {
+ return this.guardTk;
+ }
+
+ private long guardBandwidthIncludingExits;
+ @Override
+ public long getGuardBandwidthIncludingExits() {
+ return this.guardBandwidthIncludingExits;
+ }
+
+ private long guardBandwidthExcludingExits;
+ @Override
+ public long getGuardBandwidthExcludingExits() {
+ return this.guardBandwidthExcludingExits;
+ }
+
+ private int enoughMtbfInfo;
+ @Override
+ public int getEnoughMtbfInfo() {
+ return this.enoughMtbfInfo;
+ }
+
+ private int ignoringAdvertisedBws;
+ @Override
+ public int getIgnoringAdvertisedBws() {
+ return this.ignoringAdvertisedBws;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
new file mode 100644
index 0000000..99578e8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
@@ -0,0 +1,99 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.BridgePoolAssignment;
+
+/* TODO Write a test class. */
+public class BridgePoolAssignmentImpl extends DescriptorImpl
+ implements BridgePoolAssignment {
+
+ protected static List<BridgePoolAssignment> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<BridgePoolAssignment> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "bridge-pool-assignment ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ BridgePoolAssignment parsedDescriptor =
+ new BridgePoolAssignmentImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgePoolAssignmentImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ 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();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("bridge-pool-assignment ")) {
+ this.parseBridgePoolAssignmentLine(line);
+ } else {
+ this.parseBridgeLine(line);
+ }
+ }
+ }
+
+ private void parseBridgePoolAssignmentLine(String line)
+ throws DescriptorParseException {
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in bridge pool assignment.");
+ }
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseBridgeLine(String line)
+ throws DescriptorParseException {
+ String[] parts = line.split("[ \t]+");
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in bridge pool assignment.");
+ }
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[0]);
+ String poolAndDetails = line.substring(line.indexOf(" ") + 1);
+ this.entries.put(fingerprint, poolAndDetails);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private SortedMap<String, String> entries = new TreeMap<>();
+ @Override
+ public SortedMap<String, String> getEntries() {
+ return new TreeMap<>(this.entries);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
new file mode 100644
index 0000000..eb2b933
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.BridgeServerDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ServerDescriptor;
+
+public class BridgeServerDescriptorImpl extends ServerDescriptorImpl
+ implements BridgeServerDescriptor {
+
+ protected static List<ServerDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "router ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ServerDescriptor parsedDescriptor =
+ new BridgeServerDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected BridgeServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
new file mode 100644
index 0000000..1a030ef
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
@@ -0,0 +1,249 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Stack;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+
+import org.torproject.descriptor.DescriptorCollector;
+
+public class DescriptorCollectorImpl implements DescriptorCollector {
+
+ @Override
+ public void collectDescriptors(String collecTorBaseUrl,
+ String[] remoteDirectories, long minLastModified,
+ File localDirectory, boolean deleteExtraneousLocalFiles) {
+ collecTorBaseUrl = collecTorBaseUrl.endsWith("/")
+ ? collecTorBaseUrl.substring(0, collecTorBaseUrl.length() - 1)
+ : collecTorBaseUrl;
+ if (minLastModified < 0) {
+ throw new IllegalArgumentException("A negative minimum "
+ + "last-modified time is not permitted.");
+ }
+ if (localDirectory.exists() && !localDirectory.isDirectory()) {
+ throw new IllegalArgumentException("Local directory already exists "
+ + "and is not a directory.");
+ }
+ SortedMap<String, Long> localFiles =
+ this.statLocalDirectory(localDirectory);
+ SortedMap<String, String> fetchedDirectoryListings =
+ this.fetchRemoteDirectories(collecTorBaseUrl, remoteDirectories);
+ SortedSet<String> parsedDirectories = new TreeSet<>();
+ SortedMap<String, Long> remoteFiles = new TreeMap<>();
+ for (Map.Entry<String, String> e :
+ fetchedDirectoryListings.entrySet()) {
+ String remoteDirectory = e.getKey();
+ String directoryListing = e.getValue();
+ SortedMap<String, Long> parsedRemoteFiles =
+ this.parseDirectoryListing(remoteDirectory, directoryListing);
+ if (parsedRemoteFiles == null) {
+ continue;
+ }
+ parsedDirectories.add(remoteDirectory);
+ remoteFiles.putAll(parsedRemoteFiles);
+ }
+ this.fetchRemoteFiles(collecTorBaseUrl, remoteFiles, minLastModified,
+ localDirectory, localFiles);
+ if (deleteExtraneousLocalFiles) {
+ this.deleteExtraneousLocalFiles(parsedDirectories, remoteFiles,
+ localDirectory, localFiles);
+ }
+ }
+
+ SortedMap<String, Long> statLocalDirectory(
+ File localDirectory) {
+ SortedMap<String, Long> localFiles = new TreeMap<>();
+ if (!localDirectory.exists()) {
+ return localFiles;
+ }
+ Stack<File> files = new Stack<>();
+ files.add(localDirectory);
+ while (!files.isEmpty()) {
+ File file = files.pop();
+ if (file.isDirectory()) {
+ files.addAll(Arrays.asList(file.listFiles()));
+ } else {
+ String localPath = file.getPath().substring(
+ localDirectory.getPath().length());
+ localFiles.put(localPath, file.lastModified());
+ }
+ }
+ return localFiles;
+ }
+
+ SortedMap<String, String> fetchRemoteDirectories(
+ String collecTorBaseUrl, String[] remoteDirectories) {
+ SortedMap<String, String> fetchedDirectoryListings = new TreeMap<>();
+ for (String remoteDirectory : remoteDirectories) {
+ String remoteDirectoryWithSlashAtBeginAndEnd =
+ (remoteDirectory.startsWith("/") ? "" : "/") + remoteDirectory
+ + (remoteDirectory.endsWith("/") ? "" : "/");
+ String directoryUrl = collecTorBaseUrl
+ + remoteDirectoryWithSlashAtBeginAndEnd;
+ String directoryListing = this.fetchRemoteDirectory(directoryUrl);
+ if (directoryListing.length() > 0) {
+ fetchedDirectoryListings.put(
+ remoteDirectoryWithSlashAtBeginAndEnd, directoryListing);
+ }
+ }
+ return fetchedDirectoryListings;
+ }
+
+ String fetchRemoteDirectory(String url) {
+ StringBuilder sb = new StringBuilder();
+ HttpURLConnection huc = null;
+ try {
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setRequestMethod("GET");
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ if (responseCode == 200) {
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ huc.getInputStream()));
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ br.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ if (huc != null) {
+ huc.disconnect();
+ }
+ return "";
+ }
+ return sb.toString();
+ }
+
+ final Pattern DIRECTORY_LISTING_LINE_PATTERN =
+ Pattern.compile(".* href=\"([^\"/]+)\"" /* filename */
+ + ".*>(\\d{2}-\\w{3}-\\d{4} \\d{2}:\\d{2})\\s*<.*"); /* dateTime */
+
+ SortedMap<String, Long> parseDirectoryListing(
+ String remoteDirectory, String directoryListing) {
+ SortedMap<String, Long> remoteFiles = new TreeMap<>();
+ DateFormat dateTimeFormat = ParseHelper.getDateFormat(
+ "dd-MMM-yyyy HH:mm");
+ try {
+ Scanner s = new Scanner(directoryListing);
+ s.useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ Matcher matcher = DIRECTORY_LISTING_LINE_PATTERN.matcher(line);
+ if (matcher.matches()) {
+ String filename = matcher.group(1);
+ long lastModifiedMillis = dateTimeFormat.parse(
+ matcher.group(2)).getTime();
+ remoteFiles.put(remoteDirectory + filename, lastModifiedMillis);
+ }
+ }
+ s.close();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ return null;
+ }
+ return remoteFiles;
+ }
+
+ void fetchRemoteFiles(String collecTorBaseUrl,
+ SortedMap<String, Long> remoteFiles, long minLastModified,
+ File localDirectory, SortedMap<String, Long> localFiles) {
+ for (Map.Entry<String, Long> e : remoteFiles.entrySet()) {
+ String filename = e.getKey();
+ long lastModifiedMillis = e.getValue();
+ if (lastModifiedMillis < minLastModified ||
+ (localFiles.containsKey(filename) &&
+ localFiles.get(filename) >= lastModifiedMillis)) {
+ continue;
+ }
+ String url = collecTorBaseUrl + filename;
+ File destinationFile = new File(localDirectory.getPath()
+ + filename);
+ this.fetchRemoteFile(url, destinationFile, lastModifiedMillis);
+ }
+ }
+
+ void fetchRemoteFile(String url, File destinationFile,
+ long lastModifiedMillis) {
+ HttpURLConnection huc = null;
+ try {
+ File destinationDirectory = destinationFile.getParentFile();
+ destinationDirectory.mkdirs();
+ File tempDestinationFile = new File(destinationDirectory, "."
+ + destinationFile.getName());
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(tempDestinationFile));
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setRequestMethod("GET");
+ if (!url.endsWith(".xz")) {
+ huc.addRequestProperty("Accept-Encoding", "gzip");
+ }
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ if (responseCode == 200) {
+ InputStream is;
+ if (huc.getContentEncoding() != null &&
+ huc.getContentEncoding().equalsIgnoreCase("gzip")) {
+ is = new GZIPInputStream(huc.getInputStream());
+ } else {
+ is = huc.getInputStream();
+ }
+ BufferedInputStream bis = new BufferedInputStream(is);
+ int len;
+ byte[] data = new byte[8192];
+ while ((len = bis.read(data, 0, 8192)) >= 0) {
+ bos.write(data, 0, len);
+ }
+ bis.close();
+ bos.close();
+ tempDestinationFile.renameTo(destinationFile);
+ destinationFile.setLastModified(lastModifiedMillis);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ if (huc != null) {
+ huc.disconnect();
+ }
+ }
+ }
+
+ void deleteExtraneousLocalFiles(
+ SortedSet<String> parsedDirectories,
+ SortedMap<String, Long> remoteFiles, File localDirectory,
+ SortedMap<String, Long> localFiles) {
+ for (String localPath : localFiles.keySet()) {
+ for (String remoteDirectory : parsedDirectories) {
+ if (localPath.startsWith(remoteDirectory)) {
+ if (!remoteFiles.containsKey(localPath)) {
+ new File(localDirectory.getPath() + localPath).delete();
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
new file mode 100644
index 0000000..e726ce9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
@@ -0,0 +1,283 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.DescriptorRequest;
+import org.torproject.descriptor.DescriptorDownloader;
+
+public class DescriptorDownloaderImpl
+ implements DescriptorDownloader {
+
+ private boolean hasStartedDownloading = false;
+
+ private SortedMap<String, DirectoryDownloader> directoryAuthorities =
+ new TreeMap<>();
+ @Override
+ public void addDirectoryAuthority(String nickname, String ip,
+ int dirPort) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkDirectoryParameters(nickname, ip, dirPort);
+ DirectoryDownloader directoryAuthority = new DirectoryDownloader(
+ nickname, ip, dirPort);
+ this.directoryAuthorities.put(nickname, directoryAuthority);
+ }
+
+ private SortedMap<String, DirectoryDownloader> directoryMirrors =
+ new TreeMap<>();
+ @Override
+ public void addDirectoryMirror(String nickname, String ip,
+ int dirPort) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkDirectoryParameters(nickname, ip, dirPort);
+ DirectoryDownloader directoryMirror = new DirectoryDownloader(
+ nickname, ip, dirPort);
+ this.directoryMirrors.put(nickname, directoryMirror);
+ /* TODO Implement prioritizing mirrors for non-vote downloads. */
+ throw new UnsupportedOperationException("Prioritizing directory "
+ + "mirrors over directory authorities is not implemented yet. "
+ + "Until it is, configuring directory mirrors is misleading and "
+ + "therefore not supported.");
+ }
+
+ private void checkDirectoryParameters(String nickname, String ip,
+ int dirPort) {
+ if (nickname == null || nickname.length() < 1) {
+ throw new IllegalArgumentException("'" + nickname + "' is not a "
+ + "valid nickname.");
+ }
+ if (ip == null || ip.length() < 7 || ip.split("\\.").length != 4) {
+ throw new IllegalArgumentException("'" + ip + "' is not a valid IP "
+ + "address.");
+ }
+ if (dirPort < 1 || dirPort > 65535) {
+ throw new IllegalArgumentException(String.valueOf(dirPort) + " is "
+ + "not a valid DirPort.");
+ }
+ /* TODO Relax the requirement for directory nicknames to be unique.
+ * In theory, we can identify them by ip+port. */
+ if (this.directoryAuthorities.containsKey(nickname) ||
+ this.directoryMirrors.containsKey(nickname)) {
+ throw new IllegalArgumentException("Directory nicknames must be "
+ + "unique.");
+ }
+ }
+
+ private boolean downloadConsensus = false;
+ @Override
+ public void setIncludeCurrentConsensus() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.downloadConsensus = true;
+ }
+
+ private boolean downloadConsensusFromAllAuthorities = false;
+ @Override
+ public void setIncludeCurrentConsensusFromAllDirectoryAuthorities() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.downloadConsensusFromAllAuthorities = true;
+ }
+
+ private boolean includeCurrentReferencedVotes = false;
+ @Override
+ public void setIncludeCurrentReferencedVotes() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.includeCurrentReferencedVotes = true;
+ }
+
+ private Set<String> downloadVotes = new HashSet<>();
+ @Override
+ public void setIncludeCurrentVote(String fingerprint) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.checkVoteFingerprint(fingerprint);
+ this.downloadVotes.add(fingerprint);
+ }
+
+ @Override
+ public void setIncludeCurrentVotes(Set<String> fingerprints) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (fingerprints == null) {
+ throw new IllegalArgumentException("Set of fingerprints must not "
+ + "be null.");
+ }
+ for (String fingerprint : fingerprints) {
+ this.checkVoteFingerprint(fingerprint);
+ }
+ for (String fingerprint : fingerprints) {
+ this.setIncludeCurrentVote(fingerprint);
+ }
+ }
+
+ private void checkVoteFingerprint(String fingerprint) {
+ if (fingerprint == null || fingerprint.length() != 40) {
+ throw new IllegalArgumentException("'" + fingerprint + "' is not a "
+ + "valid fingerprint.");
+ }
+ }
+
+ @Override
+ public void setIncludeReferencedServerDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeServerDescriptor(String identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeServerDescriptors(Set<String> identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading server "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setIncludeReferencedExtraInfoDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeExtraInfoDescriptor(String identifier) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ @Override
+ public void setExcludeExtraInfoDescriptors(Set<String> identifiers) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ /* TODO Implement me. */
+ throw new UnsupportedOperationException("Downloading extra-info "
+ + "descriptors is not implemented yet.");
+ }
+
+ private long readTimeoutMillis = 60L * 1000L;
+ @Override
+ public void setReadTimeout(long readTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (readTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Read timeout value "
+ + String.valueOf(readTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.readTimeoutMillis = readTimeoutMillis;
+ }
+
+ private long connectTimeoutMillis = 60L * 1000L;
+ @Override
+ public void setConnectTimeout(long connectTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (connectTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Connect timeout value "
+ + String.valueOf(connectTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.connectTimeoutMillis = connectTimeoutMillis;
+ }
+
+ private long globalTimeoutMillis = 60L * 60L * 1000L;
+ @Override
+ public void setGlobalTimeout(long globalTimeoutMillis) {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ if (globalTimeoutMillis < 0L) {
+ throw new IllegalArgumentException("Global timeout value "
+ + String.valueOf(globalTimeoutMillis) + " may not be "
+ + "negative.");
+ }
+ this.globalTimeoutMillis = globalTimeoutMillis;
+ }
+
+ private boolean failUnrecognizedDescriptorLines = false;
+ @Override
+ public void setFailUnrecognizedDescriptorLines() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to download.");
+ }
+ this.failUnrecognizedDescriptorLines = true;
+ }
+
+ @Override
+ public Iterator<DescriptorRequest> downloadDescriptors() {
+ if (this.hasStartedDownloading) {
+ throw new IllegalStateException("Initiating downloads is only "
+ + "permitted once.");
+ }
+ this.hasStartedDownloading = true;
+ DownloadCoordinatorImpl downloadCoordinator =
+ new DownloadCoordinatorImpl(this.directoryAuthorities,
+ this.directoryMirrors, this.downloadConsensus,
+ this.downloadConsensusFromAllAuthorities, this.downloadVotes,
+ this.includeCurrentReferencedVotes, this.connectTimeoutMillis,
+ this.readTimeoutMillis, this.globalTimeoutMillis,
+ this.failUnrecognizedDescriptorLines);
+ Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator.
+ getDescriptorQueue();
+ return descriptorQueue;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
new file mode 100644
index 0000000..801c546
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
@@ -0,0 +1,78 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+
+public class DescriptorFileImpl implements DescriptorFile {
+
+ private File directory;
+ protected void setDirectory(File directory) {
+ this.directory = directory;
+ }
+ @Override
+ public File getDirectory() {
+ return this.directory;
+ }
+
+ private File tarball;
+ protected void setTarball(File tarball) {
+ this.tarball = tarball;
+ }
+ @Override
+ public File getTarball() {
+ return this.tarball;
+ }
+
+ private File file;
+ protected void setFile(File file) {
+ this.file = file;
+ }
+ @Override
+ public File getFile() {
+ return this.file;
+ }
+
+ private String fileName;
+ protected void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+ @Override
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ private long lastModified;
+ protected void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+ @Override
+ public long getLastModified() {
+ return this.lastModified;
+ }
+
+ private List<Descriptor> descriptors;
+ protected void setDescriptors(List<Descriptor> descriptors) {
+ this.descriptors = descriptors;
+ }
+ @Override
+ public List<Descriptor> getDescriptors() {
+ return this.descriptors == null ? new ArrayList<Descriptor>() :
+ new ArrayList<>(this.descriptors);
+ }
+
+ private Exception exception;
+ protected void setException(Exception exception) {
+ this.exception = exception;
+ }
+ @Override
+ public Exception getException() {
+ return this.exception;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
new file mode 100644
index 0000000..5625b3f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -0,0 +1,337 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+
+import org.torproject.descriptor.Descriptor;
+
+public abstract class DescriptorImpl implements Descriptor {
+
+ protected static List<Descriptor> parseDescriptors(
+ byte[] rawDescriptorBytes, String fileName,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<Descriptor> parsedDescriptors = new ArrayList<>();
+ if (rawDescriptorBytes == null) {
+ return parsedDescriptors;
+ }
+ byte[] first100Chars = new byte[Math.min(100,
+ rawDescriptorBytes.length)];
+ System.arraycopy(rawDescriptorBytes, 0, first100Chars, 0,
+ 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"))) {
+ 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"))) {
+ parsedDescriptors.addAll(RelayNetworkStatusVoteImpl.
+ parseVotes(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-network-status 1.")
+ || firstLines.startsWith("r ")) {
+ parsedDescriptors.add(new BridgeNetworkStatusImpl(
+ rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines));
+ } 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 ")) {
+ parsedDescriptors.addAll(RelayServerDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-extra-info 1.")) {
+ parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type extra-info 1.") ||
+ firstLines.startsWith("extra-info ") ||
+ firstLines.contains("\nextra-info ")) {
+ parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type microdescriptor 1.") ||
+ firstLines.startsWith("onion-key\n") ||
+ firstLines.contains("\nonion-key\n")) {
+ parsedDescriptors.addAll(MicrodescriptorImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type bridge-pool-assignment 1.") ||
+ firstLines.startsWith("bridge-pool-assignment ") ||
+ firstLines.contains("\nbridge-pool-assignment ")) {
+ 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 ")) {
+ parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
+ parseDescriptors(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type tordnsel 1.") ||
+ firstLines.startsWith("ExitNode ") ||
+ firstLines.contains("\nExitNode ")) {
+ 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")) {
+ parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type directory 1.") ||
+ firstLines.startsWith("signed-directory\n") ||
+ firstLines.contains("\nsigned-directory\n")) {
+ parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes,
+ failUnrecognizedDescriptorLines));
+ } else if (firstLines.startsWith("@type torperf 1.")) {
+ parsedDescriptors.addAll(TorperfResultImpl.parseTorperfResults(
+ rawDescriptorBytes, failUnrecognizedDescriptorLines));
+ } else {
+ throw new DescriptorParseException("Could not detect descriptor "
+ + "type in descriptor starting with '" + firstLines + "'.");
+ }
+ return parsedDescriptors;
+ }
+
+ protected static List<byte[]> splitRawDescriptorBytes(
+ byte[] rawDescriptorBytes, String startToken) {
+ List<byte[]> rawDescriptors = new ArrayList<>();
+ String splitToken = "\n" + startToken;
+ String ascii;
+ try {
+ ascii = new String(rawDescriptorBytes, "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ return rawDescriptors;
+ }
+ int endAllDescriptors = rawDescriptorBytes.length,
+ startAnnotations = 0;
+ boolean containsAnnotations = ascii.startsWith("@") ||
+ ascii.contains("\n@");
+ while (startAnnotations < endAllDescriptors) {
+ int startDescriptor;
+ if (ascii.indexOf(startToken, startAnnotations) == 0) {
+ startDescriptor = startAnnotations;
+ } else {
+ startDescriptor = ascii.indexOf(splitToken, startAnnotations - 1);
+ if (startDescriptor < 0) {
+ break;
+ } else {
+ startDescriptor += 1;
+ }
+ }
+ int endDescriptor = -1;
+ if (containsAnnotations) {
+ endDescriptor = ascii.indexOf("\n@", startDescriptor);
+ }
+ if (endDescriptor < 0) {
+ endDescriptor = ascii.indexOf(splitToken, startDescriptor);
+ }
+ if (endDescriptor < 0) {
+ endDescriptor = endAllDescriptors - 1;
+ }
+ endDescriptor += 1;
+ byte[] rawDescriptor = new byte[endDescriptor - startAnnotations];
+ System.arraycopy(rawDescriptorBytes, startAnnotations,
+ rawDescriptor, 0, endDescriptor - startAnnotations);
+ startAnnotations = endDescriptor;
+ rawDescriptors.add(rawDescriptor);
+ }
+ return rawDescriptors;
+ }
+
+ protected byte[] rawDescriptorBytes;
+ @Override
+ public byte[] getRawDescriptorBytes() {
+ return this.rawDescriptorBytes;
+ }
+
+ protected boolean failUnrecognizedDescriptorLines = false;
+
+ protected List<String> unrecognizedLines;
+ @Override
+ public List<String> getUnrecognizedLines() {
+ return this.unrecognizedLines == null ? new ArrayList<String>() :
+ new ArrayList<>(this.unrecognizedLines);
+ }
+
+ protected DescriptorImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines, boolean blankLinesAllowed)
+ throws DescriptorParseException {
+ this.rawDescriptorBytes = rawDescriptorBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.cutOffAnnotations(rawDescriptorBytes);
+ this.countKeywords(rawDescriptorBytes, blankLinesAllowed);
+ }
+
+ /* Parse annotation lines from the descriptor bytes. */
+ private List<String> annotations = new ArrayList<>();
+ private void cutOffAnnotations(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ 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);
+ if (end < 0) {
+ throw new DescriptorParseException("Annotation line does not "
+ + "contain a newline.");
+ }
+ this.annotations.add(ascii.substring(start, end));
+ start = end + 1;
+ }
+ if (start > 0) {
+ int length = rawDescriptorBytes.length;
+ byte[] rawDescriptor = new byte[length - start];
+ System.arraycopy(rawDescriptorBytes, start, rawDescriptor, 0,
+ length - start);
+ this.rawDescriptorBytes = rawDescriptor;
+ }
+ }
+ @Override
+ public List<String> getAnnotations() {
+ return new ArrayList<>(this.annotations);
+ }
+
+ /* Count parsed keywords for consistency checks by subclasses. */
+ private String firstKeyword, lastKeyword;
+ private Map<String, Integer> parsedKeywords = new HashMap<>();
+ private void countKeywords(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"))) {
+ throw new DescriptorParseException("Blank lines are not allowed.");
+ }
+ boolean skipCrypto = false;
+ Scanner s = new Scanner(descriptorString).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("-----BEGIN")) {
+ skipCrypto = true;
+ } else if (line.startsWith("-----END")) {
+ 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];
+ if (keyword.equals("")) {
+ throw new DescriptorParseException("Illegal keyword in line '"
+ + line + "'.");
+ }
+ if (this.firstKeyword == null) {
+ this.firstKeyword = keyword;
+ }
+ lastKeyword = keyword;
+ if (parsedKeywords.containsKey(keyword)) {
+ parsedKeywords.put(keyword, parsedKeywords.get(keyword) + 1);
+ } else {
+ parsedKeywords.put(keyword, 1);
+ }
+ }
+ }
+ }
+
+ protected void checkFirstKeyword(String keyword)
+ throws DescriptorParseException {
+ if (this.firstKeyword == null ||
+ !this.firstKeyword.equals(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ + "be contained in the first line.");
+ }
+ }
+
+ protected void checkLastKeyword(String keyword)
+ throws DescriptorParseException {
+ if (this.lastKeyword == null ||
+ !this.lastKeyword.equals(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' must "
+ + "be contained in the last line.");
+ }
+ }
+
+ protected void checkExactlyOnceKeywords(Set<String> keywords)
+ throws DescriptorParseException {
+ for (String keyword : keywords) {
+ int contained = 0;
+ if (this.parsedKeywords.containsKey(keyword)) {
+ contained = this.parsedKeywords.get(keyword);
+ }
+ if (contained != 1) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ + "contained " + contained + " times, but must be contained "
+ + "exactly once.");
+ }
+ }
+ }
+
+ protected void checkAtLeastOnceKeywords(Set<String> keywords)
+ throws DescriptorParseException {
+ for (String keyword : keywords) {
+ if (!this.parsedKeywords.containsKey(keyword)) {
+ throw new DescriptorParseException("Keyword '" + keyword + "' is "
+ + "contained 0 times, but must be contained at least once.");
+ }
+ }
+ }
+
+ protected void checkAtMostOnceKeywords(Set<String> keywords)
+ 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, "
+ + "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 "
+ + "not.");
+ }
+ }
+ }
+
+ protected int getKeywordCount(String keyword) {
+ if (!this.parsedKeywords.containsKey(keyword)) {
+ return 0;
+ } else {
+ return this.parsedKeywords.get(keyword);
+ }
+ }
+
+ protected void clearParsedKeywords() {
+ this.parsedKeywords = null;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
new file mode 100644
index 0000000..0f9add2
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
@@ -0,0 +1,15 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+/**
+ * @deprecated Replaced by
+ * org.torproject.descriptor.DescriptorParseException
+ */
+@Deprecated public class DescriptorParseException extends Exception {
+ private static final long serialVersionUID = 100L;
+ protected DescriptorParseException(String message) {
+ super(message);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
new file mode 100644
index 0000000..6ac53f8
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
@@ -0,0 +1,28 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorParser;
+
+public class DescriptorParserImpl implements DescriptorParser {
+
+ private boolean failUnrecognizedDescriptorLines;
+
+ @Override
+ public void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines) {
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ }
+
+ @Override
+ public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
+ String fileName) throws DescriptorParseException {
+ return DescriptorImpl.parseDescriptors(rawDescriptorBytes, fileName,
+ this.failUnrecognizedDescriptorLines);
+ }
+}
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
new file mode 100644
index 0000000..8da88e9
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
@@ -0,0 +1,364 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorReader;
+
+public class DescriptorReaderImpl implements DescriptorReader {
+
+ private boolean hasStartedReading = false;
+
+ private List<File> directories = new ArrayList<>();
+ @Override
+ public void addDirectory(File directory) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.directories.add(directory);
+ }
+
+ private List<File> tarballs = new ArrayList<>();
+ @Override
+ public void addTarball(File tarball) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.tarballs.add(tarball);
+ }
+
+ private File historyFile;
+ @Override
+ public void setExcludeFiles(File historyFile) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.historyFile = historyFile;
+ }
+
+ private SortedMap<String, Long> excludedFiles;
+ @Override
+ public void setExcludedFiles(SortedMap<String, Long> excludedFiles) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.excludedFiles = excludedFiles;
+ }
+
+ @Override
+ public SortedMap<String, Long> getExcludedFiles() {
+ if (this.reader == null || !this.reader.hasFinishedReading) {
+ throw new IllegalStateException("Operation is not permitted before "
+ + "finishing to read.");
+ }
+ return new TreeMap<>(this.reader.excludedFilesAfter);
+ }
+
+ @Override
+ public SortedMap<String, Long> getParsedFiles() {
+ if (this.reader == null || !this.reader.hasFinishedReading) {
+ throw new IllegalStateException("Operation is not permitted before "
+ + "finishing to read.");
+ }
+ return new TreeMap<>(this.reader.parsedFilesAfter);
+ }
+
+ private boolean failUnrecognizedDescriptorLines = false;
+ @Override
+ public void setFailUnrecognizedDescriptorLines() {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.failUnrecognizedDescriptorLines = true;
+ }
+
+ private Integer maxDescriptorFilesInQueue = null;
+ @Override
+ public void setMaxDescriptorFilesInQueue(int max) {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Reconfiguration is not permitted "
+ + "after starting to read.");
+ }
+ this.maxDescriptorFilesInQueue = max;
+ }
+
+ private DescriptorReaderRunnable reader;
+ @Override
+ public Iterator<DescriptorFile> readDescriptors() {
+ if (this.hasStartedReading) {
+ throw new IllegalStateException("Initiating reading is only "
+ + "permitted once.");
+ }
+ this.hasStartedReading = true;
+ BlockingIteratorImpl<DescriptorFile> descriptorQueue =
+ this.maxDescriptorFilesInQueue == null
+ ? new BlockingIteratorImpl<DescriptorFile>()
+ : new BlockingIteratorImpl<DescriptorFile>(
+ this.maxDescriptorFilesInQueue);
+ this.reader = new DescriptorReaderRunnable(this.directories,
+ this.tarballs, descriptorQueue, this.historyFile,
+ this.excludedFiles, this.failUnrecognizedDescriptorLines);
+ new Thread(this.reader).start();
+ return descriptorQueue;
+ }
+
+ private static class DescriptorReaderRunnable implements Runnable {
+ private List<File> directories;
+ private List<File> tarballs;
+ private BlockingIteratorImpl<DescriptorFile> descriptorQueue;
+ private File historyFile;
+ private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>(),
+ excludedFilesAfter = new TreeMap<>(),
+ parsedFilesAfter = new TreeMap<>();
+ private DescriptorParser descriptorParser;
+ private boolean hasFinishedReading = false;
+ private DescriptorReaderRunnable(List<File> directories,
+ List<File> tarballs,
+ BlockingIteratorImpl<DescriptorFile> descriptorQueue,
+ File historyFile, SortedMap<String, Long> excludedFiles,
+ boolean failUnrecognizedDescriptorLines) {
+ this.directories = directories;
+ this.tarballs = tarballs;
+ this.descriptorQueue = descriptorQueue;
+ this.historyFile = historyFile;
+ if (excludedFiles != null) {
+ this.excludedFilesBefore = excludedFiles;
+ }
+ this.descriptorParser = new DescriptorParserImpl();
+ this.descriptorParser.setFailUnrecognizedDescriptorLines(
+ failUnrecognizedDescriptorLines);
+ }
+ public void run() {
+ try {
+ this.readOldHistory();
+ this.readDescriptors();
+ this.readTarballs();
+ this.hasFinishedReading = true;
+ } catch (Throwable t) {
+ /* We're usually not writing to stdout or stderr, but we shouldn't
+ * stay quiet about this potential bug. If we were to switch to a
+ * logging API, this would qualify as ERROR. */
+ System.err.println("Bug: uncaught exception or error while "
+ + "reading descriptors:");
+ t.printStackTrace();
+ } finally {
+ this.descriptorQueue.setOutOfDescriptors();
+ }
+ if (this.hasFinishedReading) {
+ this.writeNewHistory();
+ }
+ }
+ private void readOldHistory() {
+ if (this.historyFile == null) {
+ return;
+ }
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(
+ this.historyFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (!line.contains(" ")) {
+ /* TODO Handle this problem? */
+ continue;
+ }
+ long lastModifiedMillis = Long.parseLong(line.substring(0,
+ line.indexOf(" ")));
+ String absolutePath = line.substring(line.indexOf(" ") + 1);
+ this.excludedFilesBefore.put(absolutePath, lastModifiedMillis);
+ }
+ br.close();
+ } catch (IOException e) {
+ /* TODO Handle this exception. */
+ } catch (NumberFormatException e) {
+ /* TODO Handle this exception. */
+ }
+ }
+ private void writeNewHistory() {
+ if (this.historyFile == null) {
+ return;
+ }
+ try {
+ if (this.historyFile.getParentFile() != null) {
+ this.historyFile.getParentFile().mkdirs();
+ }
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ this.historyFile));
+ SortedMap<String, Long> newHistory = new TreeMap<>();
+ newHistory.putAll(this.excludedFilesAfter);
+ newHistory.putAll(this.parsedFilesAfter);
+ for (Map.Entry<String, Long> e : newHistory.entrySet()) {
+ String absolutePath = e.getKey();
+ long lastModifiedMillis = e.getValue();
+ bw.write(String.valueOf(lastModifiedMillis) + " " + absolutePath
+ + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ /* TODO Handle this exception. */
+ }
+ }
+ private void readDescriptors() {
+ for (File directory : this.directories) {
+ if (!directory.exists() || !directory.isDirectory()) {
+ continue;
+ }
+ Stack<File> files = new Stack<>();
+ files.add(directory);
+ boolean abortReading = false;
+ while (!abortReading && !files.isEmpty()) {
+ File file = files.pop();
+ if (file.isDirectory()) {
+ files.addAll(Arrays.asList(file.listFiles()));
+ } else if (file.getName().endsWith(".tar") ||
+ file.getName().endsWith(".tar.bz2") ||
+ file.getName().endsWith(".tar.xz")) {
+ this.tarballs.add(file);
+ } else {
+ String absolutePath = file.getAbsolutePath();
+ long lastModifiedMillis = file.lastModified();
+ if (this.excludedFilesBefore.containsKey(absolutePath) &&
+ this.excludedFilesBefore.get(absolutePath) ==
+ lastModifiedMillis) {
+ this.excludedFilesAfter.put(absolutePath,
+ lastModifiedMillis);
+ continue;
+ }
+ this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
+ DescriptorFileImpl descriptorFile = new DescriptorFileImpl();
+ try {
+ descriptorFile.setDirectory(directory);
+ descriptorFile.setFile(file);
+ descriptorFile.setFileName(file.getAbsolutePath());
+ descriptorFile.setLastModified(lastModifiedMillis);
+ descriptorFile.setDescriptors(this.readFile(file));
+ } catch (DescriptorParseException e) {
+ descriptorFile.setException(e);
+ } catch (IOException e) {
+ descriptorFile.setException(e);
+ abortReading = true;
+ }
+ this.descriptorQueue.add(descriptorFile);
+ }
+ }
+ }
+ }
+ private void readTarballs() {
+ List<File> files = new ArrayList<>(this.tarballs);
+ boolean abortReading = false;
+ while (!abortReading && !files.isEmpty()) {
+ File tarball = files.remove(0);
+ if (!tarball.getName().endsWith(".tar") &&
+ !tarball.getName().endsWith(".tar.bz2") &&
+ !tarball.getName().endsWith(".tar.xz")) {
+ continue;
+ }
+ String absolutePath = tarball.getAbsolutePath();
+ long lastModifiedMillis = tarball.lastModified();
+ if (this.excludedFilesBefore.containsKey(absolutePath) &&
+ this.excludedFilesBefore.get(absolutePath) ==
+ lastModifiedMillis) {
+ this.excludedFilesAfter.put(absolutePath, lastModifiedMillis);
+ continue;
+ }
+ this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
+ try {
+ FileInputStream in = new FileInputStream(tarball);
+ if (in.available() > 0) {
+ TarArchiveInputStream tais = null;
+ if (tarball.getName().endsWith(".tar.bz2")) {
+ tais = new TarArchiveInputStream(
+ new BZip2CompressorInputStream(in));
+ } else if (tarball.getName().endsWith(".tar.xz")) {
+ tais = new TarArchiveInputStream(
+ new XZCompressorInputStream(in));
+ } else if (tarball.getName().endsWith(".tar")) {
+ tais = new TarArchiveInputStream(in);
+ }
+ BufferedInputStream bis = new BufferedInputStream(tais);
+ TarArchiveEntry tae = null;
+ while ((tae = tais.getNextTarEntry()) != null) {
+ if (tae.isDirectory()) {
+ continue;
+ }
+ DescriptorFileImpl descriptorFile =
+ new DescriptorFileImpl();
+ descriptorFile.setTarball(tarball);
+ descriptorFile.setFileName(tae.getName());
+ descriptorFile.setLastModified(tae.getLastModifiedDate().
+ getTime());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[1024];
+ while ((len = bis.read(data, 0, 1024)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ byte[] rawDescriptorBytes = baos.toByteArray();
+ if (rawDescriptorBytes.length < 1) {
+ continue;
+ }
+ try {
+ String fileName = tae.getName().substring(
+ tae.getName().lastIndexOf("/") + 1);
+ List<Descriptor> parsedDescriptors =
+ this.descriptorParser.parseDescriptors(
+ rawDescriptorBytes, fileName);
+ descriptorFile.setDescriptors(parsedDescriptors);
+ } catch (DescriptorParseException e) {
+ descriptorFile.setException(e);
+ }
+ this.descriptorQueue.add(descriptorFile);
+ }
+ }
+ } catch (IOException e) {
+ abortReading = true;
+ }
+ }
+ }
+ private List<Descriptor> readFile(File file) throws IOException,
+ DescriptorParseException {
+ FileInputStream fis = new FileInputStream(file);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[1024];
+ while ((len = bis.read(data, 0, 1024)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ bis.close();
+ byte[] rawDescriptorBytes = baos.toByteArray();
+ return this.descriptorParser.parseDescriptors(rawDescriptorBytes,
+ file.getName());
+ }
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
new file mode 100644
index 0000000..0238f24
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
@@ -0,0 +1,114 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.List;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorRequest;
+
+public class DescriptorRequestImpl implements DescriptorRequest {
+
+ private String requestedResource;
+ protected void setRequestedResource(String requestedResource) {
+ this.requestedResource = requestedResource;
+ }
+ protected String getRequestedResource() {
+ return this.requestedResource;
+ }
+
+ private String descriptorType;
+ protected void setDescriptorType(String descriptorType) {
+ this.descriptorType = descriptorType;
+ }
+ protected String getDescriptorType() {
+ return this.descriptorType;
+ }
+
+ private byte[] responseBytes;
+ protected byte[] getResponseBytes() {
+ return this.responseBytes;
+ }
+ protected void setResponseBytes(byte[] responseBytes) {
+ this.responseBytes = responseBytes;
+ }
+
+ private String requestUrl;
+ @Override
+ public String getRequestUrl() {
+ return this.requestUrl;
+ }
+
+ private String directoryNickname;
+ protected void setDirectoryNickname(String directoryNickname) {
+ this.directoryNickname = directoryNickname;
+ }
+ @Override
+ public String getDirectoryNickname() {
+ return this.directoryNickname;
+ }
+
+ private int responseCode;
+ protected void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+ @Override
+ public int getResponseCode() {
+ return this.responseCode;
+ }
+
+ private long requestStart;
+ protected void setRequestStart(long requestStart) {
+ this.requestStart = requestStart;
+ }
+ @Override
+ public long getRequestStart() {
+ return this.requestStart;
+ }
+
+ private long requestEnd;
+ protected void setRequestEnd(long requestEnd) {
+ this.requestEnd = requestEnd;
+ }
+ @Override
+ public long getRequestEnd() {
+ return this.requestEnd;
+ }
+
+ private boolean connectTimeoutHasExpired;
+ @Override
+ public boolean connectTimeoutHasExpired() {
+ return this.connectTimeoutHasExpired;
+ }
+
+ private boolean readTimeoutHasExpired;
+ @Override
+ public boolean readTimeoutHasExpired() {
+ return this.readTimeoutHasExpired;
+ }
+
+ private boolean globalTimeoutHasExpired;
+ @Override
+ public boolean globalTimeoutHasExpired() {
+ return this.globalTimeoutHasExpired;
+ }
+
+ private List<Descriptor> descriptors;
+ protected void setDescriptors(List<Descriptor> descriptors) {
+ this.descriptors = descriptors;
+ }
+ @Override
+ public List<Descriptor> getDescriptors() {
+ return this.descriptors;
+ }
+
+ private Exception exception;
+ protected void setException(Exception exception) {
+ this.exception = exception;
+ }
+ @Override
+ public Exception getException() {
+ return this.exception;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
new file mode 100644
index 0000000..fb2f5ad
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
@@ -0,0 +1,218 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.DirSourceEntry;
+
+public class DirSourceEntryImpl implements DirSourceEntry {
+
+ private byte[] dirSourceEntryBytes;
+ @Override
+ public byte[] getDirSourceEntryBytes() {
+ return this.dirSourceEntryBytes;
+ }
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected DirSourceEntryImpl(byte[] dirSourceEntryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.dirSourceEntryBytes = dirSourceEntryBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseDirSourceEntryBytes();
+ this.checkAndClearKeywords();
+ }
+
+ private SortedSet<String> exactlyOnceKeywords, atMostOnceKeywords;
+ 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 void parsedExactlyOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.exactlyOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in dir-source.");
+ }
+ this.exactlyOnceKeywords.remove(keyword);
+ }
+
+ private void parsedAtMostOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.atMostOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate " + keyword + "line "
+ + "in dir-source.");
+ }
+ this.atMostOnceKeywords.remove(keyword);
+ }
+
+ private void checkAndClearKeywords() throws DescriptorParseException {
+ if (!this.exactlyOnceKeywords.isEmpty()) {
+ throw new DescriptorParseException("dir-source does not contain a '"
+ + this.exactlyOnceKeywords.first() + "' line.");
+ }
+ this.exactlyOnceKeywords = null;
+ this.atMostOnceKeywords = null;
+ }
+
+ private void parseDirSourceEntryBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.dirSourceEntryBytes)).
+ useDelimiter("\n");
+ boolean skipCrypto = false;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ");
+ switch (parts[0]) {
+ case "dir-source":
+ this.parseDirSourceLine(line);
+ break;
+ case "contact":
+ this.parseContactLine(line);
+ break;
+ case "vote-digest":
+ this.parseVoteDigestLine(line);
+ break;
+ case "-----BEGIN":
+ skipCrypto = true;
+ break;
+ case "-----END":
+ skipCrypto = false;
+ break;
+ default:
+ if (!skipCrypto) {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseDirSourceLine(String line)
+ throws DescriptorParseException {
+ this.parsedExactlyOnceKeyword("dir-source");
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 7) {
+ throw new DescriptorParseException("Invalid line '" + line + "'.");
+ }
+ String nickname = parts[1];
+ if (nickname.endsWith("-legacy")) {
+ nickname = nickname.substring(0, nickname.length()
+ - "-legacy".length());
+ this.isLegacy = true;
+ this.parsedExactlyOnceKeyword("vote-digest");
+ }
+ this.nickname = ParseHelper.parseNickname(line, nickname);
+ this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
+ if (parts[3].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.hostname = parts[3];
+ this.ip = ParseHelper.parseIpv4Address(line, parts[4]);
+ this.dirPort = ParseHelper.parsePort(line, parts[5]);
+ this.orPort = ParseHelper.parsePort(line, parts[6]);
+ }
+
+ private void parseContactLine(String line)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("contact");
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseVoteDigestLine(String line)
+ throws DescriptorParseException {
+ this.parsedExactlyOnceKeyword("vote-digest");
+ String[] parts = line.split("[ \t]+");
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "'.");
+ }
+ this.voteDigest = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private boolean isLegacy;
+ @Override
+ public boolean isLegacy() {
+ return this.isLegacy;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String ip;
+ @Override
+ public String getIp() {
+ return this.ip;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private String voteDigest;
+ @Override
+ public String getVoteDigest() {
+ return this.voteDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
new file mode 100644
index 0000000..a27ed76
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
@@ -0,0 +1,104 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.zip.InflaterInputStream;
+
+import org.torproject.descriptor.DescriptorParser;
+import org.torproject.descriptor.DescriptorSourceFactory;
+
+/* Download descriptors from one directory authority or mirror. First,
+ * ask the coordinator thread to create a request, run it, and deliver
+ * the response. Repeat until the coordinator thread says there are no
+ * further requests to make. */
+public class DirectoryDownloader implements Runnable {
+
+ private String nickname;
+ private String ipPort;
+ private DescriptorParser descriptorParser;
+ protected DirectoryDownloader(String nickname, String ip, int dirPort) {
+ this.nickname = nickname;
+ this.ipPort = ip + ":" + String.valueOf(dirPort);
+ this.descriptorParser =
+ DescriptorSourceFactory.createDescriptorParser();
+ }
+
+ private DownloadCoordinator downloadCoordinator;
+ protected void setDownloadCoordinator(
+ DownloadCoordinator downloadCoordinator) {
+ this.downloadCoordinator = downloadCoordinator;
+ }
+
+ private long connectTimeout;
+ protected void setConnectTimeout(long connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ private long readTimeout;
+ protected void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+ protected void setFailUnrecognizedDescriptorLines(
+ boolean failUnrecognizedDescriptorLines) {
+ this.descriptorParser.setFailUnrecognizedDescriptorLines(
+ failUnrecognizedDescriptorLines);
+ }
+
+ @Override
+ public void run() {
+ boolean keepRunning = true;
+ do {
+ DescriptorRequestImpl request =
+ this.downloadCoordinator.createRequest(this.nickname);
+ if (request != null) {
+ String url = "http://" + this.ipPort
+ + request.getRequestedResource();
+ request.setRequestStart(System.currentTimeMillis());
+ HttpURLConnection huc = null;
+ try {
+ URL u = new URL(url);
+ huc = (HttpURLConnection) u.openConnection();
+ huc.setConnectTimeout((int) this.connectTimeout);
+ huc.setReadTimeout((int) this.readTimeout);
+ huc.setRequestMethod("GET");
+ huc.connect();
+ int responseCode = huc.getResponseCode();
+ request.setResponseCode(responseCode);
+ if (responseCode == 200) {
+ BufferedInputStream in = new BufferedInputStream(
+ new InflaterInputStream(huc.getInputStream()));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len;
+ byte[] data = new byte[8192];
+ while ((len = in.read(data, 0, 8192)) >= 0) {
+ baos.write(data, 0, len);
+ }
+ in.close();
+ byte[] responseBytes = baos.toByteArray();
+ request.setResponseBytes(responseBytes);
+ request.setRequestEnd(System.currentTimeMillis());
+ request.setDescriptors(this.descriptorParser.parseDescriptors(
+ responseBytes, null));
+ }
+ } catch (Exception e) {
+ request.setException(e);
+ if (huc != null) {
+ huc.disconnect();
+ }
+ /* Stop downloading from this directory if there are any
+ * problems, e.g., refused connections. */
+ keepRunning = false;
+ }
+ this.downloadCoordinator.deliverResponse(request);
+ } else {
+ keepRunning = false;
+ }
+ } while (keepRunning);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
new file mode 100644
index 0000000..b62fc8e
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
@@ -0,0 +1,308 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectoryKeyCertificate;
+
+/* TODO Add test class. */
+
+public class DirectoryKeyCertificateImpl extends DescriptorImpl
+ implements DirectoryKeyCertificate {
+
+ protected static List<DirectoryKeyCertificate> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<DirectoryKeyCertificate> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DirectoryKeyCertificateImpl.splitRawDescriptorBytes(
+ descriptorsBytes, "dir-key-certificate-version ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ DirectoryKeyCertificate parsedDescriptor =
+ new DirectoryKeyCertificateImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected DirectoryKeyCertificateImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ 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();
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "dir-identity-key":
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublishedLine(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "dir-key-crosscert":
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
+ break;
+ case "dir-key-certification":
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in directory key certificate.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in directory key certificate.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseDirKeyCertificateVersionLine(String line,
+ String[] parts) throws DescriptorParseException {
+ if (!line.equals("dir-key-certificate-version 3")) {
+ throw new DescriptorParseException("Illegal directory key "
+ + "certificate version number in line '" + line + "'.");
+ }
+ this.dirKeyCertificateVersion = 3;
+ }
+
+ private void parseDirAddressLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || parts[1].split(":").length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in directory key certificate.");
+ }
+ this.address = ParseHelper.parseIpv4Address(line,
+ parts[1].split(":")[0]);
+ this.port = ParseHelper.parsePort(line, parts[1].split(":")[1]);
+ }
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in directory key certificate.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parseDirIdentityKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-identity-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyPublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirKeyExpiresLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCrosscertLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-crosscert")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCertificationLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-certification")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ 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";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.certificateDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.certificateDigest == null) {
+ throw new DescriptorParseException("Could not calculate "
+ + "certificate digest.");
+ }
+ }
+
+ private int dirKeyCertificateVersion;
+ @Override
+ public int getDirKeyCertificateVersion() {
+ return this.dirKeyCertificateVersion;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int port = -1;
+ @Override
+ public int getPort() {
+ return this.port;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String dirIdentityKey;
+ @Override
+ public String getDirIdentityKey() {
+ return this.dirIdentityKey;
+ }
+
+ private long dirKeyPublishedMillis;
+ @Override
+ public long getDirKeyPublishedMillis() {
+ return this.dirKeyPublishedMillis;
+ }
+
+ private long dirKeyExpiresMillis;
+ @Override
+ public long getDirKeyExpiresMillis() {
+ return this.dirKeyExpiresMillis;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String dirKeyCrosscert;
+ @Override
+ public String getDirKeyCrosscert() {
+ return this.dirKeyCrosscert;
+ }
+
+ private String dirKeyCertification;
+ @Override
+ public String getDirKeyCertification() {
+ return this.dirKeyCertification;
+ }
+
+ private String certificateDigest;
+ @Override
+ public String getCertificateDigest() {
+ return this.certificateDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
new file mode 100644
index 0000000..a955f62
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -0,0 +1,115 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import org.torproject.descriptor.DirectorySignature;
+
+public class DirectorySignatureImpl implements DirectorySignature {
+
+ private byte[] directorySignatureBytes;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected DirectorySignatureImpl(byte[] directorySignatureBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.directorySignatureBytes = directorySignatureBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.parseDirectorySignatureBytes();
+ }
+
+ private void parseDirectorySignatureBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.directorySignatureBytes)).
+ useDelimiter("\n");
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ", -1);
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-signature":
+ int algorithmOffset = 0;
+ switch (parts.length) {
+ case 4:
+ this.algorithm = parts[1];
+ algorithmOffset = 1;
+ break;
+ case 3:
+ break;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ this.identity = ParseHelper.parseHexString(line,
+ parts[1 + algorithmOffset]);
+ this.signingKeyDigest = ParseHelper.parseHexString(
+ line, parts[2 + algorithmOffset]);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ this.signature = cryptoString;
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in dir-source entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ static final String DEFAULT_ALGORITHM = "sha1";
+
+ private String algorithm;
+ @Override
+ public String getAlgorithm() {
+ return this.algorithm == null ? DEFAULT_ALGORITHM : this.algorithm;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private String signingKeyDigest;
+ @Override
+ public String getSigningKeyDigest() {
+ return this.signingKeyDigest;
+ }
+
+ private String signature;
+ @Override
+ public String getSignature() {
+ return this.signature;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
new file mode 100644
index 0000000..72cfeae
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
@@ -0,0 +1,10 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+public interface DownloadCoordinator {
+
+ public DescriptorRequestImpl createRequest(String nickname);
+
+ public void deliverResponse(DescriptorRequestImpl request);
+}
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
new file mode 100644
index 0000000..a8e3731
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
@@ -0,0 +1,298 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorRequest;
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* TODO This whole download logic is a mess and needs a cleanup. */
+public class DownloadCoordinatorImpl implements DownloadCoordinator {
+
+ private BlockingIteratorImpl<DescriptorRequest> descriptorQueue =
+ new BlockingIteratorImpl<>();
+ protected Iterator<DescriptorRequest> getDescriptorQueue() {
+ return this.descriptorQueue;
+ }
+
+ private SortedSet<String> runningDirectories;
+ private SortedMap<String, DirectoryDownloader> directoryAuthorities;
+ private SortedMap<String, DirectoryDownloader> directoryMirrors;
+ private boolean downloadConsensusFromAllAuthorities;
+ private boolean includeCurrentReferencedVotes;
+ private long connectTimeoutMillis;
+ private long readTimeoutMillis;
+ private long globalTimeoutMillis;
+ private boolean failUnrecognizedDescriptorLines;
+
+ protected DownloadCoordinatorImpl(
+ SortedMap<String, DirectoryDownloader> directoryAuthorities,
+ SortedMap<String, DirectoryDownloader> directoryMirrors,
+ boolean downloadConsensus,
+ boolean downloadConsensusFromAllAuthorities,
+ Set<String> downloadVotes, boolean includeCurrentReferencedVotes,
+ long connectTimeoutMillis, long readTimeoutMillis,
+ long globalTimeoutMillis, boolean failUnrecognizedDescriptorLines) {
+ this.directoryAuthorities = directoryAuthorities;
+ this.directoryMirrors = directoryMirrors;
+ this.runningDirectories = new TreeSet<>();
+ this.runningDirectories.addAll(directoryAuthorities.keySet());
+ this.runningDirectories.addAll(directoryMirrors.keySet());
+ this.missingConsensus = downloadConsensus;
+ this.downloadConsensusFromAllAuthorities =
+ downloadConsensusFromAllAuthorities;
+ this.missingVotes = downloadVotes;
+ this.includeCurrentReferencedVotes = includeCurrentReferencedVotes;
+ this.connectTimeoutMillis = connectTimeoutMillis;
+ this.readTimeoutMillis = readTimeoutMillis;
+ this.globalTimeoutMillis = globalTimeoutMillis;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ if (this.directoryMirrors.isEmpty() &&
+ this.directoryAuthorities.isEmpty()) {
+ this.descriptorQueue.setOutOfDescriptors();
+ /* TODO Should we say anything if we don't have any directories
+ * configured? */
+ } else {
+ GlobalTimer globalTimer = new GlobalTimer(this.globalTimeoutMillis,
+ this);
+ this.globalTimerThread = new Thread(globalTimer);
+ this.globalTimerThread.start();
+ for (DirectoryDownloader directoryMirror :
+ this.directoryMirrors.values()) {
+ directoryMirror.setDownloadCoordinator(this);
+ directoryMirror.setConnectTimeout(this.connectTimeoutMillis);
+ directoryMirror.setReadTimeout(this.readTimeoutMillis);
+ directoryMirror.setFailUnrecognizedDescriptorLines(
+ this.failUnrecognizedDescriptorLines);
+ new Thread(directoryMirror).start();
+ }
+ for (DirectoryDownloader directoryAuthority :
+ this.directoryAuthorities.values()) {
+ directoryAuthority.setDownloadCoordinator(this);
+ directoryAuthority.setConnectTimeout(this.connectTimeoutMillis);
+ directoryAuthority.setReadTimeout(this.readTimeoutMillis);
+ directoryAuthority.setFailUnrecognizedDescriptorLines(
+ this.failUnrecognizedDescriptorLines);
+ new Thread(directoryAuthority).start();
+ }
+ }
+ }
+
+ /* Interrupt all downloads if the total download time exceeds a given
+ * time. */
+ private Thread globalTimerThread;
+ private static class GlobalTimer implements Runnable {
+ private long timeoutMillis;
+ private DownloadCoordinatorImpl downloadCoordinator;
+ private GlobalTimer(long timeoutMillis,
+ DownloadCoordinatorImpl downloadCoordinator) {
+ this.timeoutMillis = timeoutMillis;
+ this.downloadCoordinator = downloadCoordinator;
+ }
+ public void run() {
+ long started = System.currentTimeMillis(), sleep;
+ while ((sleep = started + this.timeoutMillis
+ - System.currentTimeMillis()) > 0L) {
+ try {
+ Thread.sleep(sleep);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ this.downloadCoordinator.interruptAllDownloads();
+ }
+ }
+
+ /* Are we missing the consensus, and should the next directory that
+ * hasn't tried downloading it before attempt to download it? */
+ private boolean missingConsensus = false;
+
+ /* Which directories are currently attempting to download the
+ * consensus? */
+ private Set<String> requestingConsensuses = new HashSet<>();
+
+ /* Which directories have attempted to download the consensus so far,
+ * including those directories that are currently attempting it? */
+ private Set<String> requestedConsensuses = new HashSet<>();
+
+ /* Which votes are we currently missing? */
+ private Set<String> missingVotes = new HashSet<>();
+
+ /* Which vote (map value) is a given directory (map key) currently
+ * attempting to download? */
+ private Map<String, String> requestingVotes = new HashMap<>();
+
+ /* Which votes (map value) has a given directory (map key) attempted or
+ * is currently attempting to download? */
+ private Map<String, Set<String>> requestedVotes = new HashMap<>();
+
+ private boolean hasFinishedDownloading = false;
+
+ /* Look up what request a directory should make next. If there is
+ * nothing to do right now, but maybe later, block the caller. If
+ * we're done downloading, return null to notify the caller. */
+ @Override
+ public synchronized DescriptorRequestImpl createRequest(
+ String nickname) {
+ while (!this.hasFinishedDownloading) {
+ DescriptorRequestImpl request = new DescriptorRequestImpl();
+ request.setDirectoryNickname(nickname);
+ if ((this.missingConsensus ||
+ (this.downloadConsensusFromAllAuthorities &&
+ this.directoryAuthorities.containsKey(nickname))) &&
+ !this.requestedConsensuses.contains(nickname)) {
+ if (!this.downloadConsensusFromAllAuthorities) {
+ this.missingConsensus = false;
+ }
+ this.requestingConsensuses.add(nickname);
+ this.requestedConsensuses.add(nickname);
+ request.setRequestedResource(
+ "/tor/status-vote/current/consensus.z");
+ request.setDescriptorType("consensus");
+ return request;
+ }
+ if (!this.missingVotes.isEmpty() &&
+ this.directoryAuthorities.containsKey(nickname)) {
+ String requestingVote = null;
+ for (String missingVote : this.missingVotes) {
+ if (!this.requestedVotes.containsKey(nickname) ||
+ !this.requestedVotes.get(nickname).contains(missingVote)) {
+ requestingVote = missingVote;
+ }
+ }
+ if (requestingVote != null) {
+ this.requestingVotes.put(nickname, requestingVote);
+ if (!this.requestedVotes.containsKey(nickname)) {
+ this.requestedVotes.put(nickname, new HashSet<String>());
+ }
+ this.requestedVotes.get(nickname).add(requestingVote);
+ this.missingVotes.remove(requestingVote);
+ request.setRequestedResource("/tor/status-vote/current/"
+ + requestingVote + ".z");
+ request.setDescriptorType("vote");
+ return request;
+ }
+ }
+ /* TODO Add server descriptors and extra-info descriptors later. */
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ /* TODO What shall we do? */
+ }
+ }
+ return null;
+ }
+
+ /* Deliver a response which may either contain one or more descriptors
+ * or a failure response code. Update the lists of missing descriptors,
+ * decide if there are more descriptors to download, and wake up any
+ * waiting downloader threads. */
+ @Override
+ public synchronized void deliverResponse(
+ DescriptorRequestImpl response) {
+ String nickname = response.getDirectoryNickname();
+ if (response.getException() != null) {
+ this.runningDirectories.remove(nickname);
+ }
+ switch (response.getDescriptorType()) {
+ case "consensus":
+ this.requestingConsensuses.remove(nickname);
+ if (response.getResponseCode() == 200 &&
+ response.getDescriptors() != null) {
+ if (this.includeCurrentReferencedVotes) {
+ /* TODO Only add votes if the consensus is not older than one
+ * hour. Or does that make no sense? */
+ for (Descriptor parsedDescriptor :
+ response.getDescriptors()) {
+ if (!(parsedDescriptor instanceof
+ RelayNetworkStatusConsensus)) {
+ continue;
+ }
+ RelayNetworkStatusConsensus parsedConsensus =
+ (RelayNetworkStatusConsensus) parsedDescriptor;
+ for (DirSourceEntry dirSource :
+ parsedConsensus.getDirSourceEntries().values()) {
+ String identity = dirSource.getIdentity();
+ if (!this.missingVotes.contains(identity)) {
+ boolean alreadyRequested = false;
+ for (Set<String> requestedBefore :
+ this.requestedVotes.values()) {
+ if (requestedBefore.contains(identity)) {
+ alreadyRequested = true;
+ break;
+ }
+ }
+ if (!alreadyRequested) {
+ this.missingVotes.add(identity);
+ }
+ }
+ }
+ }
+ /* TODO Later, add referenced server descriptors. */
+ }
+ } else {
+ this.missingConsensus = true;
+ }
+ break;
+ case "vote":
+ String requestedVote = requestingVotes.remove(nickname);
+ if (response.getResponseCode() != 200) {
+ this.missingVotes.add(requestedVote);
+ }
+ }
+ if (response.getRequestEnd() != 0L) {
+ this.descriptorQueue.add(response);
+ }
+ boolean doneDownloading = true;
+ if ((this.missingConsensus ||
+ this.downloadConsensusFromAllAuthorities) &&
+ (!this.requestedConsensuses.containsAll(
+ this.runningDirectories) ||
+ !this.requestingConsensuses.isEmpty())) {
+ doneDownloading = false;
+ }
+ if (!this.requestingVotes.isEmpty()) {
+ doneDownloading = false;
+ } else if (!this.missingVotes.isEmpty()) {
+ if (!this.requestedVotes.keySet().containsAll(
+ this.runningDirectories)) {
+ doneDownloading = false;
+ } else {
+ for (String missingVote : this.missingVotes) {
+ for (String runningDirectory : this.runningDirectories) {
+ Set<String> reqVotes = this.requestedVotes.get(
+ runningDirectory);
+ if (!reqVotes.contains(missingVote)) {
+ doneDownloading = false;
+ }
+ }
+ }
+ }
+ }
+ if (doneDownloading) {
+ this.hasFinishedDownloading = true;
+ this.globalTimerThread.interrupt();
+ this.descriptorQueue.setOutOfDescriptors();
+ }
+ /* Wake up all waiting downloader threads. Maybe they can now
+ * download something, or they'll realize we're done downloading. */
+ this.notifyAll();
+ }
+
+ private synchronized void interruptAllDownloads() {
+ this.hasFinishedDownloading = true;
+ this.notifyAll();
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
new file mode 100644
index 0000000..efbf31c
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
@@ -0,0 +1,216 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExitList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
+
+ private byte[] exitListEntryBytes;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ @Deprecated
+ private ExitListEntryImpl(String fingerprint, long publishedMillis,
+ long lastStatusMillis, String exitAddress, long scanMillis) {
+ this.fingerprint = fingerprint;
+ this.publishedMillis = publishedMillis;
+ this.lastStatusMillis = lastStatusMillis;
+ this.exitAddresses.put(exitAddress, scanMillis);
+ }
+
+ @Deprecated
+ List<ExitListEntry> oldEntries() {
+ List<ExitListEntry> result = new ArrayList<>();
+ if (this.exitAddresses.size() > 1) {
+ for (Map.Entry<String, Long> entry :
+ this.exitAddresses.entrySet()) {
+ result.add(new ExitListEntryImpl(this.fingerprint,
+ this.publishedMillis, this.lastStatusMillis, entry.getKey(),
+ entry.getValue()));
+ }
+ } else {
+ result.add(this);
+ }
+ return result;
+ }
+
+ protected ExitListEntryImpl(byte[] exitListEntryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.exitListEntryBytes = exitListEntryBytes;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseExitListEntryBytes();
+ this.checkAndClearKeywords();
+ }
+
+ private SortedSet<String> keywordCountingSet;
+ private void initializeKeywords() {
+ this.keywordCountingSet = new TreeSet<>();
+ this.keywordCountingSet.add("ExitNode");
+ this.keywordCountingSet.add("Published");
+ this.keywordCountingSet.add("LastStatus");
+ this.keywordCountingSet.add("ExitAddress");
+ }
+
+ private void parsedExactlyOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.keywordCountingSet.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in exit list entry.");
+ }
+ this.keywordCountingSet.remove(keyword);
+ }
+
+ private void checkAndClearKeywords() throws DescriptorParseException {
+ for (String missingKeyword : this.keywordCountingSet) {
+ throw new DescriptorParseException("Missing '" + missingKeyword
+ + "' line in exit list entry.");
+ }
+ this.keywordCountingSet = null;
+ }
+
+ private void parseExitListEntryBytes()
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.exitListEntryBytes)).
+ useDelimiter(ExitList.EOL);
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split(" ");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "ExitNode":
+ this.parseExitNodeLine(line, parts);
+ break;
+ case "Published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "LastStatus":
+ this.parseLastStatusLine(line, parts);
+ break;
+ case "ExitAddress":
+ this.parseExitAddressLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in exit list entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseExitNodeLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseLastStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.parsedExactlyOnceKeyword(parts[0]);
+ this.lastStatusMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseExitAddressLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 4) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "exit list entry.");
+ }
+ this.keywordCountingSet.remove(parts[0]);
+ this.exitAddresses.put(ParseHelper.parseIpv4Address(line, parts[1]),
+ ParseHelper.parseTimestampAtIndex(line, parts, 2, 3));
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long lastStatusMillis;
+ @Override
+ public long getLastStatusMillis() {
+ return this.lastStatusMillis;
+ }
+
+ private String exitAddress;
+ @Override
+ public String getExitAddress() {
+ if (null == exitAddress) {
+ Map.Entry<String, Long> randomEntry =
+ this.exitAddresses.entrySet().iterator().next();
+ this.exitAddress = randomEntry.getKey();
+ this.scanMillis = randomEntry.getValue();
+ }
+ return this.exitAddress;
+ }
+
+ private Map<String, Long> exitAddresses = new HashMap<>();
+ @Override
+ public Map<String, Long> getExitAddresses(){
+ return new HashMap<>(this.exitAddresses);
+ }
+
+ private long scanMillis;
+ @Override
+ public long getScanMillis() {
+ if (null == exitAddress) {
+ getExitAddress();
+ }
+ return scanMillis;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
new file mode 100644
index 0000000..10619ba
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
@@ -0,0 +1,142 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.torproject.descriptor.ExitList;
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListImpl extends DescriptorImpl implements ExitList {
+
+ protected ExitListImpl(byte[] rawDescriptorBytes, String fileName,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.splitAndParseExitListEntries(rawDescriptorBytes);
+ this.setPublishedMillisFromFileName(fileName);
+ }
+
+ private void setPublishedMillisFromFileName(String fileName)
+ throws DescriptorParseException {
+ if (this.downloadedMillis == 0L &&
+ fileName.length() == "2012-02-01-04-06-24".length()) {
+ try {
+ SimpleDateFormat fileNameFormat = new SimpleDateFormat(
+ "yyyy-MM-dd-HH-mm-ss");
+ fileNameFormat.setLenient(false);
+ fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.downloadedMillis = fileNameFormat.parse(fileName).getTime();
+ } catch (ParseException e) {
+ /* Handle below. */
+ }
+ }
+ if (this.downloadedMillis == 0L) {
+ throw new DescriptorParseException("Unrecognized exit list file "
+ + "name '" + fileName + "'.");
+ }
+ }
+
+ private void splitAndParseExitListEntries(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ Scanner s = new Scanner(descriptorString).useDelimiter(EOL);
+ StringBuilder sb = new StringBuilder();
+ boolean firstEntry = true;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) { /* Skip annotation. */
+ if (!s.hasNext()) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ } else {
+ line = s.next();
+ }
+ }
+ String[] parts = line.split(" ");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "Downloaded":
+ this.downloadedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ break;
+ case "ExitNode":
+ if (!firstEntry) {
+ this.parseExitListEntry(sb.toString().getBytes());
+ } else {
+ firstEntry = false;
+ }
+ sb = new StringBuilder();
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "Published":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "LastStatus":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ case "ExitAddress":
+ sb.append(line).append(ExitList.EOL);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in exit list.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ /* Parse the last entry. */
+ this.parseExitListEntry(sb.toString().getBytes());
+ }
+
+ protected void parseExitListEntry(byte[] exitListEntryBytes)
+ throws DescriptorParseException {
+ ExitListEntryImpl exitListEntry = new ExitListEntryImpl(
+ exitListEntryBytes, this.failUnrecognizedDescriptorLines);
+ this.exitListEntries.add(exitListEntry);
+ this.oldExitListEntries.addAll(exitListEntry.oldEntries());
+ List<String> unrecognizedExitListEntryLines = exitListEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedExitListEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedExitListEntryLines);
+ }
+ }
+
+ private long downloadedMillis;
+ @Override
+ public long getDownloadedMillis() {
+ return this.downloadedMillis;
+ }
+
+ private Set<ExitListEntry> oldExitListEntries = new HashSet<>();
+ @Deprecated
+ @Override
+ public Set<ExitListEntry> getExitListEntries() {
+ return new HashSet<>(this.oldExitListEntries);
+ }
+
+ private Set<ExitList.Entry> exitListEntries = new HashSet<>();
+ @Override
+ public Set<ExitList.Entry> getEntries() {
+ return new HashSet<>(this.exitListEntries);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..3f72616
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -0,0 +1,1284 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+
+public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
+ implements ExtraInfoDescriptor {
+
+ protected ExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ 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);
+ 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();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ List<String> cryptoLines = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "extra-info":
+ this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-db-digest":
+ this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip6-db-digest":
+ this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-start-time":
+ this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "geoip-client-origins":
+ this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-stats-end":
+ this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-ips":
+ this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-ips":
+ this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-reqs":
+ this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-reqs":
+ this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-share":
+ this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-share":
+ this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-resp":
+ this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-resp":
+ this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-direct-dl":
+ this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-direct-dl":
+ this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v2-tunneled-dl":
+ this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-v3-tunneled-dl":
+ this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-read-history":
+ this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dirreq-write-history":
+ this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-stats-end":
+ this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "entry-ips":
+ this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-stats-end":
+ this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-processed-cells":
+ this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-queued-cells":
+ this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-time-in-queue":
+ this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "cell-circuits-per-decile":
+ this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "conn-bi-direct":
+ this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-stats-end":
+ this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-written":
+ this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-kibibytes-read":
+ this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "exit-streams-opened":
+ this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-stats-end":
+ this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ips":
+ this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-versions":
+ this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bridge-ip-transports":
+ this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "transport":
+ this.parseTransportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-stats-end":
+ this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidserv-rend-relayed-cells":
+ this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
+ partsNoOpt);
+ break;
+ case "hidserv-dir-onions-seen":
+ this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "identity-ed25519":
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
+ break;
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-signature":
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
+ cryptoLines.add(line);
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in extra-info "
+ + "descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ }
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
+ } else {
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in extra-info descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseExtraInfoLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[2]);
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseGeoipDbDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipDbDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseGeoip6DbDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoip6DbDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseGeoipStartTimeLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in extra-info descriptor.");
+ }
+ this.geoipStartTimeMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseGeoipClientOriginsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.geoipClientOrigins =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.dirreqStatsEndMillis = parsedStatsEndData[0];
+ this.dirreqStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private long[] parseStatsEndLine(String line, String partsNoOpt[],
+ int partsNoOptExpectedLength) throws DescriptorParseException {
+ if (partsNoOpt.length != partsNoOptExpectedLength ||
+ partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
+ !partsNoOpt[4].equals("s)")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ long[] result = new long[2];
+ result[0] = ParseHelper.parseTimestampAtIndex(line, partsNoOpt, 1, 2);
+ result[1] = ParseHelper.parseSeconds(line,
+ partsNoOpt[3].substring(1));
+ if (result[1] <= 0) {
+ throw new DescriptorParseException("Interval length must be "
+ + "positive in line '" + line + "'.");
+ }
+ return result;
+ }
+
+ private void parseDirreqV2IpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV3IpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV2ReqsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Reqs =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV3ReqsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Reqs =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirreqV2ShareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Share = this.parseShareLine(line, partsNoOpt);
+ }
+
+ private void parseDirreqV3ShareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Share = this.parseShareLine(line, partsNoOpt);
+ }
+
+ private double parseShareLine(String line, String[] partsNoOpt)
+ throws DescriptorParseException {
+ double share = -1.0;
+ if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
+ partsNoOpt[1].endsWith("%")) {
+ String shareString = partsNoOpt[1];
+ shareString = shareString.substring(0, shareString.length() - 1);
+ try {
+ share = Double.parseDouble(shareString);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (share < 0.0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ return share;
+ }
+
+ private void parseDirreqV2RespLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2Resp =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3RespLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3Resp =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV2DirectDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2DirectDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3DirectDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3DirectDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV2TunneledDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV2TunneledDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqV3TunneledDlLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqV3TunneledDl =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line,partsNoOpt, 1, 0);
+ }
+
+ private void parseDirreqReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqReadHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseDirreqWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.dirreqWriteHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseEntryStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.entryStatsEndMillis = parsedStatsEndData[0];
+ this.entryStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseEntryIpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.entryIps = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
+ line, partsNoOpt, 1, 2);
+ }
+
+ private void parseCellStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.cellStatsEndMillis = parsedStatsEndData[0];
+ this.cellStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseCellProcessedCellsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellProcessedCells = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ if (this.cellProcessedCells.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellQueuedCellsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellQueuedCells = ParseHelper.parseCommaSeparatedDoubleValueList(
+ line, partsNoOpt, 1);
+ if (this.cellQueuedCells.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellTimeInQueueLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.cellTimeInQueue = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
+ if (this.cellTimeInQueue.length != 10) {
+ throw new DescriptorParseException("There must be exact ten values "
+ + "in line '" + line + "'.");
+ }
+ }
+
+ private void parseCellCircuitsPerDecileLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ int circuits = -1;
+ if (partsNoOpt.length == 2) {
+ try {
+ circuits = Integer.parseInt(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (circuits < 0) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.cellCircuitsPerDecile = circuits;
+ }
+
+ private void parseConnBiDirectLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 6);
+ this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
+ this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
+ Integer[] parsedConnBiDirectStats = ParseHelper.
+ parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
+ if (parsedConnBiDirectStats.length != 4) {
+ throw new DescriptorParseException("Illegal line '" + line + "' in "
+ + "extra-info descriptor.");
+ }
+ this.connBiDirectBelow = parsedConnBiDirectStats[0];
+ this.connBiDirectRead = parsedConnBiDirectStats[1];
+ this.connBiDirectWrite = parsedConnBiDirectStats[2];
+ this.connBiDirectBoth = parsedConnBiDirectStats[3];
+ }
+
+ private void parseExitStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.exitStatsEndMillis = parsedStatsEndData[0];
+ this.exitStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseExitKibibytesWrittenLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ this.exitKibibytesWritten = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitKibibytesWritten.keySet());
+ this.verifyBytesOrStreams(line, this.exitKibibytesWritten.values());
+ }
+
+ private void parseExitKibibytesReadLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.exitKibibytesRead = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitKibibytesRead.keySet());
+ this.verifyBytesOrStreams(line, this.exitKibibytesRead.values());
+ }
+
+ private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.exitStreamsOpened = this.sortByPorts(ParseHelper.
+ parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
+ this.verifyPorts(line, this.exitStreamsOpened.keySet());
+ this.verifyBytesOrStreams(line, this.exitStreamsOpened.values());
+ }
+
+ private SortedMap<String, Long> sortByPorts(
+ SortedMap<String, Long> naturalOrder) {
+ SortedMap<String, Long> byPortNumber =
+ new TreeMap<String, Long>(new Comparator<String>() {
+ public int compare(String arg0, String arg1) {
+ int port0 = 0, port1 = 0;
+ try {
+ port1 = Integer.parseInt(arg1);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ try {
+ port0 = Integer.parseInt(arg0);
+ } catch (NumberFormatException e) {
+ return 1;
+ }
+ if (port0 < port1) {
+ return -1;
+ } else if (port0 > port1) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }});
+ byPortNumber.putAll(naturalOrder);
+ return byPortNumber;
+ }
+
+ private void verifyPorts(String line, Set<String> ports)
+ throws DescriptorParseException {
+ boolean valid = true;
+ try {
+ for (String port : ports) {
+ if (!port.equals("other") && Integer.parseInt(port) <= 0) {
+ valid = false;
+ break;
+ }
+ }
+ } catch (NumberFormatException e) {
+ valid = false;
+ }
+ if (!valid) {
+ throw new DescriptorParseException("Invalid port in line '" + line
+ + "'.");
+ }
+ }
+
+ private void verifyBytesOrStreams(String line,
+ Collection<Long> bytesOrStreams) throws DescriptorParseException {
+ boolean valid = true;
+ for (long s : bytesOrStreams) {
+ if (s < 0L) {
+ valid = false;
+ break;
+ }
+ }
+ if (!valid) {
+ throw new DescriptorParseException("Invalid value in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseBridgeStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.bridgeStatsEndMillis = parsedStatsEndData[0];
+ this.bridgeStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseBridgeStatsIpsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIps =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseBridgeIpVersionsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIpVersions =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseBridgeIpTransportsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.bridgeIpTransports =
+ ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
+ partsNoOpt, 1, 0);
+ }
+
+ private void parseTransportLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.transports.add(partsNoOpt[1]);
+ }
+
+ private void parseHidservStatsEndLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+ 5);
+ this.hidservStatsEndMillis = parsedStatsEndData[0];
+ this.hidservStatsIntervalLength = parsedStatsEndData[1];
+ }
+
+ private void parseHidservRendRelayedCellsLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.hidservRendRelayedCells = Double.parseDouble(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hidservRendRelayedCellsParameters =
+ ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
+ partsNoOpt, 2);
+ }
+
+ private void parseHidservDirOnionsSeenLine(String line,
+ String lineNoOpt, String[] partsNoOpt)
+ throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.hidservDirOnionsSeen = Double.parseDouble(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hidservDirOnionsSeenParameters =
+ ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
+ partsNoOpt, 2);
+ }
+
+ private void parseRouterSignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("router-signature")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseRouterDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
+ private void parseIdentityEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseIdentityEd25519CryptoBlock(String cryptoString)
+ throws DescriptorParseException {
+ String masterKeyEd25519FromIdentityEd25519 =
+ ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
+ cryptoString);
+ if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
+ masterKeyEd25519FromIdentityEd25519)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
+ }
+
+ private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
+ if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
+ masterKeyEd25519FromMasterKeyEd25519Line)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
+ }
+
+ private void parseRouterSigEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.routerSignatureEd25519 = partsNoOpt[1];
+ }
+
+ private void parseRouterDigestSha256Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
+ this.extraInfoDigestSha256 = partsNoOpt[1];
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ if (this.extraInfoDigest != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "extra-info ";
+ String sigToken = "\nrouter-signature\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.extraInfoDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.extraInfoDigest == null) {
+ throw new DescriptorParseException("Could not calculate extra-info "
+ + "descriptor digest.");
+ }
+ }
+
+ private void calculateDigestSha256() throws DescriptorParseException {
+ if (this.extraInfoDigestSha256 != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest-sha256" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "extra-info ";
+ String sigToken = "\n-----END SIGNATURE-----\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
+ 0, sig - start);
+ this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ replaceAll("=", "");
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.extraInfoDigestSha256 == null) {
+ throw new DescriptorParseException("Could not calculate extra-info "
+ + "descriptor SHA-256 digest.");
+ }
+ }
+
+ private String extraInfoDigest;
+ @Override
+ public String getExtraInfoDigest() {
+ return this.extraInfoDigest;
+ }
+
+ private String extraInfoDigestSha256;
+ @Override
+ public String getExtraInfoDigestSha256() {
+ return this.extraInfoDigestSha256;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private BandwidthHistory readHistory;
+ @Override
+ public BandwidthHistory getReadHistory() {
+ return this.readHistory;
+ }
+
+ private BandwidthHistory writeHistory;
+ @Override
+ public BandwidthHistory getWriteHistory() {
+ return this.writeHistory;
+ }
+
+ private String geoipDbDigest;
+ @Override
+ public String getGeoipDbDigest() {
+ return this.geoipDbDigest;
+ }
+
+ private String geoip6DbDigest;
+ @Override
+ public String getGeoip6DbDigest() {
+ return this.geoip6DbDigest;
+ }
+
+ private long dirreqStatsEndMillis = -1L;
+ @Override
+ public long getDirreqStatsEndMillis() {
+ return this.dirreqStatsEndMillis;
+ }
+
+ private long dirreqStatsIntervalLength = -1L;
+ @Override
+ public long getDirreqStatsIntervalLength() {
+ return this.dirreqStatsIntervalLength;
+ }
+
+ private String dirreqV2Ips;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Ips() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Ips);
+ }
+
+ private String dirreqV3Ips;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Ips() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Ips);
+ }
+
+ private String dirreqV2Reqs;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Reqs() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Reqs);
+ }
+
+ private String dirreqV3Reqs;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Reqs() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Reqs);
+ }
+
+ private double dirreqV2Share = -1.0;
+ @Override
+ public double getDirreqV2Share() {
+ return this.dirreqV2Share;
+ }
+
+ private double dirreqV3Share = -1.0;
+ @Override
+ public double getDirreqV3Share() {
+ return this.dirreqV3Share;
+ }
+
+ private String dirreqV2Resp;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2Resp() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2Resp);
+ }
+
+ private String dirreqV3Resp;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3Resp() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3Resp);
+ }
+
+ private String dirreqV2DirectDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2DirectDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2DirectDl);
+ }
+
+ private String dirreqV3DirectDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3DirectDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3DirectDl);
+ }
+
+ private String dirreqV2TunneledDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV2TunneledDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV2TunneledDl);
+ }
+
+ private String dirreqV3TunneledDl;
+ @Override
+ public SortedMap<String, Integer> getDirreqV3TunneledDl() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.dirreqV3TunneledDl);
+ }
+
+ private BandwidthHistory dirreqReadHistory;
+ @Override
+ public BandwidthHistory getDirreqReadHistory() {
+ return this.dirreqReadHistory;
+ }
+
+ private BandwidthHistory dirreqWriteHistory;
+ @Override
+ public BandwidthHistory getDirreqWriteHistory() {
+ return this.dirreqWriteHistory;
+ }
+
+ private long entryStatsEndMillis = -1L;
+ @Override
+ public long getEntryStatsEndMillis() {
+ return this.entryStatsEndMillis;
+ }
+
+ private long entryStatsIntervalLength = -1L;
+ @Override
+ public long getEntryStatsIntervalLength() {
+ return this.entryStatsIntervalLength;
+ }
+
+ private String entryIps;
+ @Override
+ public SortedMap<String, Integer> getEntryIps() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.entryIps);
+ }
+
+ private long cellStatsEndMillis = -1L;
+ @Override
+ public long getCellStatsEndMillis() {
+ return this.cellStatsEndMillis;
+ }
+
+ private long cellStatsIntervalLength = -1L;
+ @Override
+ public long getCellStatsIntervalLength() {
+ return this.cellStatsIntervalLength;
+ }
+
+ private Integer[] cellProcessedCells;
+ @Override
+ public List<Integer> getCellProcessedCells() {
+ return this.cellProcessedCells == null ? null :
+ Arrays.asList(this.cellProcessedCells);
+ }
+
+ private Double[] cellQueuedCells;
+ @Override
+ public List<Double> getCellQueuedCells() {
+ return this.cellQueuedCells == null ? null :
+ Arrays.asList(this.cellQueuedCells);
+ }
+
+ private Integer[] cellTimeInQueue;
+ @Override
+ public List<Integer> getCellTimeInQueue() {
+ return this.cellTimeInQueue == null ? null :
+ Arrays.asList(this.cellTimeInQueue);
+ }
+
+ private int cellCircuitsPerDecile = -1;
+ @Override
+ public int getCellCircuitsPerDecile() {
+ return this.cellCircuitsPerDecile;
+ }
+
+ private long connBiDirectStatsEndMillis = -1L;
+ @Override
+ public long getConnBiDirectStatsEndMillis() {
+ return this.connBiDirectStatsEndMillis;
+ }
+
+ private long connBiDirectStatsIntervalLength = -1L;
+ @Override
+ public long getConnBiDirectStatsIntervalLength() {
+ return this.connBiDirectStatsIntervalLength;
+ }
+
+ private int connBiDirectBelow = -1;
+ @Override
+ public int getConnBiDirectBelow() {
+ return this.connBiDirectBelow;
+ }
+
+ private int connBiDirectRead = -1;
+ @Override
+ public int getConnBiDirectRead() {
+ return this.connBiDirectRead;
+ }
+
+ private int connBiDirectWrite = -1;
+ @Override
+ public int getConnBiDirectWrite() {
+ return this.connBiDirectWrite;
+ }
+
+ private int connBiDirectBoth = -1;
+ @Override
+ public int getConnBiDirectBoth() {
+ return this.connBiDirectBoth;
+ }
+
+ private long exitStatsEndMillis = -1L;
+ @Override
+ public long getExitStatsEndMillis() {
+ return this.exitStatsEndMillis;
+ }
+
+ private long exitStatsIntervalLength = -1L;
+ @Override
+ public long getExitStatsIntervalLength() {
+ return this.exitStatsIntervalLength;
+ }
+
+ private SortedMap<String, Long> exitKibibytesWritten;
+ @Override
+ public SortedMap<String, Long> getExitKibibytesWritten() {
+ return this.exitKibibytesWritten == null ? null :
+ new TreeMap<>(this.exitKibibytesWritten);
+ }
+
+ private SortedMap<String, Long> exitKibibytesRead;
+ @Override
+ public SortedMap<String, Long> getExitKibibytesRead() {
+ return this.exitKibibytesRead == null ? null :
+ new TreeMap<>(this.exitKibibytesRead);
+ }
+
+ private SortedMap<String, Long> exitStreamsOpened;
+ @Override
+ public SortedMap<String, Long> getExitStreamsOpened() {
+ return this.exitStreamsOpened == null ? null :
+ new TreeMap<>(this.exitStreamsOpened);
+ }
+
+ private long geoipStartTimeMillis = -1L;
+ @Override
+ public long getGeoipStartTimeMillis() {
+ return this.geoipStartTimeMillis;
+ }
+
+ private String geoipClientOrigins;
+ @Override
+ public SortedMap<String, Integer> getGeoipClientOrigins() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.geoipClientOrigins);
+ }
+
+ private long bridgeStatsEndMillis = -1L;
+ @Override
+ public long getBridgeStatsEndMillis() {
+ return this.bridgeStatsEndMillis;
+ }
+
+ private long bridgeStatsIntervalLength = -1L;
+ @Override
+ public long getBridgeStatsIntervalLength() {
+ return this.bridgeStatsIntervalLength;
+ }
+
+ private String bridgeIps;
+ @Override
+ public SortedMap<String, Integer> getBridgeIps() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIps);
+ }
+
+ private String bridgeIpVersions;
+ @Override
+ public SortedMap<String, Integer> getBridgeIpVersions() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIpVersions);
+ }
+
+ private String bridgeIpTransports;
+ @Override
+ public SortedMap<String, Integer> getBridgeIpTransports() {
+ return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
+ this.bridgeIpTransports);
+ }
+
+ private List<String> transports = new ArrayList<>();
+ @Override
+ public List<String> getTransports() {
+ return new ArrayList<>(this.transports);
+ }
+
+ private long hidservStatsEndMillis = -1L;
+ @Override
+ public long getHidservStatsEndMillis() {
+ return this.hidservStatsEndMillis;
+ }
+
+ private long hidservStatsIntervalLength = -1L;
+ @Override
+ public long getHidservStatsIntervalLength() {
+ return this.hidservStatsIntervalLength;
+ }
+
+ private Double hidservRendRelayedCells;
+ @Override
+ public Double getHidservRendRelayedCells() {
+ return this.hidservRendRelayedCells;
+ }
+
+ private Map<String, Double> hidservRendRelayedCellsParameters;
+ @Override
+ public Map<String, Double> getHidservRendRelayedCellsParameters() {
+ return this.hidservRendRelayedCellsParameters == null ? null :
+ new HashMap<>(this.hidservRendRelayedCellsParameters);
+ }
+
+ private Double hidservDirOnionsSeen;
+ @Override
+ public Double getHidservDirOnionsSeen() {
+ return this.hidservDirOnionsSeen;
+ }
+
+ private Map<String, Double> hidservDirOnionsSeenParameters;
+ @Override
+ public Map<String, Double> getHidservDirOnionsSeenParameters() {
+ return this.hidservDirOnionsSeenParameters == null ? null :
+ new HashMap<>(this.hidservDirOnionsSeenParameters);
+ }
+
+ private String routerSignature;
+ @Override
+ public String getRouterSignature() {
+ return this.routerSignature;
+ }
+
+ private String identityEd25519;
+ @Override
+ public String getIdentityEd25519() {
+ return this.identityEd25519;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+
+ private String routerSignatureEd25519;
+ @Override
+ public String getRouterSignatureEd25519() {
+ return this.routerSignatureEd25519;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
new file mode 100644
index 0000000..4931c31
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -0,0 +1,328 @@
+/* Copyright 2014--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.Microdescriptor;
+
+/* Contains a microdescriptor. */
+public class MicrodescriptorImpl extends DescriptorImpl
+ implements Microdescriptor {
+
+ protected static List<Microdescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<Microdescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "onion-key\n");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ Microdescriptor parsedDescriptor =
+ new MicrodescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected MicrodescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseDescriptorBytes();
+ this.calculateDigest();
+ Set<String> exactlyOnceKeywords = new HashSet<>(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();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) {
+ continue;
+ }
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "onion-key":
+ this.parseOnionKeyLine(line, parts);
+ nextCrypto = "onion-key";
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, parts);
+ break;
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "family":
+ this.parseFamilyLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "p6":
+ this.parseP6Line(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("onion-key")) {
+ this.onionKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in microdescriptor.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ ParseHelper.parseKeyword(line, parts[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in microdescriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseOnionKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("onion-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.ntorOnionKey = parts[1].replaceAll("=", "");
+ }
+
+ private void parseALine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(parts[1]);
+ }
+
+ private void parseFamilyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ String[] familyEntries = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ if (parts[i].startsWith("$")) {
+ if (parts[i].contains("=") ^ parts[i].contains("~")) {
+ String separator = parts[i].contains("=") ? "=" : "~";
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[i].substring(1, parts[i].indexOf(separator)));
+ String nickname = ParseHelper.parseNickname(line,
+ parts[i].substring(parts[i].indexOf(separator) + 1));
+ familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
+ } else {
+ familyEntries[i - 1] = "$"
+ + ParseHelper.parseTwentyByteHexString(line,
+ parts[i].substring(1));
+ }
+ } else {
+ familyEntries[i - 1] = ParseHelper.parseNickname(line, parts[i]);
+ }
+ }
+ this.familyEntries = familyEntries;
+ }
+
+ private void parsePLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validatePOrP6Line(line, parts);
+ this.defaultPolicy = parts[1];
+ this.portList = parts[2];
+ }
+
+ private void parseP6Line(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validatePOrP6Line(line, parts);
+ this.ipv6DefaultPolicy = parts[1];
+ this.ipv6PortList = parts[2];
+ }
+
+ private void validatePOrP6Line(String line, String[] parts)
+ throws DescriptorParseException {
+ boolean isValid = true;
+ if (parts.length != 3) {
+ isValid = false;
+ } else {
+ switch (parts[1]) {
+ case "accept":
+ case "reject":
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseIdLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else {
+ switch (parts[1]) {
+ case "ed25519":
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
+ this.ed25519Identity = parts[2];
+ break;
+ case "rsa1024":
+ ParseHelper.parseTwentyByteBase64String(line, parts[2]);
+ this.rsa1024Identity = parts[2];
+ break;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "onion-key\n";
+ int start = ascii.indexOf(startToken);
+ int end = ascii.length();
+ if (start >= 0 && end > start) {
+ byte[] forDigest = new byte[end - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, end - start);
+ this.microdescriptorDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.microdescriptorDigest == null) {
+ throw new DescriptorParseException("Could not calculate "
+ + "microdescriptor digest.");
+ }
+ }
+
+ private String microdescriptorDigest;
+ @Override
+ public String getMicrodescriptorDigest() {
+ return this.microdescriptorDigest;
+ }
+
+ private String onionKey;
+ @Override
+ public String getOnionKey() {
+ return this.onionKey;
+ }
+
+ private String ntorOnionKey;
+ @Override
+ public String getNtorOnionKey() {
+ return this.ntorOnionKey;
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private String[] familyEntries;
+ @Override
+ public List<String> getFamilyEntries() {
+ return this.familyEntries == null ? null :
+ Arrays.asList(this.familyEntries);
+ }
+ private String defaultPolicy;
+ @Override
+ public String getDefaultPolicy() {
+ return this.defaultPolicy;
+ }
+
+ private String portList;
+ @Override
+ public String getPortList() {
+ return this.portList;
+ }
+
+ private String ipv6DefaultPolicy;
+ @Override
+ public String getIpv6DefaultPolicy() {
+ return this.ipv6DefaultPolicy;
+ }
+
+ private String ipv6PortList;
+ @Override
+ public String getIpv6PortList() {
+ return this.ipv6PortList;
+ }
+
+ private String rsa1024Identity;
+ @Override
+ public String getRsa1024Identity() {
+ return this.rsa1024Identity;
+ }
+
+ private String ed25519Identity;
+ @Override
+ public String getEd25519Identity() {
+ return this.ed25519Identity;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
new file mode 100644
index 0000000..b73d211
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -0,0 +1,382 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.NetworkStatusEntry;
+
+public class NetworkStatusEntryImpl implements NetworkStatusEntry {
+
+ private byte[] statusEntryBytes;
+ @Override
+ public byte[] getStatusEntryBytes() {
+ return this.statusEntryBytes;
+ }
+
+ private boolean microdescConsensus;
+
+ private boolean failUnrecognizedDescriptorLines;
+ private List<String> unrecognizedLines;
+ protected List<String> getAndClearUnrecognizedLines() {
+ List<String> lines = this.unrecognizedLines;
+ this.unrecognizedLines = null;
+ return lines;
+ }
+
+ protected NetworkStatusEntryImpl(byte[] statusEntryBytes,
+ boolean microdescConsensus, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ this.statusEntryBytes = statusEntryBytes;
+ this.microdescConsensus = microdescConsensus;
+ this.failUnrecognizedDescriptorLines =
+ failUnrecognizedDescriptorLines;
+ this.initializeKeywords();
+ this.parseStatusEntryBytes();
+ this.clearAtMostOnceKeywords();
+ }
+
+ private SortedSet<String> atMostOnceKeywords;
+ private void initializeKeywords() {
+ this.atMostOnceKeywords = new TreeSet<>();
+ this.atMostOnceKeywords.add("s");
+ this.atMostOnceKeywords.add("v");
+ this.atMostOnceKeywords.add("w");
+ this.atMostOnceKeywords.add("p");
+ }
+
+ private void parsedAtMostOnceKeyword(String keyword)
+ throws DescriptorParseException {
+ if (!this.atMostOnceKeywords.contains(keyword)) {
+ throw new DescriptorParseException("Duplicate '" + keyword
+ + "' line in status entry.");
+ }
+ this.atMostOnceKeywords.remove(keyword);
+ }
+
+ private void parseStatusEntryBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.statusEntryBytes)).
+ useDelimiter("\n");
+ String line = null;
+ if (!s.hasNext() || !(line = s.next()).startsWith("r ")) {
+ throw new DescriptorParseException("Status entry must start with "
+ + "an r line.");
+ }
+ String[] rLineParts = line.split("[ \t]+");
+ this.parseRLine(line, rLineParts);
+ while (s.hasNext()) {
+ line = s.next();
+ String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") :
+ line.substring("opt ".length()).split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "a":
+ this.parseALine(line, parts);
+ break;
+ case "s":
+ this.parseSLine(line, parts);
+ break;
+ case "v":
+ this.parseVLine(line, parts);
+ break;
+ case "w":
+ this.parseWLine(line, parts);
+ break;
+ case "p":
+ this.parsePLine(line, parts);
+ break;
+ case "m":
+ this.parseMLine(line, parts);
+ break;
+ case "id":
+ this.parseIdLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in status entry.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseRLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if ((!this.microdescConsensus && parts.length != 9) ||
+ (this.microdescConsensus && parts.length != 8)) {
+ throw new DescriptorParseException("r line '" + line + "' has "
+ + "fewer space-separated elements than expected.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ this.fingerprint = ParseHelper.parseTwentyByteBase64String(line,
+ parts[2]);
+ int descriptorOffset = 0;
+ if (!this.microdescConsensus) {
+ this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
+ parts[3]);
+ descriptorOffset = 1;
+ }
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 3 + descriptorOffset, 4 + descriptorOffset);
+ this.address = ParseHelper.parseIpv4Address(line,
+ parts[5 + descriptorOffset]);
+ this.orPort = ParseHelper.parsePort(line,
+ parts[6 + descriptorOffset]);
+ this.dirPort = ParseHelper.parsePort(line,
+ parts[7 + descriptorOffset]);
+ }
+
+ private void parseALine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "status entry.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(parts[1]);
+ }
+
+ private static Map<String, Integer> flagIndexes = new HashMap<>();
+ private static Map<Integer, String> flagStrings = new HashMap<>();
+
+ private void parseSLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("s");
+ BitSet flags = new BitSet(flagIndexes.size());
+ for (int i = 1; i < parts.length; i++) {
+ String flag = parts[i];
+ if (!flagIndexes.containsKey(flag)) {
+ flagStrings.put(flagIndexes.size(), flag);
+ flagIndexes.put(flag, flagIndexes.size());
+ }
+ flags.set(flagIndexes.get(flag));
+ }
+ this.flags = flags;
+ }
+
+ private void parseVLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("v");
+ String noOptLine = line;
+ if (noOptLine.startsWith("opt ")) {
+ noOptLine = noOptLine.substring(4);
+ }
+ if (noOptLine.length() < 3) {
+ throw new DescriptorParseException("Invalid line '" + line + "' in "
+ + "status entry.");
+ } else {
+ this.version = noOptLine.substring(2);
+ }
+ }
+
+ private void parseWLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("w");
+ SortedMap<String, Integer> pairs =
+ ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "=");
+ if (pairs.isEmpty()) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ if (pairs.containsKey("Bandwidth")) {
+ this.bandwidth = pairs.remove("Bandwidth");
+ }
+ if (pairs.containsKey("Measured")) {
+ this.measured = pairs.remove("Measured");
+ }
+ if (pairs.containsKey("Unmeasured")) {
+ this.unmeasured = pairs.remove("Unmeasured") == 1L;
+ }
+ if (!pairs.isEmpty()) {
+ /* Ignore unknown key-value pair. */
+ }
+ }
+
+ private void parsePLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.parsedAtMostOnceKeyword("p");
+ boolean isValid = true;
+ if (parts.length != 3) {
+ isValid = false;
+ } else {
+ switch (parts[1]) {
+ case "accept":
+ case "reject":
+ this.defaultPolicy = parts[1];
+ this.portList = parts[2];
+ String[] ports = parts[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseMLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (this.microdescriptorDigests == null) {
+ this.microdescriptorDigests = new HashSet<>();
+ }
+ if (parts.length == 2) {
+ this.microdescriptorDigests.add(
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[1]));
+ } else if (parts.length == 3 && parts[2].length() > 7) {
+ /* 7 == "sha256=".length() */
+ this.microdescriptorDigests.add(
+ ParseHelper.parseThirtyTwoByteBase64String(line,
+ parts[2].substring(7)));
+ }
+ }
+
+ private void parseIdLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3 || !"ed25519".equals(parts[1])) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else if ("none".equals(parts[2])) {
+ this.masterKeyEd25519 = "none";
+ } else {
+ ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
+ this.masterKeyEd25519 = parts[2];
+ }
+ }
+
+ private void clearAtMostOnceKeywords() {
+ this.atMostOnceKeywords = null;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String descriptor;
+ @Override
+ public String getDescriptor() {
+ return this.descriptor;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private Set<String> microdescriptorDigests;
+ @Override
+ public Set<String> getMicrodescriptorDigests() {
+ return this.microdescriptorDigests == null ? null :
+ new HashSet<>(this.microdescriptorDigests);
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private BitSet flags;
+ @Override
+ public SortedSet<String> getFlags() {
+ SortedSet<String> result = new TreeSet<>();
+ if (this.flags != null) {
+ for (int i = this.flags.nextSetBit(0); i >= 0;
+ i = this.flags.nextSetBit(i + 1)) {
+ result.add(flagStrings.get(i));
+ }
+ }
+ return result;
+ }
+
+ private String version;
+ @Override
+ public String getVersion() {
+ return this.version;
+ }
+
+ private long bandwidth = -1L;
+ @Override
+ public long getBandwidth() {
+ return this.bandwidth;
+ }
+
+ private long measured = -1L;
+ @Override
+ public long getMeasured() {
+ return this.measured;
+ }
+
+ private boolean unmeasured = false;
+ @Override
+ public boolean getUnmeasured() {
+ return this.unmeasured;
+ }
+
+ private String defaultPolicy;
+ @Override
+ public String getDefaultPolicy() {
+ return this.defaultPolicy;
+ }
+
+ private String portList;
+ @Override
+ public String getPortList() {
+ return this.portList;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
new file mode 100644
index 0000000..5fa22c7
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -0,0 +1,270 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.DirectorySignature;
+import org.torproject.descriptor.NetworkStatusEntry;
+
+/* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
+ * consensuses, v2 statuses, and sanitized bridge network statuses and
+ * delegate the specific parts to the subclasses. */
+public abstract class NetworkStatusImpl extends DescriptorImpl {
+
+ protected NetworkStatusImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines,
+ boolean containsDirSourceEntries, boolean blankLinesAllowed)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines,
+ blankLinesAllowed);
+ this.splitAndParseParts(this.rawDescriptorBytes,
+ containsDirSourceEntries);
+ }
+
+ private void splitAndParseParts(byte[] rawDescriptorBytes,
+ boolean containsDirSourceEntries) throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ int startIndex = 0;
+ int firstDirSourceIndex = !containsDirSourceEntries ? -1 :
+ this.findFirstIndexOfKeyword(descriptorString, "dir-source");
+ int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r");
+ int directoryFooterIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-footer");
+ int firstDirectorySignatureIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-signature");
+ int endIndex = descriptorString.length();
+ if (firstDirectorySignatureIndex < 0) {
+ firstDirectorySignatureIndex = endIndex;
+ }
+ if (directoryFooterIndex < 0) {
+ directoryFooterIndex = firstDirectorySignatureIndex;
+ }
+ if (firstRIndex < 0) {
+ firstRIndex = directoryFooterIndex;
+ }
+ if (firstDirSourceIndex < 0) {
+ firstDirSourceIndex = firstRIndex;
+ }
+ if (firstDirSourceIndex > startIndex) {
+ this.parseHeaderBytes(descriptorString, startIndex,
+ firstDirSourceIndex);
+ }
+ if (firstRIndex > firstDirSourceIndex) {
+ this.parseDirSourceBytes(descriptorString, firstDirSourceIndex,
+ firstRIndex);
+ }
+ if (directoryFooterIndex > firstRIndex) {
+ this.parseStatusEntryBytes(descriptorString, firstRIndex,
+ directoryFooterIndex);
+ }
+ if (firstDirectorySignatureIndex > directoryFooterIndex) {
+ this.parseDirectoryFooterBytes(descriptorString,
+ directoryFooterIndex, firstDirectorySignatureIndex);
+ }
+ if (endIndex > firstDirectorySignatureIndex) {
+ this.parseDirectorySignatureBytes(descriptorString,
+ firstDirectorySignatureIndex, endIndex);
+ }
+ }
+
+ private int findFirstIndexOfKeyword(String descriptorString,
+ 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 {
+ return -1;
+ }
+ }
+
+ private void parseHeaderBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ byte[] headerBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ headerBytes, 0, end - start);
+ this.parseHeader(headerBytes);
+ }
+
+ private void parseDirSourceBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ List<byte[]> splitDirSourceBytes =
+ this.splitByKeyword(descriptorString, "dir-source", start, end);
+ for (byte[] dirSourceBytes : splitDirSourceBytes) {
+ this.parseDirSource(dirSourceBytes);
+ }
+ }
+
+ private void parseStatusEntryBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ List<byte[]> splitStatusEntryBytes =
+ this.splitByKeyword(descriptorString, "r", start, end);
+ for (byte[] statusEntryBytes : splitStatusEntryBytes) {
+ this.parseStatusEntry(statusEntryBytes);
+ }
+ }
+
+ private void parseDirectoryFooterBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ byte[] directoryFooterBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ directoryFooterBytes, 0, end - start);
+ this.parseFooter(directoryFooterBytes);
+ }
+
+ private void parseDirectorySignatureBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
+ descriptorString, "directory-signature", start, end);
+ for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
+ this.parseDirectorySignature(directorySignatureBytes);
+ }
+ }
+
+ private List<byte[]> splitByKeyword(String descriptorString,
+ String keyword, int start, int end) {
+ List<byte[]> splitParts = new ArrayList<>();
+ int from = start;
+ while (from < end) {
+ int to = descriptorString.indexOf("\n" + keyword + " ", from);
+ if (to < 0) {
+ to = descriptorString.indexOf("\n" + keyword + "\n", from);
+ }
+ if (to < 0) {
+ to = end;
+ } else {
+ to += 1;
+ }
+ byte[] part = new byte[to - from];
+ System.arraycopy(this.rawDescriptorBytes, from, part, 0,
+ to - from);
+ from = to;
+ splitParts.add(part);
+ }
+ return splitParts;
+ }
+
+ protected abstract void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException;
+
+ protected void parseDirSource(byte[] dirSourceBytes)
+ throws DescriptorParseException {
+ DirSourceEntryImpl dirSourceEntry = new DirSourceEntryImpl(
+ dirSourceBytes, this.failUnrecognizedDescriptorLines);
+ this.dirSourceEntries.put(dirSourceEntry.getIdentity(),
+ dirSourceEntry);
+ List<String> unrecognizedDirSourceLines = dirSourceEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedDirSourceLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedDirSourceLines);
+ }
+ }
+
+ protected String[] parseClientOrServerVersions(String line,
+ String[] parts) throws DescriptorParseException {
+ String[] result = null;
+ switch (parts.length) {
+ case 1:
+ result = new String[0];
+ break;
+ case 2:
+ result = parts[1].split(",", -1);
+ for (String version : result) {
+ if (version.length() < 1) {
+ throw new DescriptorParseException("Illegal versions line '"
+ + line + "'.");
+ }
+ }
+ break;
+ default:
+ throw new DescriptorParseException("Illegal versions line '" + line
+ + "'.");
+ }
+ return result;
+ }
+
+ protected void parseStatusEntry(byte[] statusEntryBytes)
+ throws DescriptorParseException {
+ NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
+ statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
+ this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
+ List<String> unrecognizedStatusEntryLines = statusEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected abstract void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException;
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ if (this.signatures == null) {
+ this.signatures = new ArrayList<>();
+ }
+ DirectorySignatureImpl signature = new DirectorySignatureImpl(
+ directorySignatureBytes, failUnrecognizedDescriptorLines);
+ this.signatures.add(signature);
+ List<String> unrecognizedStatusEntryLines = signature.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected SortedMap<String, DirSourceEntry> dirSourceEntries =
+ new TreeMap<>();
+ public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
+ return new TreeMap<>(this.dirSourceEntries);
+ }
+
+ protected SortedMap<String, NetworkStatusEntry> statusEntries =
+ new TreeMap<>();
+ public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
+ return new TreeMap<>(this.statusEntries);
+ }
+ public boolean containsStatusEntry(String fingerprint) {
+ return this.statusEntries.containsKey(fingerprint);
+ }
+ public NetworkStatusEntry getStatusEntry(String fingerprint) {
+ return this.statusEntries.get(fingerprint);
+ }
+
+ protected List<DirectorySignature> signatures;
+ public List<DirectorySignature> getSignatures() {
+ return this.signatures == null ? null
+ : new ArrayList<>(this.signatures);
+ }
+ public SortedMap<String, DirectorySignature> getDirectorySignatures() {
+ SortedMap<String, DirectorySignature> directorySignatures = null;
+ if (this.signatures != null) {
+ directorySignatures = new TreeMap<>();
+ for (DirectorySignature signature : this.signatures) {
+ directorySignatures.put(signature.getIdentity(), signature);
+ }
+ }
+ return directorySignatures;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
new file mode 100644
index 0000000..82c0813
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -0,0 +1,567 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+
+public class ParseHelper {
+
+ private static Pattern keywordPattern =
+ Pattern.compile("^[A-Za-z0-9-]+$");
+ protected static String parseKeyword(String line, String keyword)
+ throws DescriptorParseException {
+ if (!keywordPattern.matcher(keyword).matches()) {
+ throw new DescriptorParseException("Unrecognized character in "
+ + "keyword '" + keyword + "' in line '" + line + "'.");
+ }
+ return keyword;
+ }
+
+ private static Pattern ipv4Pattern =
+ Pattern.compile("^[0-9\\.]{7,15}$");
+ protected static String parseIpv4Address(String line, String address)
+ throws DescriptorParseException {
+ boolean isValid = true;
+ if (!ipv4Pattern.matcher(address).matches()) {
+ isValid = false;
+ } else {
+ String[] parts = address.split("\\.", -1);
+ if (parts.length != 4) {
+ isValid = false;
+ } else {
+ for (int i = 0; i < 4; i++) {
+ try {
+ int octetValue = Integer.parseInt(parts[i]);
+ if (octetValue < 0 || octetValue > 255) {
+ isValid = false;
+ }
+ } catch (NumberFormatException e) {
+ isValid = false;
+ }
+ }
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("'" + address + "' in line '"
+ + line + "' is not a valid IPv4 address.");
+ }
+ return address;
+ }
+
+ protected static int parsePort(String line, String portString)
+ throws DescriptorParseException {
+ int port = -1;
+ try {
+ port = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("'" + portString + "' in line '"
+ + line + "' is not a valid port number.");
+ }
+ if (port < 0 || port > 65535) {
+ throw new DescriptorParseException("'" + portString + "' in line '"
+ + line + "' is not a valid port number.");
+ }
+ return port;
+ }
+
+ protected static long parseSeconds(String line, String secondsString)
+ throws DescriptorParseException {
+ try {
+ return Long.parseLong(secondsString);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("'" + secondsString + "' in "
+ + "line '" + line + "' is not a valid time in seconds.");
+ }
+ }
+
+ protected static String parseExitPattern(String line, String exitPattern)
+ throws DescriptorParseException {
+ if (!exitPattern.contains(":")) {
+ throw new DescriptorParseException("'" + exitPattern + "' in line '"
+ + line + "' must contain address and port.");
+ }
+ String[] parts = exitPattern.split(":");
+ String addressPart = parts[0];
+ /* TODO Extend to IPv6. */
+ if (addressPart.equals("*")) {
+ /* Nothing to check. */
+ } else if (addressPart.contains("/")) {
+ String[] addressParts = addressPart.split("/");
+ String address = addressParts[0];
+ String mask = addressParts[1];
+ ParseHelper.parseIpv4Address(line, address);
+ if (addressParts.length != 2) {
+ throw new DescriptorParseException("'" + addressPart + "' in "
+ + "line '" + line + "' is not a valid address part.");
+ }
+ if (mask.contains(".")) {
+ ParseHelper.parseIpv4Address(line, mask);
+ } else {
+ int maskValue = -1;
+ try {
+ maskValue = Integer.parseInt(mask);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (maskValue < 0 || maskValue > 32) {
+ throw new DescriptorParseException("'" + mask + "' in line '"
+ + line + "' is not a valid IPv4 mask.");
+ }
+ }
+ } else {
+ ParseHelper.parseIpv4Address(line, addressPart);
+ }
+ String portPart = parts[1];
+ if (portPart.equals("*")) {
+ /* Nothing to check. */
+ } else if (portPart.contains("-")) {
+ String[] portParts = portPart.split("-");
+ String fromPort = portParts[0];
+ ParseHelper.parsePort(line, fromPort);
+ String toPort = portParts[1];
+ ParseHelper.parsePort(line, toPort);
+ } else {
+ ParseHelper.parsePort(line, portPart);
+ }
+ return exitPattern;
+ }
+
+ private static ThreadLocal<Map<String, DateFormat>> dateFormats =
+ new ThreadLocal<Map<String, DateFormat>> () {
+ public Map<String, DateFormat> get() {
+ return super.get();
+ }
+ protected Map<String, DateFormat> initialValue() {
+ return new HashMap<>();
+ }
+ public void remove() {
+ super.remove();
+ }
+ public void set(Map<String, DateFormat> value) {
+ super.set(value);
+ }
+ };
+ static DateFormat getDateFormat(String format) {
+ Map<String, DateFormat> threadDateFormats = dateFormats.get();
+ if (!threadDateFormats.containsKey(format)) {
+ DateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
+ dateFormat.setLenient(false);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ threadDateFormats.put(format, dateFormat);
+ }
+ return threadDateFormats.get(format);
+ }
+
+ protected static long parseTimestampAtIndex(String line, String[] parts,
+ int dateIndex, int timeIndex) throws DescriptorParseException {
+ if (dateIndex >= parts.length || timeIndex >= parts.length) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a timestamp at the expected position.");
+ }
+ long result = -1L;
+ try {
+ DateFormat dateTimeFormat = getDateFormat("yyyy-MM-dd HH:mm:ss");
+ result = dateTimeFormat.parse(
+ parts[dateIndex] + " " + parts[timeIndex]).getTime();
+ } catch (ParseException e) {
+ /* Leave result at -1L. */
+ }
+ if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
+ throw new DescriptorParseException("Illegal timestamp format in "
+ + "line '" + line + "'.");
+ }
+ return result;
+ }
+
+ protected static long parseDateAtIndex(String line, String[] parts,
+ int dateIndex) throws DescriptorParseException {
+ if (dateIndex >= parts.length) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a date at the expected position.");
+ }
+ long result = -1L;
+ try {
+ DateFormat dateFormat = getDateFormat("yyyy-MM-dd");
+ result = dateFormat.parse(parts[dateIndex]).getTime();
+ } catch (ParseException e) {
+ /* Leave result at -1L. */
+ }
+ if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
+ throw new DescriptorParseException("Illegal date format in line '"
+ + line + "'.");
+ }
+ return result;
+ }
+
+ protected static String parseTwentyByteHexString(String line,
+ String hexString) throws DescriptorParseException {
+ return parseHexString(line, hexString, 40);
+ }
+
+ protected static String parseHexString(String line, String hexString)
+ throws DescriptorParseException {
+ return parseHexString(line, hexString, -1);
+ }
+
+ private static Pattern hexPattern = Pattern.compile("^[0-9a-fA-F]*$");
+ private static String parseHexString(String line, String hexString,
+ int expectedLength) throws DescriptorParseException {
+ if (!hexPattern.matcher(hexString).matches() ||
+ hexString.length() % 2 != 0 ||
+ (expectedLength >= 0 && hexString.length() != expectedLength)) {
+ throw new DescriptorParseException("Illegal hex string in line '"
+ + line + "'.");
+ }
+ return hexString.toUpperCase();
+ }
+
+ protected static SortedMap<String, String> parseKeyValueStringPairs(
+ String line, String[] parts, int startIndex, String separatorString)
+ throws DescriptorParseException {
+ SortedMap<String, String> result = new TreeMap<>();
+ for (int i = startIndex; i < parts.length; i++) {
+ String pair = parts[i];
+ String[] pairParts = pair.split(separatorString);
+ if (pairParts.length != 2) {
+ throw new DescriptorParseException("Illegal key-value pair in "
+ + "line '" + line + "'.");
+ }
+ result.put(pairParts[0], pairParts[1]);
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Integer> parseKeyValueIntegerPairs(
+ String line, String[] parts, int startIndex, String separatorString)
+ throws DescriptorParseException {
+ SortedMap<String, Integer> result = new TreeMap<>();
+ SortedMap<String, String> keyValueStringPairs =
+ ParseHelper.parseKeyValueStringPairs(line, parts, startIndex,
+ separatorString);
+ for (Map.Entry<String, String> e : keyValueStringPairs.entrySet()) {
+ try {
+ result.put(e.getKey(), Integer.parseInt(e.getValue()));
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+ return result;
+ }
+
+ private static Pattern nicknamePattern =
+ Pattern.compile("^[0-9a-zA-Z]{1,19}$");
+ protected static String parseNickname(String line, String nickname)
+ throws DescriptorParseException {
+ if (!nicknamePattern.matcher(nickname).matches()) {
+ throw new DescriptorParseException("Illegal nickname in line '"
+ + line + "'.");
+ }
+ return nickname;
+ }
+
+ protected static boolean parseBoolean(String b, String line)
+ throws DescriptorParseException {
+ switch (b) {
+ case "1":
+ return true;
+ case "0":
+ return false;
+ default:
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private static Pattern twentyByteBase64Pattern =
+ Pattern.compile("^[0-9a-zA-Z+/]{27}$");
+ protected static String parseTwentyByteBase64String(String line,
+ String base64String) throws DescriptorParseException {
+ if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
+ throw new DescriptorParseException("'" + base64String
+ + "' in line '" + line + "' is not a valid base64-encoded "
+ + "20-byte value.");
+ }
+ return DatatypeConverter.printHexBinary(
+ DatatypeConverter.parseBase64Binary(base64String + "=")).
+ toUpperCase();
+ }
+
+ private static Pattern thirtyTwoByteBase64Pattern =
+ Pattern.compile("^[0-9a-zA-Z+/]{43}$");
+ protected static String parseThirtyTwoByteBase64String(String line,
+ String base64String) throws DescriptorParseException {
+ if (!thirtyTwoByteBase64Pattern.matcher(base64String).matches()) {
+ throw new DescriptorParseException("'" + base64String
+ + "' in line '" + line + "' is not a valid base64-encoded "
+ + "32-byte value.");
+ }
+ return DatatypeConverter.printHexBinary(
+ DatatypeConverter.parseBase64Binary(base64String + "=")).
+ toUpperCase();
+ }
+
+ private static Map<Integer, Pattern>
+ commaSeparatedKeyValueListPatterns = new HashMap<>();
+ protected static String parseCommaSeparatedKeyIntegerValueList(
+ String line, String[] partsNoOpt, int index, int keyLength)
+ throws DescriptorParseException {
+ String result = "";
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list at index " + index + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected key-value list at "
+ + "index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ if (!commaSeparatedKeyValueListPatterns.containsKey(keyLength)) {
+ String keyPattern = "[0-9a-zA-Z?<>\\-_]"
+ + (keyLength == 0 ? "+" : "{" + keyLength + "}");
+ String valuePattern = "\\-?[0-9]{1,9}";
+ String patternString = String.format("^%s=%s(,%s=%s)*$",
+ keyPattern, valuePattern, keyPattern, valuePattern);
+ commaSeparatedKeyValueListPatterns.put(keyLength,
+ Pattern.compile(patternString));
+ }
+ Pattern pattern = commaSeparatedKeyValueListPatterns.get(
+ keyLength);
+ if (pattern.matcher(partsNoOpt[index]).matches()) {
+ result = partsNoOpt[index];
+ } else {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal key or value.");
+ }
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Integer>
+ convertCommaSeparatedKeyIntegerValueList(String validatedString) {
+ SortedMap<String, Integer> result = null;
+ if (validatedString != null) {
+ result = new TreeMap<>();
+ if (validatedString.contains("=")) {
+ for (String listElement : validatedString.split(",", -1)) {
+ String[] keyAndValue = listElement.split("=");
+ result.put(keyAndValue[0], Integer.parseInt(keyAndValue[1]));
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static SortedMap<String, Long>
+ parseCommaSeparatedKeyLongValueList(String line,
+ String[] partsNoOpt, int index, int keyLength)
+ throws DescriptorParseException {
+ SortedMap<String, Long> result = new TreeMap<>();
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a key-value list at index " + index + ".");
+ } else if (partsNoOpt.length > index + 1 ) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected key-value list at "
+ + "index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ for (String listElement : listElements) {
+ String[] keyAndValue = listElement.split("=");
+ String key = null;
+ long value = -1;
+ if (keyAndValue.length == 2 && (keyLength == 0 ||
+ keyAndValue[0].length() == keyLength)) {
+ 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 Integer[] parseCommaSeparatedIntegerValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ Integer[] result = null;
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ result = new Integer[listElements.length];
+ for (int i = 0; i < listElements.length; i++) {
+ try {
+ result[i] = Integer.parseInt(listElements[i]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElements[i] + "'.");
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static Double[] parseCommaSeparatedDoubleValueList(
+ String line, String[] partsNoOpt, int index)
+ throws DescriptorParseException {
+ Double[] result = null;
+ if (partsNoOpt.length < index) {
+ throw new DescriptorParseException("Line '" + line + "' does not "
+ + "contain a comma-separated value list at index " + index
+ + ".");
+ } else if (partsNoOpt.length > index + 1) {
+ throw new DescriptorParseException("Line '" + line + "' contains "
+ + "unrecognized values beyond the expected comma-separated "
+ + "value list at index " + index + ".");
+ } else if (partsNoOpt.length > index) {
+ String[] listElements = partsNoOpt[index].split(",", -1);
+ result = new Double[listElements.length];
+ for (int i = 0; i < listElements.length; i++) {
+ try {
+ result[i] = Double.parseDouble(listElements[i]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Line '" + line + "' "
+ + "contains an illegal value in list element '"
+ + listElements[i] + "'.");
+ }
+ }
+ }
+ return result;
+ }
+
+ protected static Map<String, Double>
+ parseSpaceSeparatedStringKeyDoubleValueMap(String line,
+ String[] partsNoOpt, int startIndex)
+ throws DescriptorParseException {
+ Map<String, Double> 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;
+ Double value = null;
+ if (keyAndValue.length == 2) {
+ try {
+ value = Double.parseDouble(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 {
+ String identityEd25519CryptoBlockNoNewlines =
+ identityEd25519CryptoBlock.replaceAll("\n", "");
+ String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
+ endEd25519CertLine = "-----END ED25519 CERT-----";
+ if (!identityEd25519CryptoBlockNoNewlines.startsWith(
+ beginEd25519CertLine)) {
+ throw new DescriptorParseException("Illegal start of "
+ + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ + "'.");
+ }
+ if (!identityEd25519CryptoBlockNoNewlines.endsWith(
+ endEd25519CertLine)) {
+ throw new DescriptorParseException("Illegal end of "
+ + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ + "'.");
+ }
+ String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
+ substring(beginEd25519CertLine.length(),
+ identityEd25519CryptoBlock.length()
+ - endEd25519CertLine.length()).replaceAll("=", "");
+ byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
+ identityEd25519Base64);
+ if (identityEd25519.length < 40) {
+ throw new DescriptorParseException("Invalid length of "
+ + "identity-ed25519 (in bytes): " + identityEd25519.length);
+ } else if (identityEd25519[0] != 0x01) {
+ throw new DescriptorParseException("Unknown version in "
+ + "identity-ed25519: " + identityEd25519[0]);
+ } else if (identityEd25519[1] != 0x04) {
+ throw new DescriptorParseException("Unknown cert type in "
+ + "identity-ed25519: " + identityEd25519[1]);
+ } else if (identityEd25519[6] != 0x01) {
+ throw new DescriptorParseException("Unknown certified key type in "
+ + "identity-ed25519: " + identityEd25519[1]);
+ } else if (identityEd25519[39] == 0x00) {
+ throw new DescriptorParseException("No extensions in "
+ + "identity-ed25519 (which would contain the encoded "
+ + "master-key-ed25519): " + identityEd25519[39]);
+ } else {
+ int extensionStart = 40;
+ for (int i = 0; i < (int) identityEd25519[39]; i++) {
+ if (identityEd25519.length < extensionStart + 4) {
+ throw new DescriptorParseException("Invalid extension with id "
+ + i + " in identity-ed25519.");
+ }
+ int extensionLength = identityEd25519[extensionStart];
+ extensionLength <<= 8;
+ extensionLength += identityEd25519[extensionStart + 1];
+ int extensionType = identityEd25519[extensionStart + 2];
+ if (extensionLength == 32 && extensionType == 4) {
+ if (identityEd25519.length < extensionStart + 4 + 32) {
+ throw new DescriptorParseException("Invalid extension with "
+ + "id " + i + " in identity-ed25519.");
+ }
+ byte[] masterKeyEd25519 = new byte[32];
+ System.arraycopy(identityEd25519, extensionStart + 4,
+ masterKeyEd25519, 0, masterKeyEd25519.length);
+ String masterKeyEd25519Base64 = DatatypeConverter.
+ printBase64Binary(masterKeyEd25519).replaceAll("=", "");
+ String masterKeyEd25519Base64NoTrailingEqualSigns =
+ masterKeyEd25519Base64.replaceAll("=", "");
+ return masterKeyEd25519Base64NoTrailingEqualSigns;
+ }
+ extensionStart += 4 + extensionLength;
+ }
+ }
+ throw new DescriptorParseException("Unable to locate "
+ + "master-key-ed25519 in identity-ed25519.");
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
new file mode 100644
index 0000000..1ff15cb
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
@@ -0,0 +1,547 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayDirectory;
+import org.torproject.descriptor.RouterStatusEntry;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* TODO Write unit tests. */
+
+public class RelayDirectoryImpl extends DescriptorImpl
+ implements RelayDirectory {
+
+ protected static List<RelayDirectory> parseDirectories(
+ byte[] directoriesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayDirectory> parsedDirectories = new ArrayList<>();
+ List<byte[]> splitDirectoriesBytes =
+ DescriptorImpl.splitRawDescriptorBytes(directoriesBytes,
+ "signed-directory\n");
+ for (byte[] directoryBytes : splitDirectoriesBytes) {
+ RelayDirectory parsedDirectory =
+ new RelayDirectoryImpl(directoryBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDirectories.add(parsedDirectory);
+ }
+ return parsedDirectories;
+ }
+
+ protected RelayDirectoryImpl(byte[] directoryBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ 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();
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "signed-directory\n";
+ String sigToken = "\ndirectory-signature ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ sig = ascii.indexOf("\n", sig) + 1;
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.directoryDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.directoryDigest == null) {
+ throw new DescriptorParseException("Could not calculate v1 "
+ + "directory digest.");
+ }
+ }
+
+ private void splitAndParseParts(byte[] rawDescriptorBytes)
+ throws DescriptorParseException {
+ if (this.rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ String descriptorString = new String(rawDescriptorBytes);
+ int startIndex = 0;
+ int firstRouterIndex = this.findFirstIndexOfKeyword(descriptorString,
+ "router");
+ int directorySignatureIndex = this.findFirstIndexOfKeyword(
+ descriptorString, "directory-signature");
+ int endIndex = descriptorString.length();
+ if (directorySignatureIndex < 0) {
+ directorySignatureIndex = endIndex;
+ }
+ if (firstRouterIndex < 0) {
+ firstRouterIndex = directorySignatureIndex;
+ }
+ if (firstRouterIndex > startIndex) {
+ this.parseHeaderBytes(descriptorString, startIndex,
+ firstRouterIndex);
+ }
+ if (directorySignatureIndex > firstRouterIndex) {
+ this.parseServerDescriptorBytes(descriptorString, firstRouterIndex,
+ directorySignatureIndex);
+ }
+ if (endIndex > directorySignatureIndex) {
+ this.parseDirectorySignatureBytes(descriptorString,
+ directorySignatureIndex, endIndex);
+ }
+ }
+
+ private int findFirstIndexOfKeyword(String descriptorString,
+ 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 {
+ return -1;
+ }
+ }
+
+ private void parseHeaderBytes(String descriptorString, int start,
+ int end) throws DescriptorParseException {
+ byte[] headerBytes = new byte[end - start];
+ System.arraycopy(this.rawDescriptorBytes, start,
+ headerBytes, 0, end - start);
+ this.parseHeader(headerBytes);
+ }
+
+ private void parseServerDescriptorBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitServerDescriptorBytes =
+ this.splitByKeyword(descriptorString, "router", start, end);
+ for (byte[] statusEntryBytes : splitServerDescriptorBytes) {
+ this.parseServerDescriptor(statusEntryBytes);
+ }
+ }
+
+ private void parseDirectorySignatureBytes(String descriptorString,
+ int start, int end) throws DescriptorParseException {
+ List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
+ descriptorString, "directory-signature", start, end);
+ for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
+ this.parseDirectorySignature(directorySignatureBytes);
+ }
+ }
+
+ private List<byte[]> splitByKeyword(String descriptorString,
+ String keyword, int start, int end) {
+ List<byte[]> splitParts = new ArrayList<>();
+ int from = start;
+ while (from < end) {
+ int to = descriptorString.indexOf("\n" + keyword + " ", from);
+ if (to < 0) {
+ to = descriptorString.indexOf("\n" + keyword + "\n", from);
+ }
+ if (to < 0) {
+ to = end;
+ } else {
+ to += 1;
+ }
+ int toNoNewline = to;
+ while (toNoNewline > from &&
+ descriptorString.charAt(toNoNewline - 1) == '\n') {
+ toNoNewline--;
+ }
+ byte[] part = new byte[toNoNewline - from];
+ System.arraycopy(this.rawDescriptorBytes, from, part, 0,
+ toNoNewline - from);
+ from = to;
+ splitParts.add(part);
+ }
+ return splitParts;
+ }
+
+ private void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String publishedLine = null, nextCrypto = "",
+ runningRoutersLine = null, routerStatusLine = null;
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.isEmpty() || line.startsWith("@")) {
+ continue;
+ }
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "signed-directory":
+ this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ if (publishedLine != null) {
+ throw new DescriptorParseException("Keyword 'published' is "
+ + "contained more than once, but must be contained exactly "
+ + "once.");
+ } else {
+ publishedLine = line;
+ }
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "recommended-software":
+ this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "running-routers":
+ runningRoutersLine = line;
+ break;
+ case "router-status":
+ routerStatusLine = line;
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key") &&
+ this.dirSigningKey == null) {
+ this.dirSigningKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v1 directory.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in v1 directory.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ if (publishedLine == null) {
+ 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[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+");
+ this.parsePublishedLine(publishedLine, publishedLineNoOpt,
+ publishedPartsNoOpt);
+ }
+ if (routerStatusLine != null) {
+ String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") ?
+ routerStatusLine.substring("opt ".length()) : routerStatusLine;
+ String[] routerStatusPartsNoOpt =
+ routerStatusLineNoOpt.split("[ \t]+");
+ this.parseRouterStatusLine(routerStatusLine, routerStatusLineNoOpt,
+ routerStatusPartsNoOpt);
+ } else if (runningRoutersLine != null) {
+ String runningRoutersLineNoOpt =
+ runningRoutersLine.startsWith("opt ") ?
+ runningRoutersLine.substring("opt ".length()) :
+ runningRoutersLine;
+ String[] runningRoutersPartsNoOpt =
+ runningRoutersLineNoOpt.split("[ \t]+");
+ this.parseRunningRoutersLine(runningRoutersLine,
+ runningRoutersLineNoOpt, runningRoutersPartsNoOpt);
+ } else {
+ throw new DescriptorParseException("Either running-routers or "
+ + "router-status line must be given.");
+ }
+ }
+
+ protected void parseServerDescriptor(byte[] serverDescriptorBytes) {
+ try {
+ ServerDescriptorImpl serverDescriptor =
+ new RelayServerDescriptorImpl(serverDescriptorBytes,
+ this.failUnrecognizedDescriptorLines);
+ this.serverDescriptors.add(serverDescriptor);
+ } catch (DescriptorParseException e) {
+ this.serverDescriptorParseExceptions.add(e);
+ }
+ }
+
+ private void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(directorySignatureBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseSignedDirectoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("signed-directory")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseDirSigningKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length > 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ } else if (partsNoOpt.length == 2) {
+ /* Early directories didn't have a crypto object following the
+ * "dir-signing-key" line, but had the key base64-encoded in the
+ * same line. */
+ StringBuilder sb = new StringBuilder();
+ sb.append("-----BEGIN RSA PUBLIC KEY-----\n");
+ String keyString = partsNoOpt[1];
+ while (keyString.length() > 64) {
+ sb.append(keyString.substring(0, 64)).append("\n");
+ keyString = keyString.substring(64);
+ }
+ if (keyString.length() > 0) {
+ sb.append(keyString).append("\n");
+ }
+ sb.append("-----END RSA PUBLIC KEY-----\n");
+ this.dirSigningKey = sb.toString();
+ }
+ }
+
+ private void parseRecommendedSoftwareLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ List<String> result = new ArrayList<>();
+ if (partsNoOpt.length > 2) {
+ throw new DescriptorParseException("Illegal versions line '" + line
+ + "'.");
+ } else if (partsNoOpt.length == 2) {
+ String[] versions = partsNoOpt[1].split(",", -1);
+ for (int i = 0; i < versions.length; i++) {
+ String version = versions[i];
+ if (version.length() < 1) {
+ throw new DescriptorParseException("Illegal versions line '"
+ + line + "'.");
+ }
+ result.add(version);
+ }
+ }
+ this.recommendedSoftware = result;
+ }
+
+ private void parseRunningRoutersLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ String part = partsNoOpt[i];
+ String debugLine = "running-routers [...] " + part + " [...]";
+ boolean isLive = true;
+ if (part.startsWith("!")) {
+ isLive = false;
+ part = part.substring(1);
+ }
+ boolean isVerified;
+ String fingerprint = null, nickname = null;
+ if (part.startsWith("$")) {
+ isVerified = false;
+ fingerprint = ParseHelper.parseTwentyByteHexString(debugLine,
+ part.substring(1));
+ } else {
+ isVerified = true;
+ nickname = ParseHelper.parseNickname(debugLine, part);
+ }
+ this.statusEntries.add(new RouterStatusEntryImpl(fingerprint,
+ nickname, isLive, isVerified));
+ }
+ }
+
+ private void parseRouterStatusLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ String part = partsNoOpt[i];
+ String debugLine = "router-status [...] " + part + " [...]";
+ RouterStatusEntry entry = null;
+ if (part.contains("=")) {
+ String[] partParts = part.split("=");
+ if (partParts.length == 2) {
+ boolean isVerified = true, isLive;
+ String nickname;
+ if (partParts[0].startsWith("!")) {
+ isLive = false;
+ nickname = ParseHelper.parseNickname(debugLine,
+ partParts[0].substring(1));
+ } else {
+ isLive = true;
+ nickname = ParseHelper.parseNickname(debugLine, partParts[0]);
+ }
+ String fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, partParts[1].substring(1));
+ entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
+ isVerified);
+ }
+ } else {
+ boolean isVerified = false, isLive;
+ String nickname = null, fingerprint;
+ if (part.startsWith("!")) {
+ isLive = false;
+ fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, part.substring(2));
+ } else {
+ isLive = true;
+ fingerprint = ParseHelper.parseTwentyByteHexString(
+ debugLine, part.substring(1));;
+ }
+ entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
+ isVerified);
+ }
+ if (entry == null) {
+ throw new DescriptorParseException("Illegal router-status entry '"
+ + part + "' in v1 directory.");
+ }
+ this.statusEntries.add(entry);
+ }
+ }
+
+ private void parseDirectorySignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private List<String> recommendedSoftware;
+ @Override
+ public List<String> getRecommendedSoftware() {
+ return this.recommendedSoftware == null ? null :
+ new ArrayList<>(this.recommendedSoftware);
+ }
+
+ private String directorySignature;
+ @Override
+ public String getDirectorySignature() {
+ return this.directorySignature;
+ }
+
+ private List<RouterStatusEntry> statusEntries = new ArrayList<>();
+ @Override
+ public List<RouterStatusEntry> getRouterStatusEntries() {
+ return new ArrayList<>(this.statusEntries);
+ }
+
+ private List<ServerDescriptor> serverDescriptors = new ArrayList<>();
+ @Override
+ public List<ServerDescriptor> getServerDescriptors() {
+ return new ArrayList<>(this.serverDescriptors);
+ }
+
+ private List<Exception> serverDescriptorParseExceptions =
+ new ArrayList<>();
+ @Override
+ public List<Exception> getServerDescriptorParseExceptions() {
+ return new ArrayList<>(this.serverDescriptorParseExceptions);
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String directoryDigest;
+ @Override
+ public String getDirectoryDigest() {
+ return this.directoryDigest;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
new file mode 100644
index 0000000..73d4dfa
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.RelayExtraInfoDescriptor;
+
+public class RelayExtraInfoDescriptorImpl
+ extends ExtraInfoDescriptorImpl implements RelayExtraInfoDescriptor {
+
+ protected static List<ExtraInfoDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "extra-info ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ExtraInfoDescriptor parsedDescriptor =
+ new RelayExtraInfoDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected RelayExtraInfoDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
new file mode 100644
index 0000000..fe045c1
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -0,0 +1,414 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* Contains a network status consensus or microdesc consensus. */
+public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
+ implements RelayNetworkStatusConsensus {
+
+ protected static List<RelayNetworkStatusConsensus> parseConsensuses(
+ byte[] consensusesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatusConsensus> parsedConsensuses =
+ new ArrayList<>();
+ List<byte[]> splitConsensusBytes =
+ DescriptorImpl.splitRawDescriptorBytes(consensusesBytes,
+ "network-status-version 3");
+ for (byte[] consensusBytes : splitConsensusBytes) {
+ RelayNetworkStatusConsensus parsedConsensus =
+ new RelayNetworkStatusConsensusImpl(consensusBytes,
+ failUnrecognizedDescriptorLines);
+ parsedConsensuses.add(parsedConsensus);
+ }
+ return parsedConsensuses;
+ }
+
+ protected RelayNetworkStatusConsensusImpl(byte[] consensusBytes,
+ 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,params,directory-footer,"
+ + "bandwidth-weights").split(",")));
+ this.checkAtMostOnceKeywords(atMostOnceKeywords);
+ this.checkFirstKeyword("network-status-version");
+ this.clearParsedKeywords();
+ 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 ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.consensusDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.consensusDigest == null) {
+ throw new DescriptorParseException("Could not calculate consensus "
+ + "digest.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-method":
+ this.parseConsensusMethodLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private boolean microdescConsensus = false;
+ protected void parseStatusEntry(byte[] statusEntryBytes)
+ throws DescriptorParseException {
+ NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
+ statusEntryBytes, this.microdescConsensus,
+ this.failUnrecognizedDescriptorLines);
+ this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
+ List<String> unrecognizedStatusEntryLines = statusEntry.
+ getAndClearUnrecognizedLines();
+ if (unrecognizedStatusEntryLines != null) {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-footer":
+ break;
+ case "bandwidth-weights":
+ this.parseBandwidthWeightsLine(line, parts);
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in consensus.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.startsWith("network-status-version 3")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 3;
+ if (parts.length == 3) {
+ this.consensusFlavor = parts[2];
+ if (this.consensusFlavor.equals("microdesc")) {
+ this.microdescConsensus = true;
+ }
+ } else if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "line '" + line + "'.");
+ }
+ }
+
+ private void parseVoteStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || !parts[1].equals("consensus")) {
+ throw new DescriptorParseException("Line '" + line + "' indicates "
+ + "that this is not a consensus.");
+ }
+ }
+
+ private void parseConsensusMethodLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in consensus.");
+ }
+ try {
+ this.consensusMethod = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ if (this.consensusMethod < 1) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ }
+
+ private void parseValidAfterLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFreshUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseVotingDelayLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ try {
+ this.voteSeconds = Long.parseLong(parts[1]);
+ this.distSeconds = Long.parseLong(parts[2]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePackageLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 5) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ if (this.packageLines == null) {
+ this.packageLines = new ArrayList<>();
+ }
+ this.packageLines.add(line.substring("package ".length()));
+ }
+
+ private void parseKnownFlagsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No known flags in line '" + line
+ + "'.");
+ }
+ String[] knownFlags = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ knownFlags[i - 1] = parts[i];
+ }
+ this.knownFlags = knownFlags;
+ }
+
+ private void parseParamsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private void parseBandwidthWeightsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private String consensusDigest;
+ @Override
+ public String getConsensusDigest() {
+ return this.consensusDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private String consensusFlavor;
+ @Override
+ public String getConsensusFlavor() {
+ return this.consensusFlavor;
+ }
+
+ private int consensusMethod;
+ @Override
+ public int getConsensusMethod() {
+ return this.consensusMethod;
+ }
+
+ private long validAfterMillis;
+ @Override
+ public long getValidAfterMillis() {
+ return this.validAfterMillis;
+ }
+
+ private long freshUntilMillis;
+ @Override
+ public long getFreshUntilMillis() {
+ return this.freshUntilMillis;
+ }
+
+ private long validUntilMillis;
+ @Override
+ public long getValidUntilMillis() {
+ return this.validUntilMillis;
+ }
+
+ private long voteSeconds;
+ @Override
+ public long getVoteSeconds() {
+ return this.voteSeconds;
+ }
+
+ private long distSeconds;
+ @Override
+ public long getDistSeconds() {
+ return this.distSeconds;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private List<String> packageLines;
+ @Override
+ public List<String> getPackageLines() {
+ return this.packageLines == null ? null
+ : new ArrayList<>(this.packageLines);
+ }
+
+ private String[] knownFlags;
+ @Override
+ public SortedSet<String> getKnownFlags() {
+ return new TreeSet<>(Arrays.asList(this.knownFlags));
+ }
+
+ private SortedMap<String, Integer> consensusParams;
+ @Override
+ public SortedMap<String, Integer> getConsensusParams() {
+ return this.consensusParams == null ? null:
+ new TreeMap<>(this.consensusParams);
+ }
+
+ private SortedMap<String, Integer> bandwidthWeights;
+ @Override
+ public SortedMap<String, Integer> getBandwidthWeights() {
+ return this.bandwidthWeights == null ? null :
+ new TreeMap<>(this.bandwidthWeights);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
new file mode 100644
index 0000000..a5469db
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
@@ -0,0 +1,384 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayNetworkStatus;
+
+/* TODO Write unit tests. */
+
+public class RelayNetworkStatusImpl extends NetworkStatusImpl
+ implements RelayNetworkStatus {
+
+ protected static List<RelayNetworkStatus> parseStatuses(
+ byte[] statusesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatus> parsedStatuses = new ArrayList<>();
+ List<byte[]> splitStatusBytes =
+ DescriptorImpl.splitRawDescriptorBytes(statusesBytes,
+ "network-status-version 2");
+ for (byte[] statusBytes : splitStatusBytes) {
+ RelayNetworkStatus parsedStatus = new RelayNetworkStatusImpl(
+ statusBytes, failUnrecognizedDescriptorLines);
+ parsedStatuses.add(parsedStatus);
+ }
+ return parsedStatuses;
+ }
+
+ protected RelayNetworkStatusImpl(byte[] statusBytes,
+ 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();
+ 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 ";
+ if (!ascii.contains(sigToken)) {
+ return;
+ }
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ sig = ascii.indexOf("\n", sig) + 1;
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.statusDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.statusDigest == null) {
+ throw new DescriptorParseException("Could not calculate status "
+ + "digest.");
+ }
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.isEmpty()) {
+ continue;
+ }
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "dir-options":
+ this.parseDirOptionsLine(line, parts);
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("dir-signing-key")) {
+ this.dirSigningKey = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ throw new DescriptorParseException("No directory footer expected in "
+ + "v2 network status.");
+ }
+
+ protected void parseDirectorySignature(byte[] directorySignatureBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(directorySignatureBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "directory-signature":
+ this.parseDirectorySignatureLine(line, parts);
+ nextCrypto = "directory-signature";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ if (nextCrypto.equals("directory-signature")) {
+ this.directorySignature = cryptoString;
+ } else {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in v2 network status.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '" + line
+ + "' in v2 network status.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("network-status-version 2")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 2;
+ }
+
+ private void parseDirSourceLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 4) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in v2 network status.");
+ }
+ if (parts[1].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.address = ParseHelper.parseIpv4Address(line, parts[2]);
+ this.dirPort = ParseHelper.parsePort(line, parts[3]);
+ }
+
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in v2 network status.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ parts[1]);
+ }
+
+ private void parseContactLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseDirOptionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ String[] dirOptions = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ dirOptions[i - 1] = parts[i];
+ }
+ this.dirOptions = dirOptions;
+ }
+
+ private void parseDirectorySignatureLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ }
+
+ private String statusDigest;
+ @Override
+ public String getStatusDigest() {
+ return this.statusDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirport() {
+ return this.dirPort;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String[] dirOptions;
+ @Override
+ public SortedSet<String> getDirOptions() {
+ return new TreeSet<>(Arrays.asList(this.dirOptions));
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String directorySignature;
+ @Override
+ public String getDirectorySignature() {
+ return this.directorySignature;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
new file mode 100644
index 0000000..384ad1f
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -0,0 +1,761 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.torproject.descriptor.RelayNetworkStatusVote;
+
+/* Contains a network status vote. */
+public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
+ implements RelayNetworkStatusVote {
+
+ protected static List<RelayNetworkStatusVote> parseVotes(
+ byte[] votesBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<RelayNetworkStatusVote> parsedVotes = new ArrayList<>();
+ List<byte[]> splitVotesBytes =
+ DescriptorImpl.splitRawDescriptorBytes(votesBytes,
+ "network-status-version 3");
+ for (byte[] voteBytes : splitVotesBytes) {
+ RelayNetworkStatusVote parsedVote =
+ new RelayNetworkStatusVoteImpl(voteBytes,
+ failUnrecognizedDescriptorLines);
+ parsedVotes.add(parsedVote);
+ }
+ return parsedVotes;
+ }
+
+ protected RelayNetworkStatusVoteImpl(byte[] voteBytes,
+ 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,"
+ + "flag-thresholds,params,contact,"
+ + "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();
+ }
+
+ protected void parseHeader(byte[] headerBytes)
+ throws DescriptorParseException {
+ /* Initialize flag-thresholds values here for the case that the vote
+ * doesn't contain those values. Initializing them in the constructor
+ * or when declaring variables wouldn't work, because those parts are
+ * evaluated later and would overwrite everything we parse here. */
+ this.stableUptime = -1L;
+ this.stableMtbf = -1L;
+ this.fastBandwidth = -1L;
+ this.guardWfu = -1.0;
+ this.guardTk = -1L;
+ this.guardBandwidthIncludingExits = -1L;
+ this.guardBandwidthExcludingExits = -1L;
+ this.enoughMtbfInfo = -1;
+ this.ignoringAdvertisedBws = -1;
+
+ Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
+ String nextCrypto = "";
+ StringBuilder crypto = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ String[] parts = line.split("[ \t]+");
+ String keyword = parts[0];
+ switch (keyword) {
+ case "network-status-version":
+ this.parseNetworkStatusVersionLine(line, parts);
+ break;
+ case "vote-status":
+ this.parseVoteStatusLine(line, parts);
+ break;
+ case "consensus-methods":
+ this.parseConsensusMethodsLine(line, parts);
+ break;
+ case "published":
+ this.parsePublishedLine(line, parts);
+ break;
+ case "valid-after":
+ this.parseValidAfterLine(line, parts);
+ break;
+ case "fresh-until":
+ this.parseFreshUntilLine(line, parts);
+ break;
+ case "valid-until":
+ this.parseValidUntilLine(line, parts);
+ break;
+ case "voting-delay":
+ this.parseVotingDelayLine(line, parts);
+ break;
+ case "client-versions":
+ this.parseClientVersionsLine(line, parts);
+ break;
+ case "server-versions":
+ this.parseServerVersionsLine(line, parts);
+ break;
+ case "package":
+ this.parsePackageLine(line, parts);
+ break;
+ case "known-flags":
+ this.parseKnownFlagsLine(line, parts);
+ break;
+ case "flag-thresholds":
+ this.parseFlagThresholdsLine(line, parts);
+ break;
+ case "params":
+ this.parseParamsLine(line, parts);
+ break;
+ case "dir-source":
+ this.parseDirSourceLine(line, parts);
+ break;
+ case "contact":
+ this.parseContactLine(line, parts);
+ break;
+ case "dir-key-certificate-version":
+ this.parseDirKeyCertificateVersionLine(line, parts);
+ break;
+ case "dir-address":
+ this.parseDirAddressLine(line, parts);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, parts);
+ break;
+ case "legacy-dir-key":
+ this.parseLegacyDirKeyLine(line, parts);
+ break;
+ case "dir-key-published":
+ this.parseDirKeyPublished(line, parts);
+ break;
+ case "dir-key-expires":
+ this.parseDirKeyExpiresLine(line, parts);
+ break;
+ case "dir-identity-key":
+ this.parseDirIdentityKeyLine(line, parts);
+ nextCrypto = "dir-identity-key";
+ break;
+ case "dir-signing-key":
+ this.parseDirSigningKeyLine(line, parts);
+ nextCrypto = "dir-signing-key";
+ break;
+ case "dir-key-crosscert":
+ this.parseDirKeyCrosscertLine(line, parts);
+ nextCrypto = "dir-key-crosscert";
+ break;
+ case "dir-key-certification":
+ this.parseDirKeyCertificationLine(line, parts);
+ nextCrypto = "dir-key-certification";
+ break;
+ case "-----BEGIN":
+ crypto = new StringBuilder();
+ crypto.append(line).append("\n");
+ break;
+ case "-----END":
+ crypto.append(line).append("\n");
+ String cryptoString = crypto.toString();
+ crypto = null;
+ switch (nextCrypto) {
+ case "dir-identity-key":
+ this.dirIdentityKey = cryptoString;
+ break;
+ case "dir-signing-key":
+ this.dirSigningKey = cryptoString;
+ break;
+ case "dir-key-crosscert":
+ this.dirKeyCrosscert = cryptoString;
+ break;
+ case "dir-key-certification":
+ this.dirKeyCertification = cryptoString;
+ break;
+ default:
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block in vote.");
+ }
+ nextCrypto = "";
+ break;
+ default:
+ if (crypto != null) {
+ crypto.append(line).append("\n");
+ } else {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in vote.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseNetworkStatusVersionLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("network-status-version 3")) {
+ throw new DescriptorParseException("Illegal network status version "
+ + "number in line '" + line + "'.");
+ }
+ this.networkStatusVersion = 3;
+ }
+
+ private void parseVoteStatusLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2 || !parts[1].equals("vote")) {
+ throw new DescriptorParseException("Line '" + line + "' indicates "
+ + "that this is not a vote.");
+ }
+ }
+
+ private void parseConsensusMethodsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ Integer[] consensusMethods = new Integer[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ int consensusMethod = -1;
+ try {
+ consensusMethod = Integer.parseInt(parts[i]);
+ } catch (NumberFormatException e) {
+ /* We'll notice below that consensusMethod is still -1. */
+ }
+ if (consensusMethod < 1) {
+ throw new DescriptorParseException("Illegal consensus method "
+ + "number in line '" + line + "'.");
+ }
+ consensusMethods[i - 1] = consensusMethod;
+ }
+ this.consensusMethods = consensusMethods;
+ }
+
+ private void parsePublishedLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidAfterLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseFreshUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseValidUntilLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
+ 1, 2);
+ }
+
+ private void parseVotingDelayLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ try {
+ this.voteSeconds = Long.parseLong(parts[1]);
+ this.distSeconds = Long.parseLong(parts[2]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseClientVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedClientVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parseServerVersionsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.recommendedServerVersions = this.parseClientOrServerVersions(
+ line, parts);
+ }
+
+ private void parsePackageLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 5) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ if (this.packageLines == null) {
+ this.packageLines = new ArrayList<>();
+ }
+ this.packageLines.add(line.substring("package ".length()));
+ }
+
+ private void parseKnownFlagsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No known flags in line '" + line
+ + "'.");
+ }
+ String[] knownFlags = new String[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ knownFlags[i - 1] = parts[i];
+ }
+ this.knownFlags = knownFlags;
+ }
+
+ private void parseFlagThresholdsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length < 2) {
+ throw new DescriptorParseException("No flag thresholds in line '"
+ + line + "'.");
+ }
+ SortedMap<String, String> flagThresholds =
+ ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+ try {
+ for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+ switch (e.getKey()) {
+ case "stable-uptime":
+ this.stableUptime = Long.parseLong(e.getValue());
+ break;
+ case "stable-mtbf":
+ this.stableMtbf = Long.parseLong(e.getValue());
+ break;
+ case "fast-speed":
+ this.fastBandwidth = Long.parseLong(e.getValue());
+ break;
+ case "guard-wfu":
+ this.guardWfu = Double.parseDouble(e.getValue().
+ replaceAll("%", ""));
+ break;
+ case "guard-tk":
+ this.guardTk = Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-inc-exits":
+ this.guardBandwidthIncludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "guard-bw-exc-exits":
+ this.guardBandwidthExcludingExits =
+ Long.parseLong(e.getValue());
+ break;
+ case "enough-mtbf":
+ this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+ break;
+ case "ignoring-advertised-bws":
+ this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+ break;
+ default:
+ // empty
+ }
+ }
+ } catch (NumberFormatException ex) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+
+ private void parseParamsLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+ parts, 1, "=");
+ }
+
+ private void parseDirSourceLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 7) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, parts[1]);
+ this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
+ if (parts[3].length() < 1) {
+ throw new DescriptorParseException("Illegal hostname in '" + line
+ + "'.");
+ }
+ this.hostname = parts[3];
+ this.address = ParseHelper.parseIpv4Address(line, parts[4]);
+ this.dirPort = ParseHelper.parsePort(line, parts[5]);
+ this.orPort = ParseHelper.parsePort(line, parts[6]);
+ }
+
+ private void parseContactLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (line.length() > "contact ".length()) {
+ this.contactLine = line.substring("contact ".length());
+ } else {
+ this.contactLine = "";
+ }
+ }
+
+ private void parseDirKeyCertificateVersionLine(String line,
+ String[] parts) throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ try {
+ this.dirKeyCertificateVersion = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal dir key certificate "
+ + "version in line '" + line + "'.");
+ }
+ if (this.dirKeyCertificateVersion < 1) {
+ throw new DescriptorParseException("Illegal dir key certificate "
+ + "version in line '" + line + "'.");
+ }
+ }
+
+ private void parseDirAddressLine(String line, String[] parts) {
+ /* Nothing new to learn here. Also, this line hasn't been observed
+ * "in the wild" yet. Maybe it's just an urban legend. */
+ }
+
+ private void parseFingerprintLine(String line, String[] parts)
+ throws DescriptorParseException {
+ /* Nothing new to learn here. We already know the fingerprint from
+ * the dir-source line. But we should at least check that there's a
+ * valid fingerprint in this line. */
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in vote.");
+ }
+ ParseHelper.parseTwentyByteHexString(line, parts[1]);
+ }
+
+ private void parseLegacyDirKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (parts.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line, parts[1]);
+ }
+
+ private void parseDirKeyPublished(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirKeyExpiresLine(String line, String[] parts)
+ throws DescriptorParseException {
+ this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
+ parts, 1, 2);
+ }
+
+ private void parseDirIdentityKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-identity-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirSigningKeyLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCrosscertLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-crosscert")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseDirKeyCertificationLine(String line, String[] parts)
+ throws DescriptorParseException {
+ if (!line.equals("dir-key-certification")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ protected void parseFooter(byte[] footerBytes)
+ throws DescriptorParseException {
+ Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
+ while (s.hasNext()) {
+ String line = s.next();
+ if (!line.equals("directory-footer")) {
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in vote.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String identity;
+ @Override
+ public String getIdentity() {
+ return this.identity;
+ }
+
+ private String hostname;
+ @Override
+ public String getHostname() {
+ return this.hostname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirport() {
+ return this.dirPort;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrport() {
+ return this.orPort;
+ }
+
+ private String contactLine;
+ @Override
+ public String getContactLine() {
+ return this.contactLine;
+ }
+
+ private int dirKeyCertificateVersion;
+ @Override
+ public int getDirKeyCertificateVersion() {
+ return this.dirKeyCertificateVersion;
+ }
+
+ private String legacyDirKey;
+ @Override
+ public String getLegacyDirKey() {
+ return this.legacyDirKey;
+ }
+
+ private long dirKeyPublishedMillis;
+ @Override
+ public long getDirKeyPublishedMillis() {
+ return this.dirKeyPublishedMillis;
+ }
+
+ private long dirKeyExpiresMillis;
+ @Override
+ public long getDirKeyExpiresMillis() {
+ return this.dirKeyExpiresMillis;
+ }
+
+ private String dirIdentityKey;
+ @Override
+ public String getDirIdentityKey() {
+ return this.dirIdentityKey;
+ }
+
+ private String dirSigningKey;
+ @Override
+ public String getDirSigningKey() {
+ return this.dirSigningKey;
+ }
+
+ private String dirKeyCrosscert;
+ @Override
+ public String getDirKeyCrosscert() {
+ return this.dirKeyCrosscert;
+ }
+
+ private String dirKeyCertification;
+ @Override
+ public String getDirKeyCertification() {
+ return this.dirKeyCertification;
+ }
+
+ @Override
+ public String getSigningKeyDigest() {
+ String signingKeyDigest = null;
+ if (this.signatures != null && !this.signatures.isEmpty()) {
+ for (DirectorySignature signature : this.signatures) {
+ if (DirectorySignatureImpl.DEFAULT_ALGORITHM.equals(
+ signature.getAlgorithm())) {
+ signingKeyDigest = signature.getSigningKeyDigest();
+ break;
+ }
+ }
+ }
+ return signingKeyDigest;
+ }
+
+ private int networkStatusVersion;
+ @Override
+ public int getNetworkStatusVersion() {
+ return this.networkStatusVersion;
+ }
+
+ private Integer[] consensusMethods;
+ @Override
+ public List<Integer> getConsensusMethods() {
+ return this.consensusMethods == null ? null :
+ Arrays.asList(this.consensusMethods);
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private long validAfterMillis;
+ @Override
+ public long getValidAfterMillis() {
+ return this.validAfterMillis;
+ }
+
+ private long freshUntilMillis;
+ @Override
+ public long getFreshUntilMillis() {
+ return this.freshUntilMillis;
+ }
+
+ private long validUntilMillis;
+ @Override
+ public long getValidUntilMillis() {
+ return this.validUntilMillis;
+ }
+
+ private long voteSeconds;
+ @Override
+ public long getVoteSeconds() {
+ return this.voteSeconds;
+ }
+
+ private long distSeconds;
+ @Override
+ public long getDistSeconds() {
+ return this.distSeconds;
+ }
+
+ private String[] recommendedClientVersions;
+ @Override
+ public List<String> getRecommendedClientVersions() {
+ return this.recommendedClientVersions == null ? null :
+ Arrays.asList(this.recommendedClientVersions);
+ }
+
+ private String[] recommendedServerVersions;
+ @Override
+ public List<String> getRecommendedServerVersions() {
+ return this.recommendedServerVersions == null ? null :
+ Arrays.asList(this.recommendedServerVersions);
+ }
+
+ private List<String> packageLines;
+ @Override
+ public List<String> getPackageLines() {
+ return this.packageLines == null ? null
+ : new ArrayList<>(this.packageLines);
+ }
+
+ private String[] knownFlags;
+ @Override
+ public SortedSet<String> getKnownFlags() {
+ return new TreeSet<>(Arrays.asList(this.knownFlags));
+ }
+
+ private long stableUptime;
+ @Override
+ public long getStableUptime() {
+ return this.stableUptime;
+ }
+
+ private long stableMtbf;
+ @Override
+ public long getStableMtbf() {
+ return this.stableMtbf;
+ }
+
+ private long fastBandwidth;
+ @Override
+ public long getFastBandwidth() {
+ return this.fastBandwidth;
+ }
+
+ private double guardWfu;
+ @Override
+ public double getGuardWfu() {
+ return this.guardWfu;
+ }
+
+ private long guardTk;
+ @Override
+ public long getGuardTk() {
+ return this.guardTk;
+ }
+
+ private long guardBandwidthIncludingExits;
+ @Override
+ public long getGuardBandwidthIncludingExits() {
+ return this.guardBandwidthIncludingExits;
+ }
+
+ private long guardBandwidthExcludingExits;
+ @Override
+ public long getGuardBandwidthExcludingExits() {
+ return this.guardBandwidthExcludingExits;
+ }
+
+ private int enoughMtbfInfo;
+ @Override
+ public int getEnoughMtbfInfo() {
+ return this.enoughMtbfInfo;
+ }
+
+ private int ignoringAdvertisedBws;
+ @Override
+ public int getIgnoringAdvertisedBws() {
+ return this.ignoringAdvertisedBws;
+ }
+
+ private SortedMap<String, Integer> consensusParams;
+ @Override
+ public SortedMap<String, Integer> getConsensusParams() {
+ return this.consensusParams == null ? null:
+ new TreeMap<>(this.consensusParams);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
new file mode 100644
index 0000000..4957072
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
@@ -0,0 +1,37 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.RelayServerDescriptor;
+import org.torproject.descriptor.ServerDescriptor;
+
+public class RelayServerDescriptorImpl extends ServerDescriptorImpl
+ implements RelayServerDescriptor {
+
+ protected static List<ServerDescriptor> parseDescriptors(
+ byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
+ List<byte[]> splitDescriptorsBytes =
+ DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
+ "router ");
+ for (byte[] descriptorBytes : splitDescriptorsBytes) {
+ ServerDescriptor parsedDescriptor =
+ new RelayServerDescriptorImpl(descriptorBytes,
+ failUnrecognizedDescriptorLines);
+ parsedDescriptors.add(parsedDescriptor);
+ }
+ return parsedDescriptors;
+ }
+
+ protected RelayServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines);
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
new file mode 100644
index 0000000..a359c50
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
@@ -0,0 +1,41 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.RouterStatusEntry;
+
+public class RouterStatusEntryImpl implements RouterStatusEntry {
+
+ protected RouterStatusEntryImpl(String fingerprint, String nickname,
+ boolean isLive, boolean isVerified) {
+ this.fingerprint = fingerprint;
+ this.nickname = nickname;
+ this.isLive = isLive;
+ this.isVerified = isVerified;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private boolean isLive;
+ @Override
+ public boolean isLive() {
+ return this.isLive;
+ }
+
+ private boolean isVerified;
+ @Override
+ public boolean isVerified() {
+ return this.isVerified;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
new file mode 100644
index 0000000..1805dca
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -0,0 +1,985 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+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.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* Contains a server descriptor. */
+public abstract class ServerDescriptorImpl extends DescriptorImpl
+ implements ServerDescriptor {
+
+ protected ServerDescriptorImpl(byte[] descriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(descriptorBytes, failUnrecognizedDescriptorLines, false);
+ 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,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) {
+ throw new DescriptorParseException("Either keyword 'accept' or "
+ + "'reject' must be contained at least once.");
+ }
+ this.clearParsedKeywords();
+ return;
+ }
+
+ private void parseDescriptorBytes() throws DescriptorParseException {
+ Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+ useDelimiter("\n");
+ String nextCrypto = "";
+ List<String> cryptoLines = null;
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@")) {
+ continue;
+ }
+ String lineNoOpt = line.startsWith("opt ") ?
+ line.substring("opt ".length()) : line;
+ String[] partsNoOpt = lineNoOpt.split("[ \t]+");
+ String keyword = partsNoOpt[0];
+ switch (keyword) {
+ case "router":
+ this.parseRouterLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "or-address":
+ this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "bandwidth":
+ this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "platform":
+ this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "published":
+ this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "fingerprint":
+ this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hibernating":
+ this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "uptime":
+ this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "onion-key":
+ this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key";
+ break;
+ case "signing-key":
+ this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "signing-key";
+ break;
+ case "accept":
+ this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "reject":
+ this.parseRejectLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-signature":
+ this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "router-signature";
+ break;
+ case "contact":
+ this.parseContactLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "family":
+ this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "read-history":
+ this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "write-history":
+ this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "eventdns":
+ this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "caches-extra-info":
+ this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "extra-info-digest":
+ this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "hidden-service-dir":
+ this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "protocols":
+ this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "allow-single-hop-exits":
+ this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "dircacheport":
+ this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest":
+ this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-digest-sha256":
+ this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ipv6-policy":
+ this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "ntor-onion-key":
+ this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "identity-ed25519":
+ this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "identity-ed25519";
+ break;
+ case "master-key-ed25519":
+ this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "router-sig-ed25519":
+ this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
+ break;
+ case "onion-key-crosscert":
+ this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "onion-key-crosscert";
+ break;
+ case "ntor-onion-key-crosscert":
+ this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
+ nextCrypto = "ntor-onion-key-crosscert";
+ break;
+ case "tunnelled-dir-server":
+ this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
+ break;
+ case "-----BEGIN":
+ cryptoLines = new ArrayList<>();
+ cryptoLines.add(line);
+ break;
+ case "-----END":
+ cryptoLines.add(line);
+ StringBuilder sb = new StringBuilder();
+ for (String cryptoLine : cryptoLines) {
+ sb.append("\n").append(cryptoLine);
+ }
+ String cryptoString = sb.toString().substring(1);
+ switch (nextCrypto) {
+ case "onion-key":
+ this.onionKey = cryptoString;
+ break;
+ case "signing-key":
+ this.signingKey = cryptoString;
+ break;
+ case "router-signature":
+ this.routerSignature = cryptoString;
+ break;
+ case "identity-ed25519":
+ this.identityEd25519 = cryptoString;
+ this.parseIdentityEd25519CryptoBlock(cryptoString);
+ break;
+ case "onion-key-crosscert":
+ this.onionKeyCrosscert = cryptoString;
+ break;
+ case "ntor-onion-key-crosscert":
+ this.ntorOnionKeyCrosscert = cryptoString;
+ break;
+ default:
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized crypto "
+ + "block '" + cryptoString + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.addAll(cryptoLines);
+ }
+ }
+ cryptoLines = null;
+ nextCrypto = "";
+ break;
+ default:
+ if (cryptoLines != null) {
+ cryptoLines.add(line);
+ } else {
+ ParseHelper.parseKeyword(line, partsNoOpt[0]);
+ if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized line '"
+ + line + "' in server descriptor.");
+ } else {
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseRouterLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 6) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in server descriptor.");
+ }
+ this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
+ this.address = ParseHelper.parseIpv4Address(line, partsNoOpt[2]);
+ this.orPort = ParseHelper.parsePort(line, partsNoOpt[3]);
+ this.socksPort = ParseHelper.parsePort(line, partsNoOpt[4]);
+ this.dirPort = ParseHelper.parsePort(line, partsNoOpt[5]);
+ }
+
+ private void parseOrAddressLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ /* TODO Add more checks. */
+ /* TODO Add tests. */
+ this.orAddresses.add(partsNoOpt[1]);
+ }
+
+ private void parseBandwidthLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 3 || partsNoOpt.length > 4) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ boolean isValid = false;
+ try {
+ this.bandwidthRate = Integer.parseInt(partsNoOpt[1]);
+ this.bandwidthBurst = Integer.parseInt(partsNoOpt[2]);
+ if (partsNoOpt.length == 4) {
+ this.bandwidthObserved = Integer.parseInt(partsNoOpt[3]);
+ }
+ if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0 &&
+ this.bandwidthObserved >= 0) {
+ isValid = true;
+ }
+ if (partsNoOpt.length < 4) {
+ /* Tor versions 0.0.8 and older only wrote bandwidth lines with
+ * rate and burst values, but no observed value. */
+ this.bandwidthObserved = -1;
+ }
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal values in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parsePlatformLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (lineNoOpt.length() > "platform ".length()) {
+ this.platform = lineNoOpt.substring("platform ".length());
+ } else {
+ this.platform = "";
+ }
+ }
+
+ private void parsePublishedLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
+ partsNoOpt, 1, 2);
+ }
+
+ private void parseFingerprintLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (lineNoOpt.length() != "fingerprint".length() + 5 * 10) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ lineNoOpt.substring("fingerprint ".length()).replaceAll(" ", ""));
+ }
+
+ private void parseHibernatingLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.hibernating = ParseHelper.parseBoolean(partsNoOpt[1], line);
+ }
+
+ private void parseUptimeLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Wrong number of values in line "
+ + "'" + line + "'.");
+ }
+ boolean isValid = false;
+ try {
+ this.uptime = Long.parseLong(partsNoOpt[1]);
+ isValid = true;
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal value in line '" + line
+ + "'.");
+ }
+ }
+
+ private void parseOnionKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("onion-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseSigningKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("signing-key")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseAcceptLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
+ }
+
+ private void parseRejectLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
+ }
+
+ private void parseExitPolicyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseExitPattern(line, partsNoOpt[1]);
+ this.exitPolicyLines.add(lineNoOpt);
+ }
+
+ private void parseRouterSignatureLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("router-signature")) {
+ 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());
+ } else {
+ this.contact = "";
+ }
+ }
+
+ private void parseFamilyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ String[] familyEntries = new String[partsNoOpt.length - 1];
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ if (partsNoOpt[i].startsWith("$")) {
+ if (partsNoOpt[i].contains("=") ^ partsNoOpt[i].contains("~")) {
+ String separator = partsNoOpt[i].contains("=") ? "=" : "~";
+ String fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[i].substring(1, partsNoOpt[i].indexOf(
+ separator)));
+ String nickname = ParseHelper.parseNickname(line,
+ partsNoOpt[i].substring(partsNoOpt[i].indexOf(
+ separator) + 1));
+ familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
+ } else {
+ familyEntries[i - 1] = "$"
+ + ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[i].substring(1));
+ }
+ } else {
+ familyEntries[i - 1] = ParseHelper.parseNickname(line,
+ partsNoOpt[i]);
+ }
+ }
+ this.familyEntries = familyEntries;
+ }
+
+ private void parseReadHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseWriteHistoryLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
+ partsNoOpt);
+ }
+
+ private void parseEventdnsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1], line);
+ }
+
+ private void parseCachesExtraInfoLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("caches-extra-info")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.cachesExtraInfo = true;
+ }
+
+ private void parseExtraInfoDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ if (partsNoOpt.length >= 3) {
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[2]);
+ this.extraInfoDigestSha256 = partsNoOpt[2];
+ }
+ }
+
+ private void parseHiddenServiceDirLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length == 1) {
+ this.hiddenServiceDirVersions = new Integer[] { 2 };
+ } else {
+ try {
+ Integer[] result = new Integer[partsNoOpt.length - 1];
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ result[i - 1] = Integer.parseInt(partsNoOpt[i]);
+ }
+ this.hiddenServiceDirVersions = result;
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in line '"
+ + line + "'.");
+ }
+ }
+ }
+
+ private void parseProtocolsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ int linkIndex = -1, circuitIndex = -1;
+ for (int i = 1; i < partsNoOpt.length; i++) {
+ switch (partsNoOpt[i]) {
+ case "Link":
+ linkIndex = i;
+ break;
+ case "Circuit":
+ circuitIndex = i;
+ break;
+ default:
+ // empty
+ }
+ }
+ if (linkIndex < 0 || circuitIndex < 0 || circuitIndex < linkIndex) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ Integer[] linkProtocolVersions =
+ new Integer[circuitIndex - linkIndex - 1];
+ for (int i = linkIndex + 1, j = 0; i < circuitIndex; i++, j++) {
+ linkProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
+ }
+ Integer[] circuitProtocolVersions =
+ new Integer[partsNoOpt.length - circuitIndex - 1];
+ for (int i = circuitIndex + 1, j = 0; i < partsNoOpt.length;
+ i++, j++) {
+ circuitProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
+ }
+ this.linkProtocolVersions = linkProtocolVersions;
+ this.circuitProtocolVersions = circuitProtocolVersions;
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseAllowSingleHopExitsLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("allow-single-hop-exits")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.allowSingleHopExits = true;
+ }
+
+ private void parseDircacheportLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ /* The dircacheport line was only contained in server descriptors
+ * published by Tor 0.0.8 and before. It's only specified in old
+ * tor-spec.txt versions. */
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ if (this.dirPort != 0) {
+ throw new DescriptorParseException("At most one of dircacheport "
+ + "and the directory port in the router line may be non-zero.");
+ }
+ this.dirPort = ParseHelper.parsePort(line, partsNoOpt[1]);
+ }
+
+ private void parseRouterDigestLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.serverDescriptorDigest = ParseHelper.parseTwentyByteHexString(
+ line, partsNoOpt[1]);
+ }
+
+ private void parseIpv6PolicyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ boolean isValid = true;
+ if (partsNoOpt.length != 3) {
+ isValid = false;
+ } else {
+ switch (partsNoOpt[1]) {
+ case "accept":
+ case "reject":
+ this.ipv6DefaultPolicy = partsNoOpt[1];
+ this.ipv6PortList = partsNoOpt[2];
+ String[] ports = partsNoOpt[2].split(",", -1);
+ for (int i = 0; i < ports.length; i++) {
+ if (ports[i].length() < 1) {
+ isValid = false;
+ break;
+ }
+ }
+ break;
+ default:
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
+ }
+
+ private void parseIdentityEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseOnionKeyCrosscert(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 1) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseNtorOnionKeyCrosscert(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ try {
+ this.ntorOnionKeyCrosscertSign = Integer.parseInt(partsNoOpt[1]);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ }
+
+ private void parseTunnelledDirServerLine(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (!lineNoOpt.equals("tunnelled-dir-server")) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.tunnelledDirServer = true;
+ }
+
+ private void parseIdentityEd25519CryptoBlock(String cryptoString)
+ throws DescriptorParseException {
+ String masterKeyEd25519FromIdentityEd25519 =
+ ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
+ cryptoString);
+ if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
+ masterKeyEd25519FromIdentityEd25519)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
+ }
+
+ private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
+ if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
+ masterKeyEd25519FromMasterKeyEd25519Line)) {
+ throw new DescriptorParseException("Mismatch between "
+ + "identity-ed25519 and master-key-ed25519.");
+ }
+ this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
+ }
+
+ private void parseRouterSigEd25519Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.routerSignatureEd25519 = partsNoOpt[1];
+ }
+
+ private void parseRouterDigestSha256Line(String line, String lineNoOpt,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length != 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
+ this.serverDescriptorDigestSha256 = partsNoOpt[1];
+ }
+
+ private void calculateDigest() throws DescriptorParseException {
+ if (this.serverDescriptorDigest != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "router ";
+ String sigToken = "\nrouter-signature\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start,
+ forDigest, 0, sig - start);
+ this.serverDescriptorDigest = DatatypeConverter.printHexBinary(
+ MessageDigest.getInstance("SHA-1").digest(forDigest)).
+ toLowerCase();
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.serverDescriptorDigest == null) {
+ throw new DescriptorParseException("Could not calculate server "
+ + "descriptor digest.");
+ }
+ }
+
+ private void calculateDigestSha256() throws DescriptorParseException {
+ if (this.serverDescriptorDigestSha256 != null) {
+ /* We already learned the descriptor digest of this bridge
+ * descriptor from a "router-digest-sha256" line. */
+ return;
+ }
+ try {
+ String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+ String startToken = "router ";
+ String sigToken = "\n-----END SIGNATURE-----\n";
+ int start = ascii.indexOf(startToken);
+ int sig = ascii.indexOf(sigToken) + sigToken.length();
+ if (start >= 0 && sig >= 0 && sig > start) {
+ byte[] forDigest = new byte[sig - start];
+ System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
+ 0, sig - start);
+ this.serverDescriptorDigestSha256 =
+ DatatypeConverter.printBase64Binary(
+ MessageDigest.getInstance("SHA-256").digest(forDigest)).
+ replaceAll("=", "");
+ }
+ } catch (UnsupportedEncodingException e) {
+ /* Handle below. */
+ } catch (NoSuchAlgorithmException e) {
+ /* Handle below. */
+ }
+ if (this.serverDescriptorDigestSha256 == null) {
+ throw new DescriptorParseException("Could not calculate server "
+ + "descriptor SHA-256 digest.");
+ }
+ }
+
+ private String serverDescriptorDigest;
+ @Override
+ public String getServerDescriptorDigest() {
+ return this.serverDescriptorDigest;
+ }
+
+ private String serverDescriptorDigestSha256;
+ @Override
+ public String getServerDescriptorDigestSha256() {
+ return this.serverDescriptorDigestSha256;
+ }
+
+ private String nickname;
+ @Override
+ public String getNickname() {
+ return this.nickname;
+ }
+
+ private String address;
+ @Override
+ public String getAddress() {
+ return this.address;
+ }
+
+ private int orPort;
+ @Override
+ public int getOrPort() {
+ return this.orPort;
+ }
+
+ private int socksPort;
+ @Override
+ public int getSocksPort() {
+ return this.socksPort;
+ }
+
+ private int dirPort;
+ @Override
+ public int getDirPort() {
+ return this.dirPort;
+ }
+
+ private List<String> orAddresses = new ArrayList<>();
+ @Override
+ public List<String> getOrAddresses() {
+ return new ArrayList<>(this.orAddresses);
+ }
+
+ private int bandwidthRate;
+ @Override
+ public int getBandwidthRate() {
+ return this.bandwidthRate;
+ }
+
+ private int bandwidthBurst;
+ @Override
+ public int getBandwidthBurst() {
+ return this.bandwidthBurst;
+ }
+
+ private int bandwidthObserved;
+ @Override
+ public int getBandwidthObserved() {
+ return this.bandwidthObserved;
+ }
+
+ private String platform;
+ @Override
+ public String getPlatform() {
+ return this.platform;
+ }
+
+ private long publishedMillis;
+ @Override
+ public long getPublishedMillis() {
+ return this.publishedMillis;
+ }
+
+ private String fingerprint;
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
+
+ private boolean hibernating;
+ @Override
+ public boolean isHibernating() {
+ return this.hibernating;
+ }
+
+ private Long uptime;
+ @Override
+ public Long getUptime() {
+ return this.uptime;
+ }
+
+ private String onionKey;
+ @Override
+ public String getOnionKey() {
+ return this.onionKey;
+ }
+
+ private String signingKey;
+ @Override
+ public String getSigningKey() {
+ return this.signingKey;
+ }
+
+ private List<String> exitPolicyLines = new ArrayList<>();
+ @Override
+ public List<String> getExitPolicyLines() {
+ return new ArrayList<>(this.exitPolicyLines);
+ }
+
+ private String routerSignature;
+ @Override
+ public String getRouterSignature() {
+ return this.routerSignature;
+ }
+
+ private String contact;
+ @Override
+ public String getContact() {
+ return this.contact;
+ }
+
+ private String[] familyEntries;
+ @Override
+ public List<String> getFamilyEntries() {
+ return this.familyEntries == null ? null :
+ Arrays.asList(this.familyEntries);
+ }
+
+ private BandwidthHistory readHistory;
+ @Override
+ public BandwidthHistory getReadHistory() {
+ return this.readHistory;
+ }
+
+ private BandwidthHistory writeHistory;
+ @Override
+ public BandwidthHistory getWriteHistory() {
+ return this.writeHistory;
+ }
+
+ private boolean usesEnhancedDnsLogic;
+ @Override
+ public boolean getUsesEnhancedDnsLogic() {
+ return this.usesEnhancedDnsLogic;
+ }
+
+ private boolean cachesExtraInfo;
+ @Override
+ public boolean getCachesExtraInfo() {
+ return this.cachesExtraInfo;
+ }
+
+ private String extraInfoDigest;
+ @Override
+ public String getExtraInfoDigest() {
+ return this.extraInfoDigest;
+ }
+
+ private String extraInfoDigestSha256;
+ @Override
+ public String getExtraInfoDigestSha256() {
+ return this.extraInfoDigestSha256;
+ }
+
+ private Integer[] hiddenServiceDirVersions;
+ @Override
+ public List<Integer> getHiddenServiceDirVersions() {
+ return this.hiddenServiceDirVersions == null ? null :
+ Arrays.asList(this.hiddenServiceDirVersions);
+ }
+
+ private Integer[] linkProtocolVersions;
+ @Override
+ public List<Integer> getLinkProtocolVersions() {
+ return this.linkProtocolVersions == null ? null :
+ Arrays.asList(this.linkProtocolVersions);
+ }
+
+ private Integer[] circuitProtocolVersions;
+ @Override
+ public List<Integer> getCircuitProtocolVersions() {
+ return this.circuitProtocolVersions == null ? null :
+ Arrays.asList(this.circuitProtocolVersions);
+ }
+
+ private boolean allowSingleHopExits;
+ @Override
+ public boolean getAllowSingleHopExits() {
+ return this.allowSingleHopExits;
+ }
+
+ private String ipv6DefaultPolicy;
+ @Override
+ public String getIpv6DefaultPolicy() {
+ return this.ipv6DefaultPolicy;
+ }
+
+ private String ipv6PortList;
+ @Override
+ public String getIpv6PortList() {
+ return this.ipv6PortList;
+ }
+
+ private String ntorOnionKey;
+ @Override
+ public String getNtorOnionKey() {
+ return this.ntorOnionKey;
+ }
+
+ private String identityEd25519;
+ @Override
+ public String getIdentityEd25519() {
+ return this.identityEd25519;
+ }
+
+ private String masterKeyEd25519;
+ @Override
+ public String getMasterKeyEd25519() {
+ return this.masterKeyEd25519;
+ }
+
+ private String routerSignatureEd25519;
+ @Override
+ public String getRouterSignatureEd25519() {
+ return this.routerSignatureEd25519;
+ }
+
+ private String onionKeyCrosscert;
+ @Override
+ public String getOnionKeyCrosscert() {
+ return this.onionKeyCrosscert;
+ }
+
+ private String ntorOnionKeyCrosscert;
+ @Override
+ public String getNtorOnionKeyCrosscert() {
+ return this.ntorOnionKeyCrosscert;
+ }
+
+ private int ntorOnionKeyCrosscertSign = -1;
+ @Override
+ public int getNtorOnionKeyCrosscertSign() {
+ return ntorOnionKeyCrosscertSign;
+ }
+
+ private boolean tunnelledDirServer;
+ @Override
+ public boolean getTunnelledDirServer() {
+ return this.tunnelledDirServer;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
new file mode 100644
index 0000000..0800de0
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/impl/TorperfResultImpl.java
@@ -0,0 +1,546 @@
+/* Copyright 2012--2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.TorperfResult;
+
+public class TorperfResultImpl extends DescriptorImpl
+ implements TorperfResult {
+
+ protected static List<Descriptor> parseTorperfResults(
+ byte[] rawDescriptorBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ if (rawDescriptorBytes.length == 0) {
+ throw new DescriptorParseException("Descriptor is empty.");
+ }
+ List<Descriptor> parsedDescriptors = new ArrayList<>();
+ String descriptorString = new String(rawDescriptorBytes);
+ Scanner s = new Scanner(descriptorString).useDelimiter("\r?\n");
+ String typeAnnotation = "";
+ while (s.hasNext()) {
+ String line = s.next();
+ if (line.startsWith("@type torperf ")) {
+ String[] parts = line.split(" ");
+ if (parts.length != 3) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "'.");
+ }
+ String version = parts[2];
+ if (!version.startsWith("1.")) {
+ throw new DescriptorParseException("Unsupported version in "
+ + " line '" + line + "'.");
+ }
+ typeAnnotation = line + "\n";
+ } else {
+ parsedDescriptors.add(new TorperfResultImpl(
+ (typeAnnotation + line).getBytes(),
+ failUnrecognizedDescriptorLines));
+ typeAnnotation = "";
+ }
+ }
+ return parsedDescriptors;
+ }
+
+ protected TorperfResultImpl(byte[] rawDescriptorBytes,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
+ this.parseTorperfResultLine(new String(rawDescriptorBytes));
+ }
+
+ private void parseTorperfResultLine(String inputLine)
+ throws DescriptorParseException {
+ String line = inputLine;
+ while (line.startsWith("@") && line.contains("\n")) {
+ line = line.split("\n")[1];
+ }
+ if (line.isEmpty()) {
+ throw new DescriptorParseException("Blank lines are not allowed.");
+ }
+ String[] parts = line.split(" ");
+ for (int i = 0; i < parts.length; i++) {
+ String keyValue = parts[i];
+ String[] keyValueParts = keyValue.split("=");
+ if (keyValueParts.length != 2) {
+ throw new DescriptorParseException("Illegal key-value pair in "
+ + "line '" + line + "'.");
+ }
+ String key = keyValueParts[0];
+ this.markKeyAsParsed(key, line);
+ String value = keyValueParts[1];
+ switch (key) {
+ case "SOURCE":
+ this.parseSource(value, keyValue, line);
+ break;
+ case "FILESIZE":
+ this.parseFileSize(value, keyValue, line);
+ break;
+ case "START":
+ this.parseStart(value, keyValue, line);
+ break;
+ case "SOCKET":
+ this.parseSocket(value, keyValue, line);
+ break;
+ case "CONNECT":
+ this.parseConnect(value, keyValue, line);
+ break;
+ case "NEGOTIATE":
+ this.parseNegotiate(value, keyValue, line);
+ break;
+ case "REQUEST":
+ this.parseRequest(value, keyValue, line);
+ break;
+ case "RESPONSE":
+ this.parseResponse(value, keyValue, line);
+ break;
+ case "DATAREQUEST":
+ this.parseDataRequest(value, keyValue, line);
+ break;
+ case "DATARESPONSE":
+ this.parseDataResponse(value, keyValue, line);
+ break;
+ case "DATACOMPLETE":
+ this.parseDataComplete(value, keyValue, line);
+ break;
+ case "WRITEBYTES":
+ this.parseWriteBytes(value, keyValue, line);
+ break;
+ case "READBYTES":
+ this.parseReadBytes(value, keyValue, line);
+ break;
+ case "DIDTIMEOUT":
+ this.parseDidTimeout(value, keyValue, line);
+ break;
+ case "LAUNCH":
+ this.parseLaunch(value, keyValue, line);
+ break;
+ case "USED_AT":
+ this.parseUsedAt(value, keyValue, line);
+ break;
+ case "PATH":
+ this.parsePath(value, keyValue, line);
+ break;
+ case "BUILDTIMES":
+ this.parseBuildTimes(value, keyValue, line);
+ break;
+ case "TIMEOUT":
+ this.parseTimeout(value, keyValue, line);
+ break;
+ case "QUANTILE":
+ this.parseQuantile(value, keyValue, line);
+ break;
+ case "CIRC_ID":
+ this.parseCircId(value, keyValue, line);
+ break;
+ case "USED_BY":
+ this.parseUsedBy(value, keyValue, line);
+ break;
+ default:
+ if (key.startsWith("DATAPERC")) {
+ this.parseDataPercentile(value, keyValue, line);
+ } else if (this.failUnrecognizedDescriptorLines) {
+ throw new DescriptorParseException("Unrecognized key '" + key
+ + "' in line '" + line + "'.");
+ } else {
+ if (this.unrecognizedKeys == null) {
+ this.unrecognizedKeys = new TreeMap<>();
+ }
+ this.unrecognizedKeys.put(key, value);
+ if (this.unrecognizedLines == null) {
+ this.unrecognizedLines = new ArrayList<>();
+ }
+ if (!this.unrecognizedLines.contains(line)) {
+ this.unrecognizedLines.add(line);
+ }
+ }
+ }
+ }
+ this.checkAllRequiredKeysParsed(line);
+ }
+
+ private Set<String> parsedKeys = new HashSet<>();
+ private Set<String> requiredKeys = new HashSet<>(Arrays.asList(
+ ("SOURCE,FILESIZE,START,SOCKET,CONNECT,NEGOTIATE,REQUEST,RESPONSE,"
+ + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES").
+ split(",")));
+ private void markKeyAsParsed(String key, String line)
+ throws DescriptorParseException {
+ if (this.parsedKeys.contains(key)) {
+ throw new DescriptorParseException("Key '" + key + "' is contained "
+ + "at least twice in line '" + line + "', but must be "
+ + "contained at most once.");
+ }
+ this.parsedKeys.add(key);
+ this.requiredKeys.remove(key);
+ }
+ private void checkAllRequiredKeysParsed(String line)
+ throws DescriptorParseException {
+ for (String key : this.requiredKeys) {
+ throw new DescriptorParseException("Key '" + key + "' is contained "
+ + "contained 0 times in line '" + line + "', but must be "
+ + "contained exactly once.");
+ }
+ }
+
+ private void parseSource(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.source = value;
+ }
+
+ private void parseFileSize(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ this.fileSize = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private void parseStart(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.startMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseSocket(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.socketMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseConnect(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.connectMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseNegotiate(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.negotiateMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseRequest(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.requestMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseResponse(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.responseMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataRequest(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataRequestMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataResponse(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataResponseMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseDataComplete(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ this.dataCompleteMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseWriteBytes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.writeBytes = parseInt(value, keyValue, line);
+ }
+
+ private void parseReadBytes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.readBytes = parseInt(value, keyValue, line);
+ }
+
+ private void parseDidTimeout(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ if (value.equals("1")) {
+ this.didTimeout = true;
+ } else if (value.equals("0")) {
+ this.didTimeout = false;
+ } else {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private void parseDataPercentile(String value, String keyValue,
+ String line) throws DescriptorParseException {
+ String key = keyValue.substring(0, keyValue.indexOf("="));
+ String percentileString = key.substring("DATAPERC".length());
+ int percentile = -1;
+ try {
+ percentile = Integer.parseInt(percentileString);
+ } catch (NumberFormatException e) {
+ /* Treat key as unrecognized below. */
+ percentile = -1;
+ }
+ if (percentile < 0 || percentile > 100) {
+ if (this.unrecognizedKeys == null) {
+ this.unrecognizedKeys = new TreeMap<>();
+ }
+ this.unrecognizedKeys.put(key, value);
+ } else {
+ long timestamp = this.parseTimestamp(value, keyValue, line);
+ if (this.dataPercentiles == null) {
+ this.dataPercentiles = new TreeMap<>();
+ }
+ this.dataPercentiles.put(percentile, timestamp);
+ }
+ }
+
+ private void parseLaunch(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.launchMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parseUsedAt(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.usedAtMillis = this.parseTimestamp(value, keyValue, line);
+ }
+
+ private void parsePath(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ String[] valueParts = value.split(",");
+ String[] result = new String[valueParts.length];
+ for (int i = 0; i < valueParts.length; i++) {
+ if (valueParts[i].length() != 41) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ result[i] = ParseHelper.parseTwentyByteHexString(line,
+ valueParts[i].substring(1));
+ }
+ this.path = result;
+ }
+
+ private void parseBuildTimes(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ String[] valueParts = value.split(",");
+ Long[] result = new Long[valueParts.length];
+ for (int i = 0; i < valueParts.length; i++) {
+ result[i] = this.parseTimestamp(valueParts[i], keyValue, line);
+ }
+ this.buildTimes = result;
+ }
+
+ private void parseTimeout(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.timeout = this.parseInt(value, keyValue, line);
+ }
+
+ private void parseQuantile(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.quantile = this.parseDouble(value, keyValue, line);
+ }
+
+ private void parseCircId(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.circId = this.parseInt(value, keyValue, line);
+ }
+
+ private void parseUsedBy(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ this.usedBy = this.parseInt(value, keyValue, line);
+ }
+
+ private long parseTimestamp(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ long timestamp = -1L;
+ if (value.contains(".") && value.split("\\.").length == 2) {
+ String zeroPaddedValue = (value + "000");
+ String threeDecimalPlaces = zeroPaddedValue.substring(0,
+ zeroPaddedValue.indexOf(".") + 4);
+ String millisString = threeDecimalPlaces.replaceAll("\\.", "");
+ try {
+ timestamp = Long.parseLong(millisString);
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ if (timestamp < 0L) {
+ throw new DescriptorParseException("Illegal timestamp '" + value
+ + "' in '" + keyValue + "' in line '" + line + "'.");
+ }
+ return timestamp;
+ }
+
+ private int parseInt(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private double parseDouble(String value, String keyValue, String line)
+ throws DescriptorParseException {
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ throw new DescriptorParseException("Illegal value in '" + keyValue
+ + "' in line '" + line + "'.");
+ }
+ }
+
+ private SortedMap<String, String> unrecognizedKeys;
+ @Override
+ public SortedMap<String, String> getUnrecognizedKeys() {
+ return this.unrecognizedKeys == null ? null
+ : new TreeMap<>(this.unrecognizedKeys);
+ }
+
+ private String source;
+ @Override
+ public String getSource() {
+ return this.source;
+ }
+
+ private int fileSize;
+ @Override
+ public int getFileSize() {
+ return this.fileSize;
+ }
+
+ private long startMillis;
+ @Override
+ public long getStartMillis() {
+ return this.startMillis;
+ }
+
+ private long socketMillis;
+ @Override
+ public long getSocketMillis() {
+ return this.socketMillis;
+ }
+
+ private long connectMillis;
+ @Override
+ public long getConnectMillis() {
+ return this.connectMillis;
+ }
+
+ private long negotiateMillis;
+ @Override
+ public long getNegotiateMillis() {
+ return this.negotiateMillis;
+ }
+
+ private long requestMillis;
+ @Override
+ public long getRequestMillis() {
+ return this.requestMillis;
+ }
+
+ private long responseMillis;
+ @Override
+ public long getResponseMillis() {
+ return this.responseMillis;
+ }
+
+ private long dataRequestMillis;
+ @Override
+ public long getDataRequestMillis() {
+ return this.dataRequestMillis;
+ }
+
+ private long dataResponseMillis;
+ @Override
+ public long getDataResponseMillis() {
+ return this.dataResponseMillis;
+ }
+
+ private long dataCompleteMillis;
+ @Override
+ public long getDataCompleteMillis() {
+ return this.dataCompleteMillis;
+ }
+
+ private int writeBytes;
+ @Override
+ public int getWriteBytes() {
+ return this.writeBytes;
+ }
+
+ private int readBytes;
+ @Override
+ public int getReadBytes() {
+ return this.readBytes;
+ }
+
+ private boolean didTimeout;
+ @Override
+ public Boolean didTimeout() {
+ return this.didTimeout;
+ }
+
+ private SortedMap<Integer, Long> dataPercentiles;
+ @Override
+ public SortedMap<Integer, Long> getDataPercentiles() {
+ return this.dataPercentiles == null ? null
+ : new TreeMap<>(this.dataPercentiles);
+ }
+
+ private long launchMillis = -1L;
+ @Override
+ public long getLaunchMillis() {
+ return this.launchMillis;
+ }
+
+ private long usedAtMillis = -1L;
+ @Override
+ public long getUsedAtMillis() {
+ return this.usedAtMillis;
+ }
+
+ private String[] path;
+ @Override
+ public List<String> getPath() {
+ return this.path == null ? null : Arrays.asList(this.path);
+ }
+
+ private Long[] buildTimes;
+ @Override
+ public List<Long> getBuildTimes() {
+ return this.buildTimes == null ? null :
+ Arrays.asList(this.buildTimes);
+ }
+
+ private long timeout = -1L;
+ @Override
+ public long getTimeout() {
+ return this.timeout;
+ }
+
+ private double quantile = -1.0;
+ @Override
+ public double getQuantile() {
+ return this.quantile;
+ }
+
+ private int circId = -1;
+ @Override
+ public int getCircId() {
+ return this.circId;
+ }
+
+ private int usedBy = -1;
+ @Override
+ public int getUsedBy() {
+ return this.usedBy;
+ }
+}
+
diff --git a/src/main/java/org/torproject/descriptor/package-info.java b/src/main/java/org/torproject/descriptor/package-info.java
new file mode 100644
index 0000000..5b34554
--- /dev/null
+++ b/src/main/java/org/torproject/descriptor/package-info.java
@@ -0,0 +1,80 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+
+/**
+ * Interfaces and essential classes for obtaining and processing Tor
+ * descriptors.
+ *
+ * <p>This package contains all relevant interfaces and
+ * classes that an application would need to use this library.
+ * Applications are strongly discouraged from accessing types from the
+ * implementation package ({@code org.torproject.descriptor.impl})
+ * directly, because those may change without prior notice.</p>
+ *
+ * <p>Interfaces and classes in this package can be grouped into
+ * general-purpose types to obtain and process any type of descriptor and
+ * descriptors produced by different components of the Tor network:</p>
+ *
+ * <ol>
+ * <li>General-purpose types comprise
+ * {@link org.torproject.descriptor.DescriptorSourceFactory} which is the
+ * main entry point into using this library. This factory is used to
+ * create the descriptor sources for obtaining remote descriptor data
+ * ({@link org.torproject.descriptor.DescriptorDownloader} and
+ * {@link org.torproject.descriptor.DescriptorCollector}) and descriptor
+ * sources for processing local descriptor data
+ * ({@link org.torproject.descriptor.DescriptorReader} and
+ * {@link org.torproject.descriptor.DescriptorParser}). General-purpose
+ * types also include descriptor containers
+ * ({@link org.torproject.descriptor.DescriptorRequest} and
+ * {@link org.torproject.descriptor.DescriptorFile}) and the
+ * superinterface for all provided descriptors
+ * ({@link org.torproject.descriptor.Descriptor}).</li>
+ *
+ * <li>The first group of descriptors is published by relays and servers
+ * in the Tor network. These interfaces include server descriptors
+ * ({@link org.torproject.descriptor.ServerDescriptor} with subinterfaces
+ * {@link org.torproject.descriptor.RelayServerDescriptor} and
+ * {@link org.torproject.descriptor.BridgeServerDescriptor}), extra-info
+ * descriptors ({@link org.torproject.descriptor.ExtraInfoDescriptor} with
+ * subinterfaces
+ * {@link org.torproject.descriptor.RelayExtraInfoDescriptor} and
+ * {@link org.torproject.descriptor.BridgeExtraInfoDescriptor}),
+ * microdescriptors which are derived from server descriptors by the
+ * directory authorities
+ * ({@link org.torproject.descriptor.Microdescriptor}), and helper types
+ * for parts of the aforementioned descriptors
+ * ({@link org.torproject.descriptor.BandwidthHistory}).</li>
+ *
+ * <li>The second group of descriptors is generated by authoritative
+ * directory servers that form an opinion about relays and bridges in the
+ * Tor network. These include descriptors specified in version 3 of the
+ * directory protocol
+ * ({@link org.torproject.descriptor.RelayNetworkStatusConsensus},
+ * {@link org.torproject.descriptor.RelayNetworkStatusVote},
+ * {@link org.torproject.descriptor.DirectoryKeyCertificate}, and helper
+ * types for descriptor parts
+ * {@link org.torproject.descriptor.DirSourceEntry},
+ * {@link org.torproject.descriptor.NetworkStatusEntry}, and
+ * {@link org.torproject.descriptor.DirectorySignature}), descriptors from
+ * earlier directory protocol version 2
+ * ({@link org.torproject.descriptor.RelayNetworkStatus}) and version 1
+ * ({@link org.torproject.descriptor.RelayDirectory} and
+ * {@link org.torproject.descriptor.RouterStatusEntry}), as well as
+ * descriptors published by the bridge authority and sanitized by the
+ * CollecTor service
+ * ({@link org.torproject.descriptor.BridgeNetworkStatus}).</li>
+ *
+ * <li>The third group of descriptors is created by auxiliary services
+ * connected to the Tor network rather than by the Tor software. This
+ * group comprises descriptors by the bridge distribution service BridgeDB
+ * ({@link org.torproject.descriptor.BridgePoolAssignment}), the exit list
+ * service TorDNSEL ({@link org.torproject.descriptor.ExitList}), and the
+ * performance measurement service Torperf
+ * ({@link org.torproject.descriptor.TorperfResult}).</li>
+ * </ol>
+ *
+ * @since 1.0.0
+ */
+package org.torproject.descriptor;
+
diff --git a/src/org/torproject/descriptor/BandwidthHistory.java b/src/org/torproject/descriptor/BandwidthHistory.java
deleted file mode 100644
index 0be1a53..0000000
--- a/src/org/torproject/descriptor/BandwidthHistory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains the bandwidth history of a relay or bridge.
- *
- * <p>A bandwidth history is not a descriptor type of its own but usually
- * part of extra-info descriptors ({@link ExtraInfoDescriptor}) or server
- * descriptors ({@link ServerDescriptor}).</p>
- *
- * @since 1.0.0
- */
-public interface BandwidthHistory {
-
- /**
- * Return the original bandwidth history line as contained in the
- * descriptor, possibly prefixed with {@code "opt "}.
- *
- * @since 1.0.0
- */
- public String getLine();
-
- /**
- * Return the time in milliseconds since the epoch when the most recent
- * interval ends.
- *
- * @since 1.0.0
- */
- public long getHistoryEndMillis();
-
- /**
- * Return the interval length in seconds.
- *
- * @since 1.0.0
- */
- public long getIntervalLength();
-
- /**
- * Return the (possibly empty) bandwidth history with map keys being
- * times in milliseconds since the epoch when intervals end and map
- * values being number of bytes used in the interval, ordered from
- * oldest to newest interval.
- *
- * @since 1.0.0
- */
- public SortedMap<Long, Long> getBandwidthValues();
-}
-
diff --git a/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java b/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
deleted file mode 100644
index a3c168d..0000000
--- a/src/org/torproject/descriptor/BridgeExtraInfoDescriptor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a sanitized bridge extra-info descriptor.
- *
- * <p>Sanitized bridge extra-info descriptors share many contents with
- * relay extra-info descriptors ({@link RelayExtraInfoDescriptor}), which
- * is why they share a common
- * superinterface ({@link ExtraInfoDescriptor}). The main purpose of
- * having two subinterfaces is being able to distinguish descriptor types
- * more easily.</p>
- *
- * <p>Details about sanitizing bridge extra-info descriptors can be found
- * <a href="https://collector.torproject.org/#type-bridge-extra-info">here</a>.
- * </p>
- *
- * @since 1.1.0
- */
-public interface BridgeExtraInfoDescriptor extends ExtraInfoDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/BridgeNetworkStatus.java b/src/org/torproject/descriptor/BridgeNetworkStatus.java
deleted file mode 100644
index c7458fd..0000000
--- a/src/org/torproject/descriptor/BridgeNetworkStatus.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains a sanitized bridge network status document.
- *
- * <p>The bridge directory authority periodically publishes a network
- * status document with one entry per known bridge in the network
- * ({@link NetworkStatusEntry}) containing: a hash of its identity key, a
- * hash of its most recent server descriptor, and a summary of what the
- * bridge authority believed about its status.</p>
- *
- * <p>The main purpose of this document is to get an authoritative list of
- * running bridges to the bridge distribution service BridgeDB.</p>
- *
- * <p>Details about sanitizing bridge network statuses can be found
- * <a href="https://collector.torproject.org/#type-bridge-network-status">here</a>.
- * </p>
- *
- * @since 1.0.0
- */
-public interface BridgeNetworkStatus extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the minimum uptime in seconds that this authority requires
- * for assigning the Stable flag, or -1 if the authority doesn't report
- * this value.
- *
- * @since 1.1.0
- */
- public long getStableUptime();
-
- /**
- * Return the minimum MTBF (mean time between failure) that this
- * authority requires for assigning the Stable flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getStableMtbf();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Fast flag, or -1 if the authority doesn't report this
- * value.
- *
- * @since 1.1.0
- */
- public long getFastBandwidth();
-
- /**
- * Return the minimum WFU (weighted fractional uptime) in percent that
- * this authority requires for assigning the Guard flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public double getGuardWfu();
-
- /**
- * Return the minimum weighted time in seconds that this authority
- * needs to know about a relay before assigning the Guard flag, or -1 if
- * the authority doesn't report this information.
- *
- * @since 1.1.0
- */
- public long getGuardTk();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getGuardBandwidthIncludingExits();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can not be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.1.0
- */
- public long getGuardBandwidthExcludingExits();
-
- /**
- * Return 1 if the authority has measured enough MTBF info to use the
- * MTBF requirement instead of the uptime requirement for assigning the
- * Stable flag, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getEnoughMtbfInfo();
-
- /**
- * Return 1 if the authority has enough measured bandwidths that it'll
- * ignore the advertised bandwidth claims of routers without measured
- * bandwidth, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getIgnoringAdvertisedBws();
-
- /**
- * Return status entries for each contained bridge, with map keys being
- * SHA-1 digests of SHA-1 digest of the bridges' public identity keys,
- * encoded as 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-}
-
diff --git a/src/org/torproject/descriptor/BridgePoolAssignment.java b/src/org/torproject/descriptor/BridgePoolAssignment.java
deleted file mode 100644
index 2de4ee9..0000000
--- a/src/org/torproject/descriptor/BridgePoolAssignment.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.SortedMap;
-
-/**
- * Contains a sanitized list of bridges together with the distribution
- * pools they have been assigned to by the bridge distribution service
- * BridgeDB.
- *
- * <p>BridgeDB receives bridge network statuses
- * ({@link BridgeNetworkStatus}) from the bridge authority, assigns these
- * bridges to persistent distribution rings, and hands them out to bridge
- * users. BridgeDB periodically dumps the list of running bridges with
- * information about the rings, subrings, and file buckets to which they
- * are assigned to a local file.</p>
- *
- * <p>Details about sanitizing bridge pool assignments can be found
- * <a href="https://collector.torproject.org/#type-bridge-pool-assignment">here</a>.
- * </p>
- *
- * @since 1.0.0
- */
-public interface BridgePoolAssignment extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the entries contained in this bridge pool assignment list
- * with map keys being SHA-1 digests of SHA-1 digest of the bridges'
- * public identity keys, encoded as 40 upper-case hexadecimal
- * characters, and map values being assignment strings, e.g.
- * {@code "https ring=3 flag=stable"}.
- *
- * @since 1.0.0
- */
- public SortedMap<String, String> getEntries();
-}
-
diff --git a/src/org/torproject/descriptor/BridgeServerDescriptor.java b/src/org/torproject/descriptor/BridgeServerDescriptor.java
deleted file mode 100644
index 7d4503f..0000000
--- a/src/org/torproject/descriptor/BridgeServerDescriptor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a sanitized bridge server descriptor.
- *
- * <p>Sanitized bridge server descriptors share many contents with relay
- * server descriptors ({@link RelayServerDescriptor}), which is why they
- * share a common superinterface ({@link ServerDescriptor}). The main
- * purpose of having two subinterfaces is being able to distinguish
- * descriptor types more easily.</p>
- *
- * <p>Details about sanitizing bridge server descriptors can be found
- * <a href="https://collector.torproject.org/#type-bridge-server-descriptor">here</a>.
- * </p>
- *
- * @since 1.1.0
- */
-public interface BridgeServerDescriptor extends ServerDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/Descriptor.java b/src/org/torproject/descriptor/Descriptor.java
deleted file mode 100644
index 7cad109..0000000
--- a/src/org/torproject/descriptor/Descriptor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Superinterface for any descriptor with access to generic information
- * about the descriptor.
- *
- * @since 1.0.0
- */
-public interface Descriptor {
-
- /**
- * Return the raw descriptor bytes.
- *
- * @since 1.0.0
- */
- public byte[] getRawDescriptorBytes();
-
- /**
- * Return the (possibly empty) list of annotations in the format
- * {@code "@key( value)*"}.
- *
- * @since 1.0.0
- */
- public List<String> getAnnotations();
-
- /**
- * Return any unrecognized lines when parsing this descriptor, or an
- * empty list if there were no unrecognized lines.
- *
- * @since 1.0.0
- */
- public List<String> getUnrecognizedLines();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorCollector.java b/src/org/torproject/descriptor/DescriptorCollector.java
deleted file mode 100644
index b1027dc..0000000
--- a/src/org/torproject/descriptor/DescriptorCollector.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-
-/**
- * Descriptor source that synchronizes descriptors from the CollecTor
- * service to a given local directory.
- *
- * <p>This type is not a descriptor source in the proper sense, because it
- * does not produce descriptors by itself. But it often creates the
- * prerequisites for reading descriptors from disk using
- * {@link DescriptorReader}.</p>
- *
- * <p>Code sample:</p>
- * <pre>{@code
- * DescriptorCollector descriptorCollector =
- * DescriptorSourceFactory.createDescriptorCollector();
- * descriptorCollector.collectDescriptors(
- * // Download from Tor's main CollecTor instance,
- * "https://collector.torproject.org",
- * // include network status consensuses and relay server descriptors
- * new String[] { "/recent/relay-descriptors/consensuses/",
- * "/recent/relay-descriptors/server-descriptors/" },
- * // regardless of last-modified time,
- * 0L,
- * // write to the local directory called in/,
- * new File("in"),
- * // and delete extraneous files that do not exist remotely anymore.
- * true);
- * }</pre>
- *
- * @since 1.0.0
- */
-public interface DescriptorCollector {
-
- /**
- * Fetch remote files from a CollecTor instance that do not yet exist
- * locally and possibly delete local files that do not exist remotely
- * anymore.
- *
- * @param collecTorBaseUrl CollecTor base URL without trailing slash,
- * e.g., {@code "https://collector.torproject.org"}
- * @param remoteDirectories Remote directories to collect descriptors
- * from, e.g.,
- * {@code "/recent/relay-descriptors/server-descriptors/"}, without
- * processing subdirectories unless they are explicitly listed
- * @param minLastModified Minimum last-modified time in milliseconds of
- * files to be collected, or 0 for collecting all files
- * @param localDirectory Directory where collected files will be written
- * @param deleteExtraneousLocalFiles Whether to delete all local files
- * that do not exist remotely anymore
- *
- * @since 1.0.0
- */
- public void collectDescriptors(String collecTorBaseUrl,
- String[] remoteDirectories, long minLastModified,
- File localDirectory, boolean deleteExtraneousLocalFiles);
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorDownloader.java b/src/org/torproject/descriptor/DescriptorDownloader.java
deleted file mode 100644
index f0b1101..0000000
--- a/src/org/torproject/descriptor/DescriptorDownloader.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * Descriptor source that downloads relay descriptors from directory
- * authorities or mirrors.
- *
- * <p>Downloading descriptors is done in a batch which starts after
- * setting any configuration options and initiating the download
- * process.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorDownloader {
-
- /**
- * Add a directory authority to download descriptors from, which is
- * only required for downloading network status votes and will be used
- * when no directory mirrors are available.
- *
- * @since 1.0.0
- */
- public void addDirectoryAuthority(String nickname, String ip,
- int dirPort);
-
- /**
- * Add a directory mirror to download descriptors from, which is
- * preferred for downloading descriptors, except for network status
- * votes which are only available on directory authorities.
- *
- * @since 1.0.0
- */
- public void addDirectoryMirror(String nickname, String ip, int dirPort);
-
- /**
- * Include the current network status consensus in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentConsensus();
-
- /**
- * Include the current network status consensus in the downloads, and
- * attempt to download it from all directory authorities.
- *
- * <p>The primary purpose of doing this is to compare different
- * consensuses and download characteristics to each other. Typically,
- * downloading from a single directory mirror or authority is
- * sufficient.</p>
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentConsensusFromAllDirectoryAuthorities();
-
- /**
- * Include the current network status votes referenced from a
- * previously downloaded consensus in the downloads, which requires
- * downloading the current consensus from at least one directory mirror
- * or authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentReferencedVotes();
-
- /**
- * Include the current network status vote published by the given
- * directory authority in the downloads, which requires downloading from
- * at least one directory authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentVote(String fingerprint);
-
- /**
- * Include the current network status votes published by the given
- * directory authorities in the downloads, which requires downloading
- * from at least one directory authority.
- *
- * @since 1.0.0
- */
- public void setIncludeCurrentVotes(Set<String> fingerprints);
-
- /**
- * Include all server descriptors referenced from a previously
- * downloaded network status consensus in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeReferencedServerDescriptors();
-
- /**
- * Exclude the server descriptor with the given identifier from the
- * downloads even if it's referenced from a consensus and we're supposed
- * to download all referenced server descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeServerDescriptor(String identifier);
-
- /**
- * Exclude the server descriptors with the given identifiers from the
- * downloads even if they are referenced from a consensus and we're
- * supposed to download all referenced server descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeServerDescriptors(Set<String> identifier);
-
- /**
- * Include all extra-info descriptors referenced from previously
- * downloaded server descriptors in the downloads.
- *
- * @since 1.0.0
- */
- public void setIncludeReferencedExtraInfoDescriptors();
-
- /**
- * Exclude the extra-info descriptor with the given identifier from the
- * downloads even if it's referenced from a server descriptor and we're
- * supposed to download all referenced extra-info descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeExtraInfoDescriptor(String identifier);
-
- /**
- * Exclude the extra-info descriptors with the given identifiers from
- * the downloads even if they are referenced from server descriptors
- * and we're supposed to download all referenced extra-info
- * descriptors.
- *
- * @since 1.0.0
- */
- public void setExcludeExtraInfoDescriptors(Set<String> identifiers);
-
- /**
- * Define a connect timeout for a single request.
- *
- * <p>If a timeout expires, no further requests will be sent to the
- * directory authority or mirror. Setting this value to 0 disables the
- * connect timeout. Default value is 1 minute (60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setConnectTimeout(long connectTimeoutMillis);
-
- /**
- * Define a read timeout for a single request.
- *
- * <p>If a timeout expires, no further requests will be sent to the
- * directory authority or mirror. Setting this value to 0 disables the
- * read timeout. Default value is 1 minute (60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setReadTimeout(long readTimeoutMillis);
-
- /**
- * Define a global timeout for all requests.
- *
- * <p>Once this timeout expires, all running requests are aborted and no
- * further requests are made. Setting this value to 0 disables the
- * global timeout. Default is 1 hour (60 * 60 * 1000).</p>
- *
- * @since 1.0.0
- */
- public void setGlobalTimeout(long globalTimeoutMillis);
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines();
-
- /**
- * Download the previously configured relay descriptors and make them
- * available via the returned blocking iterator.
- *
- * <p>Whenever the downloader runs out of descriptors and expects to
- * provide more shortly after, it blocks the caller. This method can
- * only be run once.</p>
- *
- * @since 1.0.0
- */
- public Iterator<DescriptorRequest> downloadDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorFile.java b/src/org/torproject/descriptor/DescriptorFile.java
deleted file mode 100644
index 417d7f9..0000000
--- a/src/org/torproject/descriptor/DescriptorFile.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Container for descriptors read from a file.
- *
- * <p>When the {@link DescriptorReader} reads descriptors from local files
- * it provides an iterator over these containers which in turn contain
- * references to classes implementing the {@link Descriptor} interface.
- * This container also stores potentially useful meta-data about the
- * descriptor file.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorFile {
-
- /**
- * Return the directory where this descriptor file was contained, or
- * null if the file was contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getDirectory();
-
- /**
- * Return the tarball where this descriptor file was contained, or null
- * if the file was not contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getTarball();
-
- /**
- * Return the descriptor file itself, or null if the descriptor file
- * was contained in a tarball.
- *
- * @since 1.0.0
- */
- public File getFile();
-
- /**
- * Return the descriptor file name, which is either the absolute path
- * of the file on disk, or the tar file entry name.
- *
- * @since 1.0.0
- */
- public String getFileName();
-
- /**
- * Return the time in milliseconds since the epoch when the descriptor
- * file on disk was last modified.
- *
- * @since 1.0.0
- */
- public long getLastModified();
-
- /**
- * Return the descriptors contained in the descriptor file.
- *
- * @since 1.0.0
- */
- public List<Descriptor> getDescriptors();
-
- /**
- * Return the first exception that was thrown when reading this file or
- * parsing its content, or null if no exception was thrown.
- *
- * @since 1.0.0
- */
- public Exception getException();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorParseException.java b/src/org/torproject/descriptor/DescriptorParseException.java
deleted file mode 100644
index 309d3f7..0000000
--- a/src/org/torproject/descriptor/DescriptorParseException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Thrown if raw descriptor contents cannot be parsed to one or more
- * {@link Descriptor} instances, according to descriptor specifications.
- *
- * @since 1.0.0
- */
-@SuppressWarnings("deprecation")
-public class DescriptorParseException
- extends org.torproject.descriptor.impl.DescriptorParseException {
- private static final long serialVersionUID = 100L;
- public DescriptorParseException(String message) {
- super(message);
- }
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorParser.java b/src/org/torproject/descriptor/DescriptorParser.java
deleted file mode 100644
index 680b8b2..0000000
--- a/src/org/torproject/descriptor/DescriptorParser.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Descriptor source that parses descriptors from raw descriptor contents.
- *
- * <p>Unlike most of the other descriptor sources this descriptor source
- * does not operate in a batch-processing mode. It takes the raw
- * descriptor contents of one or more descriptors, parses them, and
- * returns a list of descriptors.</p>
- *
- * <p>This descriptor source is internally used by other descriptor
- * sources but can also be used directly by applications that obtain
- * raw descriptor contents via other means than one of the existing
- * descriptor sources.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorParser {
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines);
-
- /**
- * Parse descriptors in the given byte array, possibly parsing the
- * publication time from the file name, depending on the descriptor
- * type.
- *
- * @since 1.0.0
- */
- public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
- String fileName) throws DescriptorParseException;
-}
diff --git a/src/org/torproject/descriptor/DescriptorReader.java b/src/org/torproject/descriptor/DescriptorReader.java
deleted file mode 100644
index 771755e..0000000
--- a/src/org/torproject/descriptor/DescriptorReader.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.io.File;
-import java.util.Iterator;
-import java.util.SortedMap;
-
-/**
- * Descriptor source that reads descriptors from local files and provides
- * an iterator over parsed descriptors.
- *
- * <p>This descriptor source is likely the most widely used one, possibly
- * in combination with {@link DescriptorCollector} to synchronize
- * descriptors from the CollecTor service.</p>
- *
- * <p>Reading descriptors is done in a batch which starts after setting
- * any configuration options and initiating the read process.</p>
- *
- * <p>Code sample:</p>
- * <pre>{@code
- * DescriptorReader descriptorReader =
- * DescriptorSourceFactory.createDescriptorReader();
- * // Read descriptors from local directory called in/.
- * descriptorReader.addDirectory(new File("in"));
- * Iterator<DescriptorFile> descriptorFiles =
- * descriptorReader.readDescriptors();
- * while (descriptorFiles.hasNext()) {
- * DescriptorFile descriptorFile = descriptorFiles.next();
- * for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- * if ((descriptor instanceof RelayNetworkStatusConsensus)) {
- * // Only process network status consensuses, ignore the rest.
- * RelayNetworkStatusConsensus consensus =
- * (RelayNetworkStatusConsensus) descriptor;
- * processConsensus(consensus);
- * }
- * }
- * }}</pre>
- *
- * @since 1.0.0
- */
-public interface DescriptorReader {
-
- /**
- * Add a local directory to read descriptors from, which may contain
- * descriptor files or tarballs containing descriptor files.
- *
- * @since 1.0.0
- */
- public void addDirectory(File directory);
-
- /**
- * Add a tarball to read descriptors from, which may be uncompressed,
- * bz2-compressed, or xz-compressed.
- *
- * @since 1.0.0
- */
- public void addTarball(File tarball);
-
- /**
- * Exclude files that are listed in the given history file and that
- * haven't changed since they have last been read.
- *
- * <p>Add a new line for each descriptor that is read in this execution
- * and remove lines for files that don't exist anymore.</p>
- *
- * <p>Lines in the history file contain the last modified time in
- * milliseconds since the epoch and the absolute path of a file.</p>
- *
- * @since 1.0.0
- */
- public void setExcludeFiles(File historyFile);
-
- /**
- * Exclude files if they haven't changed since the corresponding last
- * modified timestamps.
- *
- * <p>Can be used instead of (or in addition to) a history file.</p>
- *
- * @since 1.0.0
- */
- public void setExcludedFiles(SortedMap<String, Long> excludedFiles);
-
- /**
- * Return files and last modified timestamps of files that exist in the
- * input directory or directories, but that have been excluded from
- * parsing, because they haven't changed since they were last read.
- *
- * <p>Can be used instead of (or in addition to) a history file when
- * combined with the set of parsed files.</p>
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExcludedFiles();
-
- /**
- * Return files and last modified timestamps of files that exist in the
- * input directory or directories and that have been parsed.
- *
- * <p>Can be used instead of (or in addition to) a history file when
- * combined with the set of excluded files.</p>
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getParsedFiles();
-
- /**
- * Fail descriptor parsing when encountering an unrecognized line.
- *
- * <p>This option is not set by default, because the Tor specifications
- * allow for new lines to be added that shall be ignored by older Tor
- * versions. But some applications may want to handle unrecognized
- * descriptor lines explicitly.</p>
- *
- * @since 1.0.0
- */
- public void setFailUnrecognizedDescriptorLines();
-
- /**
- * Don't keep more than this number of parsed descriptor files in the
- * queue.
- *
- * <p>The default is 100, but if descriptor files contain hundreds or
- * even thousands of descriptors, that default may be too high.</p>
- *
- * @since 1.0.0
- */
- public void setMaxDescriptorFilesInQueue(int max);
-
- /**
- * Read the previously configured descriptors and make them available
- * via the returned blocking iterator.
- *
- * <p>Whenever the reader runs out of descriptors and expects to provide
- * more shortly after, it blocks the caller. This method can only be
- * run once.</p>
- *
- * @since 1.0.0
- */
- public Iterator<DescriptorFile> readDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorRequest.java b/src/org/torproject/descriptor/DescriptorRequest.java
deleted file mode 100644
index c36c0c0..0000000
--- a/src/org/torproject/descriptor/DescriptorRequest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Container for descriptors downloaded from a directory authority or
- * mirror.
- *
- * <p>When the {@link DescriptorDownloader} downloads descriptors from
- * directory authorities or mirrors it provides an iterator over these
- * containers which in turn contain references to classes implementing the
- * {@link Descriptor} interface. This container also stores potentially
- * useful meta-data about the descriptor request.</p>
- *
- * @since 1.0.0
- */
-public interface DescriptorRequest {
-
- /**
- * Return the request URL that was used in this request.
- *
- * @since 1.0.0
- */
- public String getRequestUrl();
-
- /**
- * Return the nickname of the directory mirror or authority as
- * previously configured.
- *
- * @since 1.0.0
- */
- public String getDirectoryNickname();
-
- /**
- * Return the first exception that was thrown when making this request
- * or parsing the response, or null if no exception was thrown.
- *
- * @since 1.0.0
- */
- public Exception getException();
-
- /**
- * Return the response code that the directory mirror or authority
- * returned.
- *
- * @since 1.0.0
- */
- public int getResponseCode();
-
- /**
- * Return the time in milliseconds since the epoch when this request
- * was started.
- *
- * @since 1.0.0
- */
- public long getRequestStart();
-
- /**
- * Return the time in milliseconds since the epoch when this request
- * ended.
- *
- * @since 1.0.0
- */
- public long getRequestEnd();
-
- /**
- * Return whether this request ended, because the connect timeout has
- * expired.
- *
- * @since 1.0.0
- */
- public boolean connectTimeoutHasExpired();
-
- /**
- * Return whether this request ended, because the read timeout has
- * expired.
- *
- * @since 1.0.0
- */
- public boolean readTimeoutHasExpired();
-
- /**
- * Return whether this request ended, because the global timeout for
- * all requests has expired.
- *
- * @since 1.0.0
- */
- public boolean globalTimeoutHasExpired();
-
- /**
- * Return the descriptors contained in the reply.
- *
- * @since 1.0.0
- */
- public List<Descriptor> getDescriptors();
-}
-
diff --git a/src/org/torproject/descriptor/DescriptorSourceFactory.java b/src/org/torproject/descriptor/DescriptorSourceFactory.java
deleted file mode 100644
index af13f39..0000000
--- a/src/org/torproject/descriptor/DescriptorSourceFactory.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Factory for descriptor sources which in turn produce descriptors.
- *
- * <p>Descriptor sources are the only producers of classes implementing
- * the {@link Descriptor} superinterface. There exist descriptor sources
- * for obtaining remote descriptor data ({@link DescriptorDownloader} and
- * {@link DescriptorCollector}) and descriptor sources for processing
- * local descriptor data ({@link DescriptorReader} and
- * {@link DescriptorParser}).</p>
- *
- * <p>By default, this factory returns implementations from the library's
- * own impl package. This may be overridden by setting Java properties,
- * though most users will simply use the default implementations.</p>
- *
- * <p>These properties can be used for setting the implementation:</p>
- * <ul>
- * <li>{@code descriptor.collector}</li>
- * <li>{@code descriptor.downloader}</li>
- * <li>{@code descriptor.parser}</li>
- * <li>{@code descriptor.reader}</li>
- * </ul>
- *
- * <p>Assuming the classpath contains the special implementation
- * referenced, your application classes as well as a descriptor API jar
- * the following is an example for using a different implementation of the
- * descriptor downloader:</p>
- *
- * <p><code>
- * java -Ddescriptor.downloader=my.special.descriptorimpl.Downloader my.app.Mainclass
- * </code></p>
- *
- * @since 1.0.0
- */
-public final class DescriptorSourceFactory {
-
- /**
- * Default implementation of the {@link DescriptorDownloader}
- * descriptor source.
- *
- * @since 1.0.0
- */
- public final static String DOWNLOADER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorDownloaderImpl";
-
- /**
- * Default implementation of the {@link DescriptorParser} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String PARSER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorParserImpl";
-
- /**
- * Default implementation of the {@link DescriptorReader} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String READER_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorReaderImpl";
-
- /**
- * Default implementation of the {@link DescriptorCollector} descriptor
- * source.
- *
- * @since 1.0.0
- */
- public final static String COLLECTOR_DEFAULT =
- "org.torproject.descriptor.impl.DescriptorCollectorImpl";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorParser} descriptor source, which is by default set
- * to the class in {@link #PARSER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String PARSER_PROPERTY = "descriptor.parser";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorReader} descriptor source, which is by default set
- * to the class in {@link #READER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String READER_PROPERTY = "descriptor.reader";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorDownloader} descriptor source, which is by default
- * set to the class in {@link #DOWNLOADER_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String DOWNLOADER_PROPERTY =
- "descriptor.downloader";
-
- /**
- * Property name for overriding the implementation of the
- * {@link DescriptorCollector} descriptor source, which is by default
- * set to the class in {@link #COLLECTOR_DEFAULT}.
- *
- * @since 1.0.0
- */
- public final static String COLLECTOR_PROPERTY = "descriptor.collector";
-
- /**
- * Create a new {@link DescriptorParser} by instantiating the class in
- * {@link #PARSER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorParser createDescriptorParser() {
- return (DescriptorParser) retrieve(PARSER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorReader} by instantiating the class in
- * {@link #READER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorReader createDescriptorReader() {
- return (DescriptorReader) retrieve(READER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorDownloader} by instantiating the class
- * in {@link #DOWNLOADER_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorDownloader createDescriptorDownloader() {
- return (DescriptorDownloader) retrieve(DOWNLOADER_PROPERTY);
- }
-
- /**
- * Create a new {@link DescriptorCollector} by instantiating the class
- * in {@link #COLLECTOR_PROPERTY}.
- *
- * @since 1.0.0
- */
- public final static DescriptorCollector createDescriptorCollector() {
- return (DescriptorCollector) retrieve(COLLECTOR_PROPERTY);
- }
-
- private final static <T> Object retrieve(String type) {
- Object object;
- String clazzName = null;
- try {
- switch (type) {
- case PARSER_PROPERTY:
- clazzName = System.getProperty(type, PARSER_DEFAULT);
- break;
- case DOWNLOADER_PROPERTY:
- clazzName = System.getProperty(type, DOWNLOADER_DEFAULT);
- break;
- case READER_PROPERTY:
- clazzName = System.getProperty(type, READER_DEFAULT);
- break;
- case COLLECTOR_PROPERTY:
- clazzName = System.getProperty(type, COLLECTOR_DEFAULT);
- break;
- }
- object = ClassLoader.getSystemClassLoader().loadClass(clazzName).
- newInstance();
- } catch (ClassNotFoundException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- } catch (InstantiationException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- } catch (IllegalAccessException ex) {
- throw new ImplementationNotAccessibleException("Cannot load class "
- + clazzName + "for type " + type, ex);
- }
- return object;
- }
-}
-
diff --git a/src/org/torproject/descriptor/DirSourceEntry.java b/src/org/torproject/descriptor/DirSourceEntry.java
deleted file mode 100644
index 96d81ee..0000000
--- a/src/org/torproject/descriptor/DirSourceEntry.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains details about an authority and its vote that contributed to a
- * consensus.
- *
- * <p>A directory source entry is not a descriptor type of its own but is
- * part of a network status consensus
- * ({@link RelayNetworkStatusConsensus}).</p>
- *
- * @since 1.0.0
- */
-public interface DirSourceEntry {
-
- /**
- * Return the raw directory source entry bytes.
- *
- * @since 1.0.0
- */
- public byte[] getDirSourceEntryBytes();
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.2.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getIp();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return the TCP port where this authority accepts TLS connections for
- * the main OR protocol.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return whether this directory source entry was created using a
- * legacy key.
- *
- * @since 1.0.0
- */
- public boolean isLegacy();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the SHA-1 vote digest, encoded as 40 lower-case hexadecimal
- * characters.
- *
- * @since 1.0.0
- */
- public String getVoteDigest();
-}
-
diff --git a/src/org/torproject/descriptor/DirectoryKeyCertificate.java b/src/org/torproject/descriptor/DirectoryKeyCertificate.java
deleted file mode 100644
index 07211ef..0000000
--- a/src/org/torproject/descriptor/DirectoryKeyCertificate.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a key certificate in the version 3 directory protocol.
- *
- * <p>Every directory authority in the version 3 directory protocol uses
- * two keys: a medium-term signing key, and a long-term authority identity
- * key. (Authorities also have a relay identity key used in their role as
- * a relay and by earlier versions of the directory protocol.) The
- * identity key is used from time to time to sign new key certificates
- * containing signing keys. The contained signing key is used to sign key
- * certificates and status documents.</p>
- *
- * @since 1.0.0
- */
-public interface DirectoryKeyCertificate extends Descriptor {
-
- /**
- * Return the version of this descriptor, which must be 3 or higher.
- *
- * @since 1.0.0
- */
- public int getDirKeyCertificateVersion();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the certificate does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or -1 if the certificate does not contain a port.
- *
- * @since 1.0.0
- */
- public int getPort();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the authority's identity key in PEM format.
- *
- * @since 1.0.0
- */
- public String getDirIdentityKey();
-
- /**
- * Return the time in milliseconds since the epoch when the authority's
- * signing key and this key certificate were generated.
- *
- * @since 1.0.0
- */
- public long getDirKeyPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch after which the
- * authority's signing key is no longer valid.
- *
- * @since 1.0.0
- */
- public long getDirKeyExpiresMillis();
-
- /**
- * Return the authority's signing key in PEM format.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return the signature of the authority's identity key made using the
- * authority's signing key, or null if the certificate does not contain
- * such a signature.
- *
- * @since 1.0.0
- */
- public String getDirKeyCrosscert();
-
- /**
- * Return the certificate signature from the initial item
- * "dir-key-certificate-version" until the final item
- * "dir-key-certification", signed with the authority identity key.
- *
- * @since 1.0.0
- */
- public String getDirKeyCertification();
-
- /**
- * Return the SHA-1 certificate digest, encoded as 40 lower-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getCertificateDigest();
-}
-
diff --git a/src/org/torproject/descriptor/DirectorySignature.java b/src/org/torproject/descriptor/DirectorySignature.java
deleted file mode 100644
index 8877a4e..0000000
--- a/src/org/torproject/descriptor/DirectorySignature.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains the signature of a network status consensus or vote.
- *
- * <p>A directory signature is not a descriptor type of its own but is
- * part of a network status consensus
- * ({@link RelayNetworkStatusConsensus}) or vote
- * ({@link RelayNetworkStatusVote}).</p>
- *
- * @since 1.0.0
- */
-public interface DirectorySignature {
-
- /**
- * Return the digest algorithm, which is "sha1" by default and which
- * can be "sha256" or another digest algorithm.
- *
- * @since 1.0.0
- */
- public String getAlgorithm();
-
- /**
- * Return the SHA-1 digest of the authority's long-term identity key in
- * the version 3 directory protocol, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the SHA-1 digest of the authority's medium-term signing key
- * in the version 3 directory protocol, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getSigningKeyDigest();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key in the version 3 directory protocol.
- *
- * @since 1.0.0
- */
- public String getSignature();
-}
-
diff --git a/src/org/torproject/descriptor/ExitList.java b/src/org/torproject/descriptor/ExitList.java
deleted file mode 100644
index 2a5cb2e..0000000
--- a/src/org/torproject/descriptor/ExitList.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Contains an exit list containing the IP addresses of relays that the
- * exit list service TorDNSEL found when exiting through them.
- *
- * @since 1.0.0
- */
-public interface ExitList extends Descriptor {
-
- /**
- * End-of-line character expected in exit lists.
- *
- * @since 1.0.0
- */
- public final static String EOL = "\n";
-
- /**
- * Exit list entry containing results from a single exit scan.
- *
- * @since 1.1.0
- */
- public interface Entry {
-
- /**
- * Return the scanned relay's fingerprint, which is a SHA-1 digest of
- * the relays's public identity key, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.1.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when the scanned
- * relay's last known descriptor was published.
- *
- * @since 1.1.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the network
- * status that this scan was based on was published.
- *
- * @since 1.1.0
- */
- public long getLastStatusMillis();
-
- /**
- * Return the IP addresses that were determined in the scan with map
- * keys being IPv4 addresses in dotted-quad format and map values
- * being scan times in milliseconds since the epoch.
- *
- * @since 1.1.0
- */
- public Map<String, Long> getExitAddresses();
- }
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was downloaded.
- *
- * @since 1.0.0
- */
- public long getDownloadedMillis();
-
- /**
- * Return the unordered set of exit scan results.
- *
- * @since 1.0.0
- * @deprecated The {@link ExitListEntry} type has been deprecated and
- * superseded by {@link ExitList.Entry} which is returned by
- * {@link #getEntries()}.
- */
- @Deprecated
- public Set<ExitListEntry> getExitListEntries();
-
- /**
- * Return the unordered set of exit scan results.
- *
- * @since 1.1.0
- */
- public Set<ExitList.Entry> getEntries();
-}
-
diff --git a/src/org/torproject/descriptor/ExitListEntry.java b/src/org/torproject/descriptor/ExitListEntry.java
deleted file mode 100644
index 2a3d79f..0000000
--- a/src/org/torproject/descriptor/ExitListEntry.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Exit list entry containing results from a single exit scan.
- *
- * @since 1.0.0
- * @deprecated Superseded by {@link ExitList.Entry}.
- */
-@Deprecated
-public interface ExitListEntry extends ExitList.Entry {
-
- /**
- * Return the scanned relay's fingerprint, which is a SHA-1 digest of
- * the relays's public identity key, encoded as 40 upper-case
- * hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when the scanned
- * relay's last known descriptor was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the network
- * status that this scan was based on was published.
- *
- * @since 1.0.0
- */
- public long getLastStatusMillis();
-
- /**
- * Return the IPv4 address in dotted-quad format that was determined in
- * the scan.
- *
- * @since 1.0.0
- */
- public String getExitAddress();
-
- /**
- * Return the scan time in milliseconds since the epoch.
- *
- * @since 1.0.0
- */
- public long getScanMillis();
-}
-
diff --git a/src/org/torproject/descriptor/ExtraInfoDescriptor.java b/src/org/torproject/descriptor/ExtraInfoDescriptor.java
deleted file mode 100644
index 49efbf3..0000000
--- a/src/org/torproject/descriptor/ExtraInfoDescriptor.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-
-/**
- * Contains a relay or sanitized bridge extra-info descriptor.
- *
- * <p>Relays publish extra-info descriptors as an addendum to server
- * descriptors ({@link ServerDescriptor}) to report extraneous information
- * to the directory authorities that clients do not need to download in
- * order to function. This information primarily consists of statistics
- * gathered by the relay about its usage and can take up a lot of
- * descriptor space. The separation of server descriptors and extra-info
- * descriptors has become less relevant with the introduction of
- * microdescriptors ({@link Microdescriptor}) that are derived from server
- * descriptors by the directory authority and which clients download
- * instead of server descriptors, but it persists.</p>
- *
- * <p>Bridges publish extra-info descriptors to the bridge authority for
- * the same reason, to include statistics about their usage without
- * increasing the directory protocol overhead for bridge clients. In this
- * case, the separation of server descriptors and extra-info descriptors
- * is slightly more relevant, because there are no microdescriptors for
- * bridges, so that bridge clients still download server descriptors of
- * bridges they're using. Another reason is that bridges need to include
- * information like details of all the transports they support in their
- * descriptors, and bridge clients using one such transport are not
- * supposed to learn the details of the other transports.</p>
- *
- * <p>It's worth noting that all contents of extra-info descriptors are
- * written and signed by relays and bridges without a third party
- * verifying their correctness. The (bridge) directory authorities may
- * decide to exclude dishonest servers from the network statuses they
- * produce, but that wouldn't be reflected in extra-info descriptors.</p>
- *
- * @since 1.0.0
- */
-public interface ExtraInfoDescriptor extends Descriptor {
-
- /**
- * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
- * descriptors) or upper-case (bridge descriptors) hexadecimal
- * characters, that is used to reference this descriptor from a server
- * descriptor.
- *
- * @since 1.0.0
- */
- public String getExtraInfoDigest();
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that may be used to reference
- * this descriptor from a server descriptor.
- *
- * @since 1.1.0
- */
- public String getExtraInfoDigestSha256();
-
- /**
- * Return the server's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the server's public identity key, encoded
- * as 40 upper-case hexadecimal characters, that is typically used to
- * uniquely identify the server.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * and the corresponding server descriptor were generated.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the server's history of read bytes, or null if the descriptor
- * does not contain a bandwidth history; older Tor versions included
- * bandwidth histories in their server descriptors
- * ({@link ServerDescriptor#getReadHistory()}).
- *
- * @since 1.0.0
- */
- public BandwidthHistory getReadHistory();
-
- /**
- * Return the server's history of written bytes, or null if the
- * descriptor does not contain a bandwidth history; older Tor versions
- * included bandwidth histories in their server descriptors
- * ({@link ServerDescriptor#getWriteHistory()}).
- *
- * @since 1.0.0
- */
- public BandwidthHistory getWriteHistory();
-
- /**
- * Return a SHA-1 digest of the GeoIP database file used by this server
- * to resolve client IP addresses to country codes, encoded as 40
- * upper-case hexadecimal characters, or null if no GeoIP database
- * digest is included.
- *
- * @since 1.0.0
- */
- public String getGeoipDbDigest();
-
- /**
- * Return a SHA-1 digest of the GeoIPv6 database file used by this
- * server to resolve client IP addresses to country codes, encoded as 40
- * upper-case hexadecimal characters, or null if no GeoIPv6 database
- * digest is included.
- *
- * @since 1.0.0
- */
- public String getGeoip6DbDigest();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * directory request statistics interval ended, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getDirreqStatsEndMillis();
-
- /**
- * Return the interval length of the included directory request
- * statistics in seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getDirreqStatsIntervalLength();
-
- /**
- * Return statistics on unique IP addresses requesting v2 network
- * statuses with map keys being country codes and map values being
- * numbers of unique IP addresses rounded up to the nearest multiple of
- * 8, or null if no such statistics are included (which is the case with
- * recent Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Ips();
-
- /**
- * Return statistics on unique IP addresses requesting v3 network
- * status consensuses of any flavor with map keys being country codes
- * and map values being numbers of unique IP addresses rounded up to the
- * nearest multiple of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Ips();
-
- /**
- * Return statistics on directory requests for v2 network statuses with
- * map keys being country codes and map values being request numbers
- * rounded up to the nearest multiple of 8, or null if no such
- * statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Reqs();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor with map keys being country codes and map
- * values being request numbers rounded up to the nearest multiple of 8,
- * or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Reqs();
-
- /**
- * Return the share of requests for v2 network statuses that the server
- * expects to receive from clients, or -1.0 if this share is not
- * included (which is the case with recent Tor versions).
- *
- * @since 1.0.0
- */
- public double getDirreqV2Share();
-
- /**
- * Return the share of requests for v3 network status consensuses of
- * any flavor that the server expects to receive from clients, or -1.0
- * if this share is not included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public double getDirreqV3Share();
-
- /**
- * Return statistics on responses to directory requests for v2 network
- * statuses with map keys being response strings and map values being
- * response numbers rounded up to the nearest multiple of 4, or null if
- * no such statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2Resp();
-
- /**
- * Return statistics on responses to directory requests for v3 network
- * status consensuses of any flavor with map keys being response strings
- * and map values being response numbers rounded up to the nearest
- * multiple of 4, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3Resp();
-
- /**
- * Return statistics on directory requests for v2 network statuses to
- * the server's directory port with map keys being statistic keys and
- * map values being statistic values like counts or quantiles, or null
- * if no such statistics are included (which is the case with recent Tor
- * versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2DirectDl();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor to the server's directory port with map
- * keys being statistic keys and map values being statistic values like
- * counts or quantiles, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3DirectDl();
-
- /**
- * Return statistics on directory requests for v2 network statuses
- * tunneled through a circuit with map keys being statistic keys and map
- * values being statistic values, or null if no such statistics are
- * included (which is the case with recent Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV2TunneledDl();
-
- /**
- * Return statistics on directory requests for v3 network status
- * consensuses of any flavor tunneled through a circuit with map keys
- * being statistic keys and map values being statistic values, or null
- * if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getDirreqV3TunneledDl();
-
- /**
- * Return the directory request read history contained in this
- * descriptor, or null if no such history is contained.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getDirreqReadHistory();
-
- /**
- * Return the directory request write history contained in this
- * descriptor, or null if no such history is contained.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getDirreqWriteHistory();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * entry statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getEntryStatsEndMillis();
-
- /**
- * Return the interval length of the included entry statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getEntryStatsIntervalLength();
-
- /**
- * Return statistics on client IP addresses with map keys being country
- * codes and map values being the number of unique IP addresses that
- * have connected from that country rounded up to the nearest multiple
- * of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getEntryIps();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * cell statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getCellStatsEndMillis();
-
- /**
- * Return the interval length of the included cell statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getCellStatsIntervalLength();
-
- /**
- * Return the mean number of processed cells per circuit by circuit
- * decile starting with the loudest decile at index 0 and the quietest
- * decile at index 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public List<Integer> getCellProcessedCells();
-
- /**
- * Return the mean number of cells contained in circuit queues by
- * circuit decile starting with the loudest decile at index 0 and the
- * quietest decile at index 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public List<Double> getCellQueuedCells();
-
- /**
- * Return the mean times in milliseconds that cells spend in circuit
- * queues by circuit decile starting with the loudest decile at index 0
- * and the quietest decile at index 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public List<Integer> getCellTimeInQueue();
-
- /**
- * Return the mean number of circuits included in any of the cell
- * statistics deciles, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public int getCellCircuitsPerDecile();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * statistics on bi-directional connection usage ended, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getConnBiDirectStatsEndMillis();
-
- /**
- * Return the interval length of the included statistics on
- * bi-directional connection usage in seconds, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public long getConnBiDirectStatsIntervalLength();
-
- /**
- * Return the number of connections on which this server read and wrote
- * less than 2 KiB/s in a 10-second interval, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectBelow();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval and at least 10 times more
- * in read direction than in write direction, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectRead();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval and at least 10 times more
- * in write direction than in read direction, or -1 if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectWrite();
-
- /**
- * Return the number of connections on which this server read and wrote
- * at least 2 KiB/s in a 10-second interval but not 10 times more in
- * either direction, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public int getConnBiDirectBoth();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * exit statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getExitStatsEndMillis();
-
- /**
- * Return the interval length of the included exit statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getExitStatsIntervalLength();
-
- /**
- * Return statistics on KiB written to streams exiting the Tor network
- * by target TCP port with map keys being string representations of
- * ports (or {@code "other"}) and map values being KiB rounded up to the
- * next full KiB, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitKibibytesWritten();
-
- /**
- * Return statistics on KiB read from streams exiting the Tor network
- * by target TCP port with map keys being string representations of
- * ports (or {@code "other"}) and map values being KiB rounded up to the
- * next full KiB, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitKibibytesRead();
-
- /**
- * Return statistics on opened streams exiting the Tor network by
- * target TCP port with map keys being string representations of ports
- * (or {@code "other"}) and map values being the number of opened
- * streams, rounded up to the nearest multiple of 4, or null if no such
- * statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Long> getExitStreamsOpened();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * "geoip" statistics interval started, or -1 if no such statistics are
- * included (which is the case except for very old Tor versions).
- *
- * @since 1.0.0
- */
- public long getGeoipStartTimeMillis();
-
- /**
- * Return statistics on the origin of client IP addresses with map keys
- * being country codes and map values being the number of unique IP
- * addresses that have connected from that country between the start of
- * the statistics interval and the descriptor publication time rounded
- * up to the nearest multiple of 8, or null if no such statistics are
- * included (which is the case except for very old Tor versions).
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getGeoipClientOrigins();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * bridge statistics interval ended, or -1 if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public long getBridgeStatsEndMillis();
-
- /**
- * Return the interval length of the included bridge statistics in
- * seconds, or -1 if no such statistics are included.
- *
- * @since 1.0.0
- */
- public long getBridgeStatsIntervalLength();
-
- /**
- * Return statistics on bridge client IP addresses by country with map
- * keys being country codes and map values being the number of unique IP
- * addresses that have connected from that country rounded up to the
- * nearest multiple of 8, or null if no such statistics are included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIps();
-
- /**
- * Return statistics on bridge client IP addresses by IP version with
- * map keys being protocol families, e.g., {@code "v4"} or {@code "v6"},
- * and map values being the number of unique IP addresses rounded up to
- * the nearest multiple of 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIpVersions();
-
- /**
- * Return statistics on bridge client IP addresses by transport with
- * map keys being pluggable transport names, e.g., {@code "obfs2"} or
- * {@code "obfs3"} for known transports, {@code "<OR>"} for the default
- * onion routing protocol, or {@code "<??>"} for an unknown transport,
- * and map values being the number of unique IP addresses rounded up to
- * the nearest multiple of 8, or null if no such statistics are
- * included.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBridgeIpTransports();
-
- /**
- * Return the (possibly empty) list of pluggable transports supported
- * by this server.
- *
- * @since 1.0.0
- */
- public List<String> getTransports();
-
- /**
- * Return the time in milliseconds since the epoch when the included
- * hidden-service statistics interval ended, or -1 if no such statistics
- * are included.
- *
- * @since 1.1.0
- */
- public long getHidservStatsEndMillis();
-
- /**
- * Return the interval length of the included hidden-service statistics
- * in seconds, or -1 if no such statistics are included.
- *
- * @since 1.1.0
- */
- public long getHidservStatsIntervalLength();
-
- /**
- * Return the approximate number of RELAY cells seen in either
- * direction on a circuit after receiving and successfully processing a
- * RENDEZVOUS1 cell, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Double getHidservRendRelayedCells();
-
- /**
- * Return the obfuscation parameters applied to the original
- * measurement value of RELAY cells seen in either direction on a
- * circuit after receiving and successfully processing a RENDEZVOUS1
- * cell, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Map<String, Double> getHidservRendRelayedCellsParameters();
-
- /**
- * Return the approximate number of unique hidden-service identities
- * seen in descriptors published to and accepted by this hidden-service
- * directory, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Double getHidservDirOnionsSeen();
-
- /**
- * Return the obfuscation parameters applied to the original
- * measurement value of unique hidden-service identities seen in
- * descriptors published to and accepted by this hidden-service
- * directory, or null if no such statistics are included.
- *
- * @since 1.1.0
- */
- public Map<String, Double> getHidservDirOnionsSeenParameters();
-
- /**
- * 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
- * a signature (which is the case in sanitized bridge descriptors).
- *
- * @since 1.1.0
- */
- public String getRouterSignature();
-
- /**
- * Return the Ed25519 certificate in PEM format, or null if the
- * descriptor doesn't contain one.
- *
- * @since 1.1.0
- */
- public String getIdentityEd25519();
-
- /**
- * Return the Ed25519 master key, encoded as 43 base64 characters
- * without padding characters, which was either parsed from the optional
- * {@code "master-key-ed25519"} line or derived from the (likewise
- * optional) Ed25519 certificate following the
- * {@code "identity-ed25519"} line, or null if the descriptor contains
- * neither Ed25519 master key nor Ed25519 certificate.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-
- /**
- * Return the Ed25519 signature of the SHA-256 digest of the entire
- * descriptor, encoded as 86 base64 characters without padding
- * characters, from the first character up to and including the first
- * space after the {@code "router-sig-ed25519"} string, prefixed with
- * the string {@code "Tor router descriptor signature v1"}.
- *
- * @since 1.1.0
- */
- public String getRouterSignatureEd25519();
-}
-
diff --git a/src/org/torproject/descriptor/ImplementationNotAccessibleException.java b/src/org/torproject/descriptor/ImplementationNotAccessibleException.java
deleted file mode 100644
index c54e48f..0000000
--- a/src/org/torproject/descriptor/ImplementationNotAccessibleException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Thrown if a descriptor source implementation class cannot be found,
- * instantiated, or accessed.
- *
- * @see DescriptorSourceFactory
- * @since 1.0.0
- */
-@SuppressWarnings("serial")
-public class ImplementationNotAccessibleException
- extends RuntimeException {
-
- public ImplementationNotAccessibleException(String string,
- Throwable ex) {
- super(string, ex);
- }
-}
-
diff --git a/src/org/torproject/descriptor/Microdescriptor.java b/src/org/torproject/descriptor/Microdescriptor.java
deleted file mode 100644
index f19b7df..0000000
--- a/src/org/torproject/descriptor/Microdescriptor.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Copyright 2014--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a relay microdescriptor.
- *
- * <p>A microdescriptor is a stripped-down version of a relay server
- * descriptor ({@link RelayServerDescriptor}) generated by the directory
- * authorities by extracting and/or transforming relay server descriptor
- * contents following strict rules without adding the authority's opinion
- * about the relay. Microdescriptors are referenced from microdescriptor
- * consensuses ({@link RelayNetworkStatusConsensus}) and downloaded by
- * clients to make path-selection decisions and to build circuits.
- * Microdescriptors contain only the most relevant parts that clients care
- * about. Microdescriptors are expected to be relatively static and only
- * change about once per week.</p>
- *
- * @since 1.0.0
- */
-public interface Microdescriptor extends Descriptor {
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that is used to reference this
- * descriptor from a vote or microdescriptor consensus.
- *
- * @since 1.0.0
- */
- public String getMicrodescriptorDigest();
-
- /**
- * Return the RSA-1024 public key in PEM format used to encrypt CREATE
- * cells for this server, or null if the descriptor doesn't contain an
- * onion key.
- *
- * @since 1.0.0
- */
- public String getOnionKey();
-
- /**
- * Return the curve25519 public key, encoded as 43 base64 characters
- * without padding characters, that is used for the ntor circuit
- * extended handshake, or null if the descriptor didn't contain an
- * ntor-onion-key line.
- *
- * @since 1.0.0
- */
- public String getNtorOnionKey();
-
- /**
- * Return IP addresses and TCP ports where this server accepts TLS
- * connections for the main OR protocol, or an empty list if the server
- * does not support additional addresses or ports; entries are given in
- * the order as they are listed in the descriptor; IPv4 addresses are
- * given in dotted-quad format, IPv6 addresses use the colon-separated
- * hexadecimal format surrounded by square brackets, and TCP ports are
- * separated from the IP address using a colon.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return nicknames, $-prefixed identity fingerprints, or tuples of the
- * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
- * of servers contained in this server's family, or null if the
- * descriptor does not contain a family line.
- *
- * @since 1.0.0
- */
- public List<String> getFamilyEntries();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv4 port summary, or null if the descriptor didn't contain an
- * IPv4 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv4 targets.
- *
- * @since 1.0.0
- */
- public String getDefaultPolicy();
-
- /**
- * Return the port list of the IPv4 exit-policy summary, or null if the
- * descriptor didn't contain an IPv4 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv4 targets.
- *
- * @since 1.0.0
- */
- public String getPortList();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv6 port summary, or null if the descriptor didn't contain an
- * IPv6 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6DefaultPolicy();
-
- /**
- * Return the port list of the IPv6 exit-policy summary, or null if the
- * descriptor didn't contain an IPv6 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6PortList();
-
- /**
- * Return a SHA-1 digest of the server's RSA-1024 identity key, encoded
- * as 27 base64 characters without padding characters, that is only
- * included to prevent collisions between microdescriptors, or null if
- * no such digest is included.
- *
- * @since 1.1.0
- */
- public String getRsa1024Identity();
-
- /**
- * Return a SHA-256 digest of the server's Ed25519 identity key,
- * encoded as 43 base64 characters without padding characters, that is
- * only included to prevent collisions between microdescriptors, or null
- * if no such digest is included.
- *
- * @since 1.1.0
- */
- public String getEd25519Identity();
-}
-
diff --git a/src/org/torproject/descriptor/NetworkStatusEntry.java b/src/org/torproject/descriptor/NetworkStatusEntry.java
deleted file mode 100644
index 43b3175..0000000
--- a/src/org/torproject/descriptor/NetworkStatusEntry.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-
-/**
- * Contains an entry in a network status in the version 2 or 3 directory
- * protocol or in a bridge network status.
- *
- * <p>A network status entry is not a descriptor type of its own but is
- * part of a network status in the version 2 directory protocol
- * ({@link RelayNetworkStatus}), a vote ({@link RelayNetworkStatusVote})
- * or flavored/unflavored consensus (@link RelayNetworkStatusConsensus})
- * in the version 3 directory protocol, or a bridge network status
- * ({@link BridgeNetworkStatus}). Entries in signed directories in the
- * version 1 directory protocol are represented by router status entries
- * ({@link RouterStatusEntry}).</p>
- *
- * @since 1.0.0
- */
-public interface NetworkStatusEntry {
-
- /**
- * Return the raw network status entry bytes.
- *
- * @since 1.0.0
- */
- public byte[] getStatusEntryBytes();
-
- /**
- * Return the server nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the server's identity key, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the SHA-1 digest of the server descriptor, or null if the
- * containing network status does not contain server descriptor
- * references, like a microdesc consensus.
- *
- * @since 1.0.0
- */
- public String getDescriptor();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the server's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this server accepts TLS connections for
- * the main OR protocol.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return the TCP port where this server accepts directory-related HTTP
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return the (possibly empty) set of microdescriptor digests if the
- * containing network status is a vote or microdesc consensus, or null
- * otherwise.
- *
- * @since 1.0.0
- */
- public Set<String> getMicrodescriptorDigests();
-
- /**
- * Return additional IP addresses and TCP ports where this server
- * accepts TLS connections for the main OR protocol, or an empty list if
- * the network status doesn't contain any such additional addresses and
- * ports.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return the relay flags assigned to this server, or null if the
- * status entry didn't contain any relay flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getFlags();
-
- /**
- * Return the Tor software version, or null if the status entry didn't
- * contain version information.
- *
- * @since 1.0.0
- */
- public String getVersion();
-
- /**
- * Return the bandwidth weight of this server or -1 if the status entry
- * didn't contain a bandwidth line.
- *
- * @since 1.0.0
- */
- public long getBandwidth();
-
- /**
- * Return the measured bandwidth or -1 if the status entry either
- * didn't contain bandwidth information or didn't contain an indication
- * that this information is based on measured bandwidth.
- *
- * @since 1.0.0
- */
- public long getMeasured();
-
- /**
- * Return whether the status entry is yet unmeasured by the bandwidth
- * authorities; only included in consensuses using method 17 or higher.
- *
- * @since 1.0.0
- */
- public boolean getUnmeasured();
-
- /**
- * Return the default policy of the port summary, which can be either
- * {@code "accept"} or {@code "reject"}, or null if the status entry
- * didn't contain an exit policy summary.
- *
- * @since 1.0.0
- */
- public String getDefaultPolicy();
-
- /**
- * Return the list of ports or port intervals of the exit port summary,
- * or null if the status entry didn't contain an exit policy summary.
- *
- * @since 1.0.0
- */
- public String getPortList();
-
- /**
- * Return the server's Ed25519 master key, encoded as 43 base64
- * characters without padding characters, "none" if the relay doesn't
- * have an Ed25519 identity, or null if the status entry didn't contain
- * this information or if the status is not a vote.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-}
-
diff --git a/src/org/torproject/descriptor/RelayDirectory.java b/src/org/torproject/descriptor/RelayDirectory.java
deleted file mode 100644
index 8f3e58b..0000000
--- a/src/org/torproject/descriptor/RelayDirectory.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a signed directory in the version 1 directory protocol.
- *
- * <p>Directory authorities in the (long outdated) version 1 of the
- * directory protocol served signed directory documents containing a list
- * of signed server descriptors ({@link ServerDescriptor}) along with
- * short summaries of the status of each server
- * ({@link RouterStatusEntry}).</p>
- *
- * <p>Clients in that version of the directory protocol would fetch this
- * signed directory to get up-to-date information on the state of the
- * network and be certain that the list was attested by a trusted
- * directory authority.</p>
- *
- * <p>Signed directories in the version 1 directory protocol have first
- * been superseded by network status documents in the version 2 directory
- * protocol ({@link RelayNetworkStatus}) and later by network status
- * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
- * directory protocol.</p>
- *
- * @since 1.0.0
- */
-public interface RelayDirectory extends Descriptor {
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this authority
- * as long-term identity key and to sign network statuses, or null if
- * this key is not included in the descriptor header.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return recommended Tor versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedSoftware();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key.
- *
- * @since 1.0.0
- */
- public String getDirectorySignature();
-
- /**
- * Return router status entries, one for each contained relay.
- *
- * @since 1.0.0
- */
- public List<RouterStatusEntry> getRouterStatusEntries();
-
- /**
- * Return a list of server descriptors contained in the signed
- * directory.
- *
- * @since 1.0.0
- */
- public List<ServerDescriptor> getServerDescriptors();
-
- /**
- * Return a (very likely empty) list of exceptions from parsing the
- * contained server descriptors.
- *
- * @since 1.0.0
- */
- public List<Exception> getServerDescriptorParseExceptions();
-
- /**
- * Return the directory nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the SHA-1 directory digest, encoded as 40 lower-case
- * hexadecimal characters, that the directory authority used to sign the
- * directory.
- *
- * @since 1.0.0
- */
- public String getDirectoryDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java b/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java
deleted file mode 100644
index 73f8438..0000000
--- a/src/org/torproject/descriptor/RelayExtraInfoDescriptor.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a relay extra-info descriptor.
- *
- * <p>Relay extra-info descriptors share many contents with sanitized
- * bridge extra-info descriptors ({@link BridgeExtraInfoDescriptor}),
- * which is why they share a common superinterface
- * ({@link ExtraInfoDescriptor}). The main purpose of having two
- * subinterfaces is being able to distinguish descriptor types more
- * easily.</p>
- *
- * @since 1.1.0
- */
-public interface RelayExtraInfoDescriptor extends ExtraInfoDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatus.java b/src/org/torproject/descriptor/RelayNetworkStatus.java
deleted file mode 100644
index db3ddac..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatus.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status document in the version 2 directory protocol.
- *
- * <p>Directory authorities in the (outdated) version 2 of the directory
- * protocol published signed network status documents. Each network
- * status listed, for every relay in the network
- * ({@link NetworkStatusEntry}): a hash of its identity key, a hash of its
- * most recent server descriptor, and a summary of what the authority
- * believed about its status.</p>
- *
- * <p>Clients would download the authorities' network status documents in
- * turn, and believe statements about routers iff they were attested to by
- * more than half of the authorities.</p>
- *
- * <p>Network status documents in the version 2 directory protocol
- * supersede signed directories in the version 1 directory protocol
- * ({@link RelayDirectory}) and have been superseded by network status
- * consensuses ({@link RelayNetworkStatusConsensus}) in the version 3
- * directory protocol.</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatus extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 2.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.0.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the descriptor does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirport();
-
- /**
- * Return a SHA-1 digest of the authority's public identity key,
- * encoded as 40 upper-case hexadecimal characters, which is also used
- * to sign network statuses.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this authority
- * as long-term identity key and to sign network statuses.
- *
- * @since 1.0.0
- */
- public String getDirSigningKey();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * authority does not recommend server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * authority does not recommend client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the set of flags that this directory assigns to relays, or
- * null if the status does not assign such flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getDirOptions();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the directory signature string made with the authority's
- * identity key.
- *
- * @since 1.0.0
- */
- public String getDirectorySignature();
-
- /**
- * Return the SHA-1 status digest, encoded as 40 lower-case hexadecimal
- * characters, that the directory authority used to sign the network
- * status.
- *
- * @since 1.0.0
- */
- public String getStatusDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
deleted file mode 100644
index 15fdaca..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status consensus in the version 3 directory protocol.
- *
- * <p>Directory authorities in the version 3 of the directory protocol
- * periodically generate a view of the current descriptors and status for
- * known relays and send a signed summary of this view to the other
- * authorities ({@link RelayNetworkStatusVote}). The authorities compute
- * the result of this vote and sign a network status consensus containing
- * the result of the vote, which is this document.</p>
- *
- * <p>Clients use consensus documents to find out when their list of
- * relays is out-of-date by looking at the contained network status
- * entries ({@link NetworkStatusEntry}). If it is, they download any
- * missing server descriptors ({@link ServerDescriptor}).</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatusConsensus extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 3 or
- * higher.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the consensus flavor name, which denotes the variant of the
- * original, unflavored consensus, encoded as a string of alphanumeric
- * characters and dashes, or null if this descriptor is the unflavored
- * consensus.
- *
- * @since 1.0.0
- */
- public String getConsensusFlavor();
-
- /**
- * Return the consensus method number of this descriptor, which is the
- * highest consensus method supported by more than 2/3 of voting
- * authorities, or 0 if no consensus method is contained in the
- * descriptor.
- *
- * @since 1.0.0
- */
- public int getConsensusMethod();
-
- /**
- * Return the time in milliseconds since the epoch at which this
- * descriptor became valid.
- *
- * @since 1.0.0
- */
- public long getValidAfterMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which this
- * descriptor is the freshest that is available.
- *
- * @since 1.0.0
- */
- public long getFreshUntilMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which this
- * descriptor was valid.
- *
- * @since 1.0.0
- */
- public long getValidUntilMillis();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect votes from the other authorities when producing the
- * next consensus.
- *
- * @since 1.0.0
- */
- public long getVoteSeconds();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect signatures from the other authorities when producing
- * the next consensus.
- *
- * @since 1.0.0
- */
- public long getDistSeconds();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * consensus does not contain an opinion about server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * consensus does not contain an opinion about client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * 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
- * authorities and agreed upon by the majority of directory authorities,
- * or null if the consensus does not contain package information.
- *
- * @since 1.3.0
- */
- public List<String> getPackageLines();
-
- /**
- * Return known relay flags in this descriptor that were contained in
- * enough votes for this consensus to be an authoritative opinion for
- * these relay flags.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getKnownFlags();
-
- /**
- * Return consensus parameters contained in this descriptor with map
- * keys being case-sensitive parameter identifiers and map values being
- * parameter values, or null if the consensus doesn't contain consensus
- * parameters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getConsensusParams();
-
- /**
- * Return directory source entries for each directory authority that
- * contributed to the consensus, with map keys being SHA-1 digests of
- * the authorities' identity keys in the version 3 directory protocol,
- * encoded as 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirSourceEntry> getDirSourceEntries();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return directory signatures of this consensus, with map keys being
- * SHA-1 digests of the authorities' identity keys in the version 3
- * directory protocol, encoded as 40 upper-case hexadecimal characters.
- *
- * @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by an authority using the same
- * identity key digest and different algorithms.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirectorySignature> getDirectorySignatures();
-
- /**
- * Return the list of signatures contained in this consensus.
- *
- * @since 1.3.0
- */
- public List<DirectorySignature> getSignatures();
-
- /**
- * Return optional weights to be applied to router bandwidths during
- * path selection with map keys being case-sensitive weight identifiers
- * and map values being weight values, or null if the consensus doesn't
- * contain such weights.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getBandwidthWeights();
-
- /**
- * Return the SHA-1 digest of this consensus, encoded as 40 upper-case
- * hexadecimal characters that directory authorities use to sign the
- * consensus.
- *
- * @since 1.0.0
- */
- public String getConsensusDigest();
-}
-
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
deleted file mode 100644
index 1f77db6..0000000
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-/**
- * Contains a network status vote in the version 3 directory protocol.
- *
- * <p>Directory authorities in the version 3 of the directory protocol
- * periodically generate a view of the current descriptors and status for
- * known relays and send a signed summary of this view to the other
- * authorities, which is this document. The authorities compute the
- * result of this vote and sign a network status consensus containing the
- * result of the vote ({@link RelayNetworkStatusConsensus}).</p>
- *
- * @since 1.0.0
- */
-public interface RelayNetworkStatusVote extends Descriptor {
-
- /**
- * Return the document format version of this descriptor which is 3 or
- * higher.
- *
- * @since 1.0.0
- */
- public int getNetworkStatusVersion();
-
- /**
- * Return the list of consensus method numbers supported by this
- * authority, or null if the descriptor doesn't say so, which would mean
- * that only method 1 is supported.
- *
- * @since 1.0.0
- */
- public List<Integer> getConsensusMethods();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * was published.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch at which the
- * consensus is supposed to become valid.
- *
- * @since 1.0.0
- */
- public long getValidAfterMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which the
- * consensus is supposed to be the freshest that is available.
- *
- * @since 1.0.0
- */
- public long getFreshUntilMillis();
-
- /**
- * Return the time in milliseconds since the epoch until which the
- * consensus is supposed to be valid.
- *
- * @since 1.0.0
- */
- public long getValidUntilMillis();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect votes from the other authorities when producing the
- * next consensus.
- *
- * @since 1.0.0
- */
- public long getVoteSeconds();
-
- /**
- * Return the number of seconds that the directory authorities will
- * allow to collect signatures from the other authorities when producing
- * the next consensus.
- *
- * @since 1.0.0
- */
- public long getDistSeconds();
-
- /**
- * Return recommended Tor versions for server usage, or null if the
- * authority does not recommend server versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedServerVersions();
-
- /**
- * Return recommended Tor versions for client usage, or null if the
- * authority does not recommend client versions.
- *
- * @since 1.0.0
- */
- public List<String> getRecommendedClientVersions();
-
- /**
- * 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
- * null if this descriptor does not contain package information.
- *
- * @since 1.3.0
- */
- public List<String> getPackageLines();
-
- /**
- * Return known relay flags by this authority.
- *
- * @since 1.0.0
- */
- public SortedSet<String> getKnownFlags();
-
- /**
- * Return the minimum uptime in seconds that this authority requires
- * for assigning the Stable flag, or -1 if the authority doesn't report
- * this value.
- *
- * @since 1.0.0
- */
- public long getStableUptime();
-
- /**
- * Return the minimum MTBF (mean time between failure) that this
- * authority requires for assigning the Stable flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getStableMtbf();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Fast flag, or -1 if the authority doesn't report this
- * value.
- *
- * @since 1.0.0
- */
- public long getFastBandwidth();
-
- /**
- * Return the minimum WFU (weighted fractional uptime) in percent that
- * this authority requires for assigning the Guard flag, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public double getGuardWfu();
-
- /**
- * Return the minimum weighted time in seconds that this authority
- * needs to know about a relay before assigning the Guard flag, or -1 if
- * the authority doesn't report this information.
- *
- * @since 1.0.0
- */
- public long getGuardTk();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getGuardBandwidthIncludingExits();
-
- /**
- * Return the minimum bandwidth that this authority requires for
- * assigning the Guard flag if exits can not be guards, or -1 if the
- * authority doesn't report this value.
- *
- * @since 1.0.0
- */
- public long getGuardBandwidthExcludingExits();
-
- /**
- * Return 1 if the authority has measured enough MTBF info to use the
- * MTBF requirement instead of the uptime requirement for assigning the
- * Stable flag, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.0.0
- */
- public int getEnoughMtbfInfo();
-
- /**
- * Return 1 if the authority has enough measured bandwidths that it'll
- * ignore the advertised bandwidth claims of routers without measured
- * bandwidth, 0 if not, or -1 if the authority doesn't report this
- * information.
- *
- * @since 1.1.0
- */
- public int getIgnoringAdvertisedBws();
-
- /**
- * Return consensus parameters contained in this descriptor with map
- * keys being case-sensitive parameter identifiers and map values being
- * parameter values, or null if the authority doesn't include consensus
- * parameters in its vote.
- *
- * @since 1.0.0
- */
- public SortedMap<String, Integer> getConsensusParams();
-
- /**
- * Return the authority's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the authority's long-term authority
- * identity key used for the version 3 directory protocol, encoded as
- * 40 upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getIdentity();
-
- /**
- * Return the authority's hostname.
- *
- * @since 1.2.0
- */
- public String getHostname();
-
- /**
- * Return the authority's primary IPv4 address in dotted-quad format,
- * or null if the descriptor does not contain an address.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this authority accepts directory-related
- * HTTP connections, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getDirport();
-
- /**
- * Return the TCP port where this authority accepts TLS connections for
- * the main OR protocol, or 0 if the authority does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getOrport();
-
- /**
- * Return the contact information for this authority, which may contain
- * non-ASCII characters, or null if no contact information is included
- * in the descriptor.
- *
- * @since 1.0.0
- */
- public String getContactLine();
-
- /**
- * Return the version of the directory key certificate used by this
- * authority, which must be 3 or higher.
- *
- * @since 1.0.0
- */
- public int getDirKeyCertificateVersion();
-
- /**
- * Return the SHA-1 digest for an obsolete authority identity key still
- * used by this authority to keep older clients working, or null if this
- * authority does not use such a key.
- *
- * @since 1.0.0
- */
- public String getLegacyDirKey();
-
- /**
- * Return the authority's identity key in PEM format.
- *
- * @since 1.2.0
- */
- public String getDirIdentityKey();
-
- /**
- * Return the time in milliseconds since the epoch when the authority's
- * signing key and corresponding key certificate were generated.
- *
- * @since 1.0.0
- */
- public long getDirKeyPublishedMillis();
-
- /**
- * Return the time in milliseconds since the epoch after which the
- * authority's signing key is no longer valid.
- *
- * @since 1.0.0
- */
- public long getDirKeyExpiresMillis();
-
- /**
- * Return the authority's signing key in PEM format.
- *
- * @since 1.2.0
- */
- public String getDirSigningKey();
-
- /**
- * Return the SHA-1 digest of the authority's signing key, encoded as
- * 40 upper-case hexadecimal characters, or null if this digest cannot
- * be obtained from the directory signature.
- *
- * @deprecated Removed in order to be more explicit that authorities may
- * use different digest algorithms than "sha1"; see
- * {@link #getSignatures()} and
- * {@link DirectorySignature#getSigningKeyDigest()} for
- * alternatives.
- *
- * @since 1.0.0
- */
- public String getSigningKeyDigest();
-
- /**
- * Return the signature of the authority's identity key made using the
- * authority's signing key, or null if the vote does not contain such a
- * signature.
- *
- * @since 1.2.0
- */
- public String getDirKeyCrosscert();
-
- /**
- * Return the certificate signature from the initial item
- * "dir-key-certificate-version" until the final item
- * "dir-key-certification", signed with the authority identity key.
- *
- * @since 1.2.0
- */
- public String getDirKeyCertification();
-
- /**
- * Return status entries for each contained server, with map keys being
- * SHA-1 digests of the servers' public identity keys, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public SortedMap<String, NetworkStatusEntry> getStatusEntries();
-
- /**
- * Return whether a status entry with the given relay fingerprint
- * (SHA-1 digest of the server's public identity key, encoded as 40
- * upper-case hexadecimal characters) exists; convenience method for
- * {@code getStatusEntries().containsKey(fingerprint)}.
- *
- * @since 1.0.0
- */
- public boolean containsStatusEntry(String fingerprint);
-
- /**
- * Return a status entry by relay fingerprint (SHA-1 digest of the
- * server's public identity key, encoded as 40 upper-case hexadecimal
- * characters), or null if no such status entry exists; convenience
- * method for {@code getStatusEntries().get(fingerprint)}.
- *
- * @since 1.0.0
- */
- public NetworkStatusEntry getStatusEntry(String fingerprint);
-
- /**
- * Return the directory signature of this vote, with the single map key
- * being the SHA-1 digest of the authority's identity key in the version
- * 3 directory protocol, encoded as 40 upper-case hexadecimal
- * characters.
- *
- * @deprecated Replaced by {@link #getSignatures()} which permits an
- * arbitrary number of signatures made by the authority using the same
- * identity key digest and different algorithms.
- *
- * @since 1.0.0
- */
- public SortedMap<String, DirectorySignature> getDirectorySignatures();
-
- /**
- * Return a list of signatures contained in this vote, which is
- * typically a single signature made by the authority but which may also
- * be more than one signature made with different keys or algorithms.
- *
- * @since 1.3.0
- */
- public List<DirectorySignature> getSignatures();
-}
-
diff --git a/src/org/torproject/descriptor/RelayServerDescriptor.java b/src/org/torproject/descriptor/RelayServerDescriptor.java
deleted file mode 100644
index 6ef3140..0000000
--- a/src/org/torproject/descriptor/RelayServerDescriptor.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Copyright 2015--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a relay server descriptor.
- *
- * <p>Relay server descriptors share many contents with sanitized bridge
- * server descriptors ({@link BridgeServerDescriptor}), which is why they
- * share a common superinterface ({@link ServerDescriptor}). The main
- * purpose of having two subinterfaces is being able to distinguish
- * descriptor types more easily.</p>
- *
- * @since 1.1.0
- */
-public interface RelayServerDescriptor extends ServerDescriptor {
-
-}
-
diff --git a/src/org/torproject/descriptor/RouterStatusEntry.java b/src/org/torproject/descriptor/RouterStatusEntry.java
deleted file mode 100644
index f9a56db..0000000
--- a/src/org/torproject/descriptor/RouterStatusEntry.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-/**
- * Contains a router status entry contained in a signed directory in the
- * version 1 directory protocol.
- *
- * <p>Directory authorities in the (long outdated) version 1 of the
- * directory protocol included router status entries with short summaries
- * of the status of each server in the signed directories they produced
- * ({@link RelayDirectory}). These entries contained references to server
- * descriptors published by relays together with the authorities' opinion
- * on whether relays were verified and live.</p>
- *
- * @since 1.0.0
- */
-public interface RouterStatusEntry {
-
- /**
- * Return the relay nickname consisting of 1 to 19 alphanumeric
- * characters, or null if the relay is unverified.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return a SHA-1 digest of the relay's identity key, encoded as 40
- * upper-case hexadecimal characters.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return whether the relay is verified.
- *
- * @since 1.0.0
- */
- public boolean isVerified();
-
- /**
- * Return whether the relay is live.
- *
- * @since 1.0.0
- */
- public boolean isLive();
-}
-
diff --git a/src/org/torproject/descriptor/ServerDescriptor.java b/src/org/torproject/descriptor/ServerDescriptor.java
deleted file mode 100644
index d1af421..0000000
--- a/src/org/torproject/descriptor/ServerDescriptor.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/* Copyright 2011--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-
-/**
- * Contains a relay or sanitized bridge server descriptor.
- *
- * <p>Relays publish server descriptors to the directory authorities to
- * register in the network. Server descriptors contain information about
- * the capabilities of a server, like their exit policy, that clients use
- * to select servers for their circuits (along with information provided
- * by directory authorities on reachability, stability, and capacity of
- * servers). Server descriptors also contain network addresses and
- * cryptographic material that clients use to build circuits.</p>
- *
- * <p>Prior to the introduction of microdescriptors
- * ({@link Microdescriptor}), the directory authorities included
- * cryptographic digests of server descriptors in network statuses
- * ({@link RelayNetworkStatusConsensus}) and clients downloaded all
- * referenced server descriptors. Nowadays, the directory authorities
- * derive microdescriptors from server descriptors and reference those
- * in network statuses, and clients only download microdescriptors instead
- * of server descriptors.</p>
- *
- * <p>Bridges publish server descriptors to the bridge directory
- * authority, also to announce themselves in the network. The bridge
- * directory authority compiles a list of available bridges
- * ({@link BridgeNetworkStatus}) for the bridge distribution service
- * BridgeDB. There are no microdescriptors for bridges, so that bridge
- * clients still rely on downloading bridge server descriptors directly
- * from the bridge they're connecting to.</p>
- *
- * <p>It's worth noting that all contents of server descriptors are
- * written and signed by relays and bridges without a third party
- * verifying their correctness. The (bridge) directory authorities may
- * decide to exclude dishonest servers from the network statuses they
- * produce, but that wouldn't be reflected in server descriptors.</p>
- *
- * @since 1.0.0
- */
-public interface ServerDescriptor extends Descriptor {
-
- /**
- * Return the SHA-1 descriptor digest, encoded as 40 lower-case (relay
- * descriptors) or upper-case (bridge descriptors) hexadecimal
- * characters, that is used to reference this descriptor from a network
- * status descriptor.
- *
- * @since 1.0.0
- */
- public String getServerDescriptorDigest();
-
- /**
- * Return the SHA-256 descriptor digest, encoded as 43 base64
- * characters without padding characters, that may be used to reference
- * this server descriptor from a network status descriptor.
- *
- * @since 1.1.0
- */
- public String getServerDescriptorDigestSha256();
-
- /**
- * Return the server's nickname consisting of 1 to 19 alphanumeric
- * characters.
- *
- * @since 1.0.0
- */
- public String getNickname();
-
- /**
- * Return the server's primary IPv4 address in dotted-quad format.
- *
- * @since 1.0.0
- */
- public String getAddress();
-
- /**
- * Return the TCP port where this server accepts TLS connections for
- * the main OR protocol, or 0 if the server does not accept such
- * connections.
- *
- * @since 1.0.0
- */
- public int getOrPort();
-
- /**
- * Return the TCP port where this server accepts SOCKS connections,
- * which is deprecated and should always be 0.
- *
- * @since 1.0.0
- */
- public int getSocksPort();
-
- /**
- * Return the TCP port where this server accepts directory-related HTTP
- * connections, or 0 if the server does not accept such connections.
- *
- * @since 1.0.0
- */
- public int getDirPort();
-
- /**
- * Return IP addresses and TCP ports where this server accepts TLS
- * connections for the main OR protocol, or an empty list if the server
- * does not support additional addresses or ports; entries are given in
- * the order as they are listed in the descriptor; IPv4 addresses are
- * given in dotted-quad format, IPv6 addresses use the colon-separated
- * hexadecimal format surrounded by square brackets, and TCP ports are
- * separated from the IP address using a colon.
- *
- * @since 1.0.0
- */
- public List<String> getOrAddresses();
-
- /**
- * Return the average bandwidth in bytes per second that the server is
- * willing to sustain over long periods.
- *
- * @since 1.0.0
- */
- public int getBandwidthRate();
-
- /**
- * Return the burst bandwidth in bytes per second that the server is
- * willing to sustain in very short intervals.
- *
- * @since 1.0.0
- */
- public int getBandwidthBurst();
-
- /**
- * Return the observed bandwidth in bytes per second as an estimate of
- * the capacity that the server can handle, or -1 if the descriptor
- * doesn't contain an observed bandwidth value (which is the case for
- * Tor 0.0.8 or older).
- *
- * @since 1.0.0
- */
- public int getBandwidthObserved();
-
- /**
- * Return a human-readable string describing the Tor software version
- * and the operating system of this server, which may contain non-ASCII
- * characters, typically written as {@code "Tor $version on $system"},
- * or null if this descriptor does not contain a platform line.
- *
- * @since 1.0.0
- */
- public String getPlatform();
-
- /**
- * Return the time in milliseconds since the epoch when this descriptor
- * and the corresponding extra-info descriptor were generated.
- *
- * @since 1.0.0
- */
- public long getPublishedMillis();
-
- /**
- * Return a SHA-1 digest of the server's public identity key, encoded
- * as 40 upper-case hexadecimal characters (without spaces after every 4
- * characters as opposed to the encoding in the descriptor), that is
- * typically used to uniquely identify the server, or null if this
- * descriptor does not contain a fingerprint line.
- *
- * @since 1.0.0
- */
- public String getFingerprint();
-
- /**
- * Return whether the server was hibernating when this descriptor was
- * published and should not be used to build circuits.
- *
- * @since 1.0.0
- */
- public boolean isHibernating();
-
- /**
- * Return the number of seconds that the server process has been
- * running (which might even be negative in a few descriptors due to a
- * bug that was fixed in Tor 0.1.2.7-alpha), or null if the descriptor
- * does not contain an uptime line.
- *
- * @since 1.0.0
- */
- public Long getUptime();
-
- /**
- * Return the RSA-1024 public key in PEM format used to encrypt CREATE
- * cells for this server, or null if the descriptor doesn't contain an
- * onion key (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getOnionKey();
-
- /**
- * Return the RSA-1024 public key in PEM format used by this server as
- * long-term identity key, or null if the descriptor doesn't contain a
- * signing key (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getSigningKey();
-
- /**
- * Return the server's exit policy consisting of one or more accept or
- * reject rules that the server follows when deciding whether to allow a
- * new stream to a given IP address and TCP port.
- *
- * @since 1.0.0
- */
- public List<String> getExitPolicyLines();
-
- /**
- * 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
- * a signature (which is the case in sanitized bridge descriptors).
- *
- * @since 1.0.0
- */
- public String getRouterSignature();
-
- /**
- * Return the contact information for this server, which may contain
- * non-ASCII characters, or null if no contact information is included
- * in the descriptor.
- *
- * @since 1.0.0
- */
- public String getContact();
-
- /**
- * Return nicknames, $-prefixed identity fingerprints, or tuples of the
- * format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
- * of servers contained in this server's family, or null if the
- * descriptor does not contain a family line.
- *
- * @since 1.0.0
- */
- public List<String> getFamilyEntries();
-
- /**
- * Return the server's history of read bytes, or null if the descriptor
- * does not contain a bandwidth history; current Tor versions include
- * bandwidth histories in their extra-info descriptors
- * ({@link ExtraInfoDescriptor#getReadHistory()}), not in their server
- * descriptors.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getReadHistory();
-
- /**
- * Return the server's history of written bytes, or null if the
- * descriptor does not contain a bandwidth history; current Tor versions
- * include bandwidth histories in their extra-info descriptors
- * ({@link ExtraInfoDescriptor#getWriteHistory()}), not in their server
- * descriptors.
- *
- * @since 1.0.0
- */
- public BandwidthHistory getWriteHistory();
-
- /**
- * Return true if the server uses the enhanced DNS logic, or false if
- * doesn't use it or doesn't include an eventdns line in its
- * descriptor; current Tor versions should be presumed to have the evdns
- * backend.
- *
- * @since 1.0.0
- */
- public boolean getUsesEnhancedDnsLogic();
-
- /**
- * Return whether this server is a directory cache that provides
- * extra-info descriptors.
- *
- * @since 1.0.0
- */
- public boolean getCachesExtraInfo();
-
- /**
- * Return the SHA-1 digest of the server's extra-info descriptor,
- * encoded as 40 upper-case hexadecimal characters, or null if the
- * server did not upload a corresponding extra-info descriptor.
- *
- * @since 1.0.0
- */
- public String getExtraInfoDigest();
-
- /**
- * Return the SHA-256 digest of the server's extra-info descriptor,
- * encoded as 43 base64 characters without padding characters, or null
- * if the server either did not upload a corresponding extra-info
- * descriptor or did not refer to it using a SHA-256 digest.
- *
- * @since 1.1.0
- */
- public String getExtraInfoDigestSha256();
-
- /**
- * Return the list of hidden service descriptor version numbers that
- * this server stores and serves, or null if it doesn't store and serve
- * any hidden service descriptors.
- *
- * @since 1.0.0
- */
- public List<Integer> getHiddenServiceDirVersions();
-
- /**
- * Return the list of link protocol versions that this server
- * supports.
- *
- * @since 1.0.0
- */
- public List<Integer> getLinkProtocolVersions();
-
- /**
- * Return the list of circuit protocol versions that this server
- * supports.
- *
- * @since 1.0.0
- */
- public List<Integer> getCircuitProtocolVersions();
-
- /**
- * Return whether this server allows single-hop circuits to make exit
- * connections.
- *
- * @since 1.0.0
- */
- public boolean getAllowSingleHopExits();
-
- /**
- * Return the default policy, {@code "accept"} or {@code "reject"}, of
- * the IPv6 port summary, or null if the descriptor didn't contain an
- * IPv6 exit-policy summary line which is equivalent to rejecting all
- * streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6DefaultPolicy();
-
- /**
- * Return the port list of the IPv6 exit-policy summary, or null if the
- * descriptor didn't contain an IPv6 exit-policy summary line which is
- * equivalent to rejecting all streams to IPv6 targets.
- *
- * @since 1.0.0
- */
- public String getIpv6PortList();
-
- /**
- * Return the curve25519 public key, encoded as 43 base64 characters
- * without padding characters, that is used for the ntor circuit
- * extended handshake, or null if the descriptor didn't contain an
- * ntor-onion-key line. */
- public String getNtorOnionKey();
-
- /**
- * Return the Ed25519 certificate in PEM format, or null if the
- * descriptor doesn't contain one.
- *
- * @since 1.1.0
- */
- public String getIdentityEd25519();
-
- /**
- * Return the Ed25519 master key, encoded as 43 base64 characters
- * without padding characters, which was either parsed from the optional
- * {@code "master-key-ed25519"} line or derived from the (likewise
- * optional) Ed25519 certificate following the
- * {@code "identity-ed25519"} line, or null if the descriptor contains
- * neither Ed25519 master key nor Ed25519 certificate.
- *
- * @since 1.1.0
- */
- public String getMasterKeyEd25519();
-
- /**
- * Return the Ed25519 signature of the SHA-256 digest of the entire
- * descriptor, encoded as 86 base64 characters without padding
- * characters, from the first character up to and including the first
- * space after the {@code "router-sig-ed25519"} string, prefixed with
- * the string {@code "Tor router descriptor signature v1"}.
- *
- * @since 1.1.0
- */
- public String getRouterSignatureEd25519();
-
- /**
- * Return an RSA-1024 signature in PEM format, generated using the
- * server's onion key, that proves that the party creating the
- * descriptor had control over the private key corresponding to the
- * onion key, or null if the descriptor does not contain such a
- * signature.
- *
- * @since 1.1.0
- */
- public String getOnionKeyCrosscert();
-
- /**
- * Return an Ed25519 signature in PEM format, generated using the
- * server's ntor onion key, that proves that the party creating the
- * descriptor had control over the private key corresponding to the ntor
- * onion key, or null if the descriptor does not contain such a
- * signature.
- *
- * @since 1.1.0
- */
- public String getNtorOnionKeyCrosscert();
-
- /**
- * Return the sign of the Ed25519 public key corresponding to the ntor
- * onion key as 0 or 1, or -1 if the descriptor does not contain this
- * information.
- *
- * @since 1.1.0
- */
- public int getNtorOnionKeyCrosscertSign();
-
- /**
- * Return whether the server accepts "tunneled" directory requests using
- * a BEGIN_DIR cell over the server's OR port.
- *
- * @since 1.3.0
- */
- public boolean getTunnelledDirServer();
-}
-
diff --git a/src/org/torproject/descriptor/TorperfResult.java b/src/org/torproject/descriptor/TorperfResult.java
deleted file mode 100644
index 188200b..0000000
--- a/src/org/torproject/descriptor/TorperfResult.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.descriptor;
-
-import java.util.List;
-import java.util.SortedMap;
-
-/**
- * Contains performance measurement results from making simple HTTP
- * requests over the Tor network.
- *
- * <p>The performance measurement service Torperf publishes performance
- * data from making simple HTTP requests over the Tor network. Torperf
- * uses a trivial SOCKS client to download files of various sizes over the
- * Tor network and notes how long substeps take.</p>
- *
- * @since 1.0.0
- */
-public interface TorperfResult extends Descriptor {
-
- /**
- * Return all unrecognized keys together with their values, or null if
- * all keys were recognized.
- *
- * @since 1.2.0
- */
- public SortedMap<String, String> getUnrecognizedKeys();
-
- /**
- * Return the configured name of the data source.
- *
- * @since 1.0.0
- */
- public String getSource();
-
- /**
- * Return the configured file size in bytes.
- *
- * @since 1.0.0
- */
- public int getFileSize();
-
- /**
- * Return the time in milliseconds since the epoch when the connection
- * process started.
- *
- * @since 1.0.0
- */
- public long getStartMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the socket was
- * created.
- *
- * @since 1.0.0
- */
- public long getSocketMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the socket was
- * connected.
- *
- * @since 1.0.0
- */
- public long getConnectMillis();
-
- /**
- * Return the time in milliseconds since the epoch when SOCKS 5
- * authentication methods have been negotiated.
- *
- * @since 1.0.0
- */
- public long getNegotiateMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the SOCKS
- * request was sent.
- *
- * @since 1.0.0
- */
- public long getRequestMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the SOCKS
- * response was received.
- *
- * @since 1.0.0
- */
- public long getResponseMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the HTTP
- * request was written.
- *
- * @since 1.0.0
- */
- public long getDataRequestMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the first
- * response was received.
- *
- * @since 1.0.0
- */
- public long getDataResponseMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the payload was
- * complete.
- *
- * @since 1.0.0
- */
- public long getDataCompleteMillis();
-
- /**
- * Return the total number of bytes written.
- *
- * @since 1.0.0
- */
- public int getWriteBytes();
-
- /**
- * Return the total number of bytes read.
- *
- * @since 1.0.0
- */
- public int getReadBytes();
-
- /**
- * Return whether the request timed out (as opposed to failing), or
- * null if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public Boolean didTimeout();
-
- /**
- * Return the times in milliseconds since the epoch when {@code x%} of
- * expected bytes were read for {@code 0 <= x <= 100}, or null if the
- * torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public SortedMap<Integer, Long> getDataPercentiles();
-
- /**
- * Return the time in milliseconds since the epoch when the circuit was
- * launched, or -1 if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public long getLaunchMillis();
-
- /**
- * Return the time in milliseconds since the epoch when the circuit was
- * used, or -1 if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public long getUsedAtMillis();
-
- /**
- * Return a list of fingerprints of the relays in the circuit, or null
- * if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public List<String> getPath();
-
- /**
- * Return a list of times in milliseconds since the epoch when circuit
- * hops were built, or null if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public List<Long> getBuildTimes();
-
- /**
- * Return the circuit build timeout that the Tor client used when
- * building this circuit, or -1 if the torperf line didn't contain that
- * information.
- *
- * @since 1.0.0
- */
- public long getTimeout();
-
- /**
- * Return the circuit build time quantile that the Tor client uses to
- * determine its circuit-build timeout, or -1 if the torperf line
- * didn't contain that information.
- *
- * @since 1.0.0
- */
- public double getQuantile();
-
- /**
- * Return the identifier of the circuit used for this measurement, or
- * -1 if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public int getCircId();
-
- /**
- * Return the identifier of the stream used for this measurement, or -1
- * if the torperf line didn't contain that information.
- *
- * @since 1.0.0
- */
- public int getUsedBy();
-}
-
diff --git a/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
deleted file mode 100644
index 295e0a4..0000000
--- a/src/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.BandwidthHistory;
-
-public class BandwidthHistoryImpl implements BandwidthHistory {
-
- protected BandwidthHistoryImpl(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- boolean isValid = false;
- this.line = line;
- if (partsNoOpt.length == 5 || partsNoOpt.length == 6) {
- try {
- this.historyEndMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- if (partsNoOpt[3].startsWith("(") &&
- partsNoOpt[4].startsWith("s)")) {
- this.intervalLength = Long.parseLong(partsNoOpt[3].
- substring(1));
- if (this.intervalLength <= 0L) {
- throw new DescriptorParseException("Only positive interval "
- + "lengths are allowed in line '" + line + "'.");
- }
- String[] values = null;
- if (partsNoOpt.length == 5 &&
- partsNoOpt[4].equals("s)")) {
- /* There are no bandwidth values to parse. */
- isValid = true;
- } else if (partsNoOpt.length == 6) {
- /* There are bandwidth values to parse. */
- values = partsNoOpt[5].split(",", -1);
- } else if (partsNoOpt[4].length() > 2) {
- /* There are bandwidth values to parse, but there is no space
- * between "s)" and "0,0,0,0". Very old Tor versions around
- * Tor 0.0.8 wrote such history lines, and even though
- * dir-spec.txt implies a space here, the old format isn't
- * totally broken. Let's pretend there's a space. */
- values = partsNoOpt[4].substring(2).split(",", -1);
- }
- if (values != null) {
- this.bandwidthValues = new long[values.length];
- for (int i = values.length - 1; i >= 0; i--) {
- long bandwidthValue = Long.parseLong(values[i]);
- if (bandwidthValue < 0L) {
- throw new DescriptorParseException("Negative bandwidth "
- + "values are not allowed in line '" + line + "'.");
- }
- this.bandwidthValues[i] = bandwidthValue;
- }
- isValid = true;
- }
- }
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Invalid bandwidth-history line "
- + "'" + line + "'.");
- }
- }
-
- private String line;
- @Override
- public String getLine() {
- return this.line;
- }
-
- private long historyEndMillis;
- @Override
- public long getHistoryEndMillis() {
- return this.historyEndMillis;
- }
-
- private long intervalLength;
- @Override
- public long getIntervalLength() {
- return this.intervalLength;
- }
-
- private long[] bandwidthValues;
- @Override
- public SortedMap<Long, Long> getBandwidthValues() {
- SortedMap<Long, Long> result = new TreeMap<>();
- if (this.bandwidthValues != null) {
- long endMillis = this.historyEndMillis;
- for (int i = this.bandwidthValues.length - 1; i >= 0; i--) {
- result.put(endMillis, bandwidthValues[i]);
- endMillis -= this.intervalLength * 1000L;
- }
- }
- return result;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java
deleted file mode 100644
index 66426d8..0000000
--- a/src/org/torproject/descriptor/impl/BlockingIteratorImpl.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-
-/* Provide an iterator for a queue of objects and block when there are
- * currently no objects in the queue. Allow the producer to signal that
- * there won't be further objects and unblock any waiting consumers. */
-public class BlockingIteratorImpl<T> implements Iterator<T> {
-
- /* Queue containing produced elemnts waiting for consumers. */
- private Queue<T> queue = new LinkedList<>();
-
- /* Maximum number of elements in queue. */
- private int maxQueueSize = 100;
-
- /* Restrict object construction to the impl package. */
- protected BlockingIteratorImpl() {
- }
-
- /* Create instance with maximum queue size. */
- protected BlockingIteratorImpl(int maxQueueSize) {
- this.maxQueueSize = maxQueueSize;
- }
-
- /* Add an object to the queue if there's still room. */
- protected synchronized void add(T object) {
- if (this.outOfDescriptors) {
- throw new IllegalStateException("Internal error: Adding results to "
- + "descriptor queue not allowed after sending end-of-stream "
- + "object.");
- }
- while (this.queue.size() >= this.maxQueueSize) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- this.queue.offer(object);
- notifyAll();
- }
-
- /* Signalize that there won't be any further objects to be enqueued. */
- private boolean outOfDescriptors = false;
- protected synchronized void setOutOfDescriptors() {
- if (this.outOfDescriptors) {
- throw new IllegalStateException("Internal error: Sending "
- + "end-of-stream object only permitted once.");
- }
- this.outOfDescriptors = true;
- notifyAll();
- }
-
- /* Return whether there are more objects. Block if there are currently
- * no objects, but the producer hasn't signalized that there won't be
- * further objects. */
- @Override
- public synchronized boolean hasNext() {
- while (!this.outOfDescriptors && this.queue.isEmpty()) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- return this.queue.peek() != null;
- }
-
- /* Return the next object in the queue or throw an exception when there
- * are no further objects. Block if there are currently no objects, but
- * the producer hasn't signalized that there won't be further
- * objects. */
- @Override
- public synchronized T next() {
- while (!this.outOfDescriptors && this.queue.isEmpty()) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- if (this.queue.peek() == null) {
- throw new NoSuchElementException();
- }
- notifyAll();
- return this.queue.remove();
- }
-
- /* Don't support explicitly removing objects. They are removed
- * anyway. */
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
deleted file mode 100644
index 15d40d8..0000000
--- a/src/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.BridgeExtraInfoDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-
-public class BridgeExtraInfoDescriptorImpl
- extends ExtraInfoDescriptorImpl implements BridgeExtraInfoDescriptor {
-
- protected static List<ExtraInfoDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "extra-info ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ExtraInfoDescriptor parsedDescriptor =
- new BridgeExtraInfoDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgeExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
deleted file mode 100644
index bf3804d..0000000
--- a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedMap;
-import java.util.TimeZone;
-
-import org.torproject.descriptor.BridgeNetworkStatus;
-
-/* Contains a bridge network status. */
-public class BridgeNetworkStatusImpl extends NetworkStatusImpl
- implements BridgeNetworkStatus {
-
- protected BridgeNetworkStatusImpl(byte[] statusBytes,
- String fileName, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(statusBytes, failUnrecognizedDescriptorLines, false, false);
- this.setPublishedMillisFromFileName(fileName);
- }
-
- private void setPublishedMillisFromFileName(String fileName)
- throws DescriptorParseException {
- if (this.publishedMillis != 0L) {
- /* We already learned the publication timestamp from parsing the
- * "published" line. */
- return;
- }
- if (fileName.length() ==
- "20000101-000000-4A0CCD2DDC7995083D73F5D667100C8A5831F16D".
- length()) {
- String publishedString = fileName.substring(0,
- "yyyyMMdd-HHmmss".length());
- try {
- SimpleDateFormat fileNameFormat = new SimpleDateFormat(
- "yyyyMMdd-HHmmss");
- fileNameFormat.setLenient(false);
- fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- this.publishedMillis = fileNameFormat.parse(publishedString).
- getTime();
- } catch (ParseException e) {
- }
- }
- if (this.publishedMillis == 0L) {
- throw new DescriptorParseException("Unrecognized bridge network "
- + "status file name '" + fileName + "'.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- /* Initialize flag-thresholds values here for the case that the status
- * doesn't contain those values. Initializing them in the constructor
- * or when declaring variables wouldn't work, because those parts are
- * evaluated later and would overwrite everything we parse here. */
- this.stableUptime = -1L;
- this.stableMtbf = -1L;
- this.fastBandwidth = -1L;
- this.guardWfu = -1.0;
- this.guardTk = -1L;
- this.guardBandwidthIncludingExits = -1L;
- this.guardBandwidthExcludingExits = -1L;
- this.enoughMtbfInfo = -1;
- this.ignoringAdvertisedBws = -1;
-
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in bridge network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFlagThresholdsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No flag thresholds in line '"
- + line + "'.");
- }
- SortedMap<String, String> flagThresholds =
- ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
- try {
- for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
- switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
- }
- }
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
-
- protected void parseDirSource(byte[] dirSourceBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory source expected in "
- + "bridge network status.");
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory footer expected in "
- + "bridge network status.");
- }
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory signature expected "
- + "in bridge network status.");
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long stableUptime;
- @Override
- public long getStableUptime() {
- return this.stableUptime;
- }
-
- private long stableMtbf;
- @Override
- public long getStableMtbf() {
- return this.stableMtbf;
- }
-
- private long fastBandwidth;
- @Override
- public long getFastBandwidth() {
- return this.fastBandwidth;
- }
-
- private double guardWfu;
- @Override
- public double getGuardWfu() {
- return this.guardWfu;
- }
-
- private long guardTk;
- @Override
- public long getGuardTk() {
- return this.guardTk;
- }
-
- private long guardBandwidthIncludingExits;
- @Override
- public long getGuardBandwidthIncludingExits() {
- return this.guardBandwidthIncludingExits;
- }
-
- private long guardBandwidthExcludingExits;
- @Override
- public long getGuardBandwidthExcludingExits() {
- return this.guardBandwidthExcludingExits;
- }
-
- private int enoughMtbfInfo;
- @Override
- public int getEnoughMtbfInfo() {
- return this.enoughMtbfInfo;
- }
-
- private int ignoringAdvertisedBws;
- @Override
- public int getIgnoringAdvertisedBws() {
- return this.ignoringAdvertisedBws;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
deleted file mode 100644
index 99578e8..0000000
--- a/src/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.BridgePoolAssignment;
-
-/* TODO Write a test class. */
-public class BridgePoolAssignmentImpl extends DescriptorImpl
- implements BridgePoolAssignment {
-
- protected static List<BridgePoolAssignment> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<BridgePoolAssignment> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "bridge-pool-assignment ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- BridgePoolAssignment parsedDescriptor =
- new BridgePoolAssignmentImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgePoolAssignmentImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- 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();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("bridge-pool-assignment ")) {
- this.parseBridgePoolAssignmentLine(line);
- } else {
- this.parseBridgeLine(line);
- }
- }
- }
-
- private void parseBridgePoolAssignmentLine(String line)
- throws DescriptorParseException {
- String[] parts = line.split("[ \t]+");
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in bridge pool assignment.");
- }
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseBridgeLine(String line)
- throws DescriptorParseException {
- String[] parts = line.split("[ \t]+");
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in bridge pool assignment.");
- }
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[0]);
- String poolAndDetails = line.substring(line.indexOf(" ") + 1);
- this.entries.put(fingerprint, poolAndDetails);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private SortedMap<String, String> entries = new TreeMap<>();
- @Override
- public SortedMap<String, String> getEntries() {
- return new TreeMap<>(this.entries);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
deleted file mode 100644
index eb2b933..0000000
--- a/src/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.BridgeServerDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ServerDescriptor;
-
-public class BridgeServerDescriptorImpl extends ServerDescriptorImpl
- implements BridgeServerDescriptor {
-
- protected static List<ServerDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "router ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ServerDescriptor parsedDescriptor =
- new BridgeServerDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected BridgeServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
deleted file mode 100644
index 1a030ef..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.Stack;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.GZIPInputStream;
-
-import org.torproject.descriptor.DescriptorCollector;
-
-public class DescriptorCollectorImpl implements DescriptorCollector {
-
- @Override
- public void collectDescriptors(String collecTorBaseUrl,
- String[] remoteDirectories, long minLastModified,
- File localDirectory, boolean deleteExtraneousLocalFiles) {
- collecTorBaseUrl = collecTorBaseUrl.endsWith("/")
- ? collecTorBaseUrl.substring(0, collecTorBaseUrl.length() - 1)
- : collecTorBaseUrl;
- if (minLastModified < 0) {
- throw new IllegalArgumentException("A negative minimum "
- + "last-modified time is not permitted.");
- }
- if (localDirectory.exists() && !localDirectory.isDirectory()) {
- throw new IllegalArgumentException("Local directory already exists "
- + "and is not a directory.");
- }
- SortedMap<String, Long> localFiles =
- this.statLocalDirectory(localDirectory);
- SortedMap<String, String> fetchedDirectoryListings =
- this.fetchRemoteDirectories(collecTorBaseUrl, remoteDirectories);
- SortedSet<String> parsedDirectories = new TreeSet<>();
- SortedMap<String, Long> remoteFiles = new TreeMap<>();
- for (Map.Entry<String, String> e :
- fetchedDirectoryListings.entrySet()) {
- String remoteDirectory = e.getKey();
- String directoryListing = e.getValue();
- SortedMap<String, Long> parsedRemoteFiles =
- this.parseDirectoryListing(remoteDirectory, directoryListing);
- if (parsedRemoteFiles == null) {
- continue;
- }
- parsedDirectories.add(remoteDirectory);
- remoteFiles.putAll(parsedRemoteFiles);
- }
- this.fetchRemoteFiles(collecTorBaseUrl, remoteFiles, minLastModified,
- localDirectory, localFiles);
- if (deleteExtraneousLocalFiles) {
- this.deleteExtraneousLocalFiles(parsedDirectories, remoteFiles,
- localDirectory, localFiles);
- }
- }
-
- SortedMap<String, Long> statLocalDirectory(
- File localDirectory) {
- SortedMap<String, Long> localFiles = new TreeMap<>();
- if (!localDirectory.exists()) {
- return localFiles;
- }
- Stack<File> files = new Stack<>();
- files.add(localDirectory);
- while (!files.isEmpty()) {
- File file = files.pop();
- if (file.isDirectory()) {
- files.addAll(Arrays.asList(file.listFiles()));
- } else {
- String localPath = file.getPath().substring(
- localDirectory.getPath().length());
- localFiles.put(localPath, file.lastModified());
- }
- }
- return localFiles;
- }
-
- SortedMap<String, String> fetchRemoteDirectories(
- String collecTorBaseUrl, String[] remoteDirectories) {
- SortedMap<String, String> fetchedDirectoryListings = new TreeMap<>();
- for (String remoteDirectory : remoteDirectories) {
- String remoteDirectoryWithSlashAtBeginAndEnd =
- (remoteDirectory.startsWith("/") ? "" : "/") + remoteDirectory
- + (remoteDirectory.endsWith("/") ? "" : "/");
- String directoryUrl = collecTorBaseUrl
- + remoteDirectoryWithSlashAtBeginAndEnd;
- String directoryListing = this.fetchRemoteDirectory(directoryUrl);
- if (directoryListing.length() > 0) {
- fetchedDirectoryListings.put(
- remoteDirectoryWithSlashAtBeginAndEnd, directoryListing);
- }
- }
- return fetchedDirectoryListings;
- }
-
- String fetchRemoteDirectory(String url) {
- StringBuilder sb = new StringBuilder();
- HttpURLConnection huc = null;
- try {
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setRequestMethod("GET");
- huc.connect();
- int responseCode = huc.getResponseCode();
- if (responseCode == 200) {
- BufferedReader br = new BufferedReader(new InputStreamReader(
- huc.getInputStream()));
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line).append("\n");
- }
- br.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- if (huc != null) {
- huc.disconnect();
- }
- return "";
- }
- return sb.toString();
- }
-
- final Pattern DIRECTORY_LISTING_LINE_PATTERN =
- Pattern.compile(".* href=\"([^\"/]+)\"" /* filename */
- + ".*>(\\d{2}-\\w{3}-\\d{4} \\d{2}:\\d{2})\\s*<.*"); /* dateTime */
-
- SortedMap<String, Long> parseDirectoryListing(
- String remoteDirectory, String directoryListing) {
- SortedMap<String, Long> remoteFiles = new TreeMap<>();
- DateFormat dateTimeFormat = ParseHelper.getDateFormat(
- "dd-MMM-yyyy HH:mm");
- try {
- Scanner s = new Scanner(directoryListing);
- s.useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- Matcher matcher = DIRECTORY_LISTING_LINE_PATTERN.matcher(line);
- if (matcher.matches()) {
- String filename = matcher.group(1);
- long lastModifiedMillis = dateTimeFormat.parse(
- matcher.group(2)).getTime();
- remoteFiles.put(remoteDirectory + filename, lastModifiedMillis);
- }
- }
- s.close();
- } catch (ParseException e) {
- e.printStackTrace();
- return null;
- }
- return remoteFiles;
- }
-
- void fetchRemoteFiles(String collecTorBaseUrl,
- SortedMap<String, Long> remoteFiles, long minLastModified,
- File localDirectory, SortedMap<String, Long> localFiles) {
- for (Map.Entry<String, Long> e : remoteFiles.entrySet()) {
- String filename = e.getKey();
- long lastModifiedMillis = e.getValue();
- if (lastModifiedMillis < minLastModified ||
- (localFiles.containsKey(filename) &&
- localFiles.get(filename) >= lastModifiedMillis)) {
- continue;
- }
- String url = collecTorBaseUrl + filename;
- File destinationFile = new File(localDirectory.getPath()
- + filename);
- this.fetchRemoteFile(url, destinationFile, lastModifiedMillis);
- }
- }
-
- void fetchRemoteFile(String url, File destinationFile,
- long lastModifiedMillis) {
- HttpURLConnection huc = null;
- try {
- File destinationDirectory = destinationFile.getParentFile();
- destinationDirectory.mkdirs();
- File tempDestinationFile = new File(destinationDirectory, "."
- + destinationFile.getName());
- BufferedOutputStream bos = new BufferedOutputStream(
- new FileOutputStream(tempDestinationFile));
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setRequestMethod("GET");
- if (!url.endsWith(".xz")) {
- huc.addRequestProperty("Accept-Encoding", "gzip");
- }
- huc.connect();
- int responseCode = huc.getResponseCode();
- if (responseCode == 200) {
- InputStream is;
- if (huc.getContentEncoding() != null &&
- huc.getContentEncoding().equalsIgnoreCase("gzip")) {
- is = new GZIPInputStream(huc.getInputStream());
- } else {
- is = huc.getInputStream();
- }
- BufferedInputStream bis = new BufferedInputStream(is);
- int len;
- byte[] data = new byte[8192];
- while ((len = bis.read(data, 0, 8192)) >= 0) {
- bos.write(data, 0, len);
- }
- bis.close();
- bos.close();
- tempDestinationFile.renameTo(destinationFile);
- destinationFile.setLastModified(lastModifiedMillis);
- }
- } catch (IOException e) {
- e.printStackTrace();
- if (huc != null) {
- huc.disconnect();
- }
- }
- }
-
- void deleteExtraneousLocalFiles(
- SortedSet<String> parsedDirectories,
- SortedMap<String, Long> remoteFiles, File localDirectory,
- SortedMap<String, Long> localFiles) {
- for (String localPath : localFiles.keySet()) {
- for (String remoteDirectory : parsedDirectories) {
- if (localPath.startsWith(remoteDirectory)) {
- if (!remoteFiles.containsKey(localPath)) {
- new File(localDirectory.getPath() + localPath).delete();
- }
- }
- }
- }
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
deleted file mode 100644
index e726ce9..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DescriptorDownloader;
-
-public class DescriptorDownloaderImpl
- implements DescriptorDownloader {
-
- private boolean hasStartedDownloading = false;
-
- private SortedMap<String, DirectoryDownloader> directoryAuthorities =
- new TreeMap<>();
- @Override
- public void addDirectoryAuthority(String nickname, String ip,
- int dirPort) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkDirectoryParameters(nickname, ip, dirPort);
- DirectoryDownloader directoryAuthority = new DirectoryDownloader(
- nickname, ip, dirPort);
- this.directoryAuthorities.put(nickname, directoryAuthority);
- }
-
- private SortedMap<String, DirectoryDownloader> directoryMirrors =
- new TreeMap<>();
- @Override
- public void addDirectoryMirror(String nickname, String ip,
- int dirPort) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkDirectoryParameters(nickname, ip, dirPort);
- DirectoryDownloader directoryMirror = new DirectoryDownloader(
- nickname, ip, dirPort);
- this.directoryMirrors.put(nickname, directoryMirror);
- /* TODO Implement prioritizing mirrors for non-vote downloads. */
- throw new UnsupportedOperationException("Prioritizing directory "
- + "mirrors over directory authorities is not implemented yet. "
- + "Until it is, configuring directory mirrors is misleading and "
- + "therefore not supported.");
- }
-
- private void checkDirectoryParameters(String nickname, String ip,
- int dirPort) {
- if (nickname == null || nickname.length() < 1) {
- throw new IllegalArgumentException("'" + nickname + "' is not a "
- + "valid nickname.");
- }
- if (ip == null || ip.length() < 7 || ip.split("\\.").length != 4) {
- throw new IllegalArgumentException("'" + ip + "' is not a valid IP "
- + "address.");
- }
- if (dirPort < 1 || dirPort > 65535) {
- throw new IllegalArgumentException(String.valueOf(dirPort) + " is "
- + "not a valid DirPort.");
- }
- /* TODO Relax the requirement for directory nicknames to be unique.
- * In theory, we can identify them by ip+port. */
- if (this.directoryAuthorities.containsKey(nickname) ||
- this.directoryMirrors.containsKey(nickname)) {
- throw new IllegalArgumentException("Directory nicknames must be "
- + "unique.");
- }
- }
-
- private boolean downloadConsensus = false;
- @Override
- public void setIncludeCurrentConsensus() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.downloadConsensus = true;
- }
-
- private boolean downloadConsensusFromAllAuthorities = false;
- @Override
- public void setIncludeCurrentConsensusFromAllDirectoryAuthorities() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.downloadConsensusFromAllAuthorities = true;
- }
-
- private boolean includeCurrentReferencedVotes = false;
- @Override
- public void setIncludeCurrentReferencedVotes() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.includeCurrentReferencedVotes = true;
- }
-
- private Set<String> downloadVotes = new HashSet<>();
- @Override
- public void setIncludeCurrentVote(String fingerprint) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.checkVoteFingerprint(fingerprint);
- this.downloadVotes.add(fingerprint);
- }
-
- @Override
- public void setIncludeCurrentVotes(Set<String> fingerprints) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (fingerprints == null) {
- throw new IllegalArgumentException("Set of fingerprints must not "
- + "be null.");
- }
- for (String fingerprint : fingerprints) {
- this.checkVoteFingerprint(fingerprint);
- }
- for (String fingerprint : fingerprints) {
- this.setIncludeCurrentVote(fingerprint);
- }
- }
-
- private void checkVoteFingerprint(String fingerprint) {
- if (fingerprint == null || fingerprint.length() != 40) {
- throw new IllegalArgumentException("'" + fingerprint + "' is not a "
- + "valid fingerprint.");
- }
- }
-
- @Override
- public void setIncludeReferencedServerDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeServerDescriptor(String identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeServerDescriptors(Set<String> identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading server "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setIncludeReferencedExtraInfoDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeExtraInfoDescriptor(String identifier) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- @Override
- public void setExcludeExtraInfoDescriptors(Set<String> identifiers) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- /* TODO Implement me. */
- throw new UnsupportedOperationException("Downloading extra-info "
- + "descriptors is not implemented yet.");
- }
-
- private long readTimeoutMillis = 60L * 1000L;
- @Override
- public void setReadTimeout(long readTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (readTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Read timeout value "
- + String.valueOf(readTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.readTimeoutMillis = readTimeoutMillis;
- }
-
- private long connectTimeoutMillis = 60L * 1000L;
- @Override
- public void setConnectTimeout(long connectTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (connectTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Connect timeout value "
- + String.valueOf(connectTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.connectTimeoutMillis = connectTimeoutMillis;
- }
-
- private long globalTimeoutMillis = 60L * 60L * 1000L;
- @Override
- public void setGlobalTimeout(long globalTimeoutMillis) {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- if (globalTimeoutMillis < 0L) {
- throw new IllegalArgumentException("Global timeout value "
- + String.valueOf(globalTimeoutMillis) + " may not be "
- + "negative.");
- }
- this.globalTimeoutMillis = globalTimeoutMillis;
- }
-
- private boolean failUnrecognizedDescriptorLines = false;
- @Override
- public void setFailUnrecognizedDescriptorLines() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to download.");
- }
- this.failUnrecognizedDescriptorLines = true;
- }
-
- @Override
- public Iterator<DescriptorRequest> downloadDescriptors() {
- if (this.hasStartedDownloading) {
- throw new IllegalStateException("Initiating downloads is only "
- + "permitted once.");
- }
- this.hasStartedDownloading = true;
- DownloadCoordinatorImpl downloadCoordinator =
- new DownloadCoordinatorImpl(this.directoryAuthorities,
- this.directoryMirrors, this.downloadConsensus,
- this.downloadConsensusFromAllAuthorities, this.downloadVotes,
- this.includeCurrentReferencedVotes, this.connectTimeoutMillis,
- this.readTimeoutMillis, this.globalTimeoutMillis,
- this.failUnrecognizedDescriptorLines);
- Iterator<DescriptorRequest> descriptorQueue = downloadCoordinator.
- getDescriptorQueue();
- return descriptorQueue;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/org/torproject/descriptor/impl/DescriptorFileImpl.java
deleted file mode 100644
index 801c546..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorFileImpl.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-
-public class DescriptorFileImpl implements DescriptorFile {
-
- private File directory;
- protected void setDirectory(File directory) {
- this.directory = directory;
- }
- @Override
- public File getDirectory() {
- return this.directory;
- }
-
- private File tarball;
- protected void setTarball(File tarball) {
- this.tarball = tarball;
- }
- @Override
- public File getTarball() {
- return this.tarball;
- }
-
- private File file;
- protected void setFile(File file) {
- this.file = file;
- }
- @Override
- public File getFile() {
- return this.file;
- }
-
- private String fileName;
- protected void setFileName(String fileName) {
- this.fileName = fileName;
- }
- @Override
- public String getFileName() {
- return this.fileName;
- }
-
- private long lastModified;
- protected void setLastModified(long lastModified) {
- this.lastModified = lastModified;
- }
- @Override
- public long getLastModified() {
- return this.lastModified;
- }
-
- private List<Descriptor> descriptors;
- protected void setDescriptors(List<Descriptor> descriptors) {
- this.descriptors = descriptors;
- }
- @Override
- public List<Descriptor> getDescriptors() {
- return this.descriptors == null ? new ArrayList<Descriptor>() :
- new ArrayList<>(this.descriptors);
- }
-
- private Exception exception;
- protected void setException(Exception exception) {
- this.exception = exception;
- }
- @Override
- public Exception getException() {
- return this.exception;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
deleted file mode 100644
index 5625b3f..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-
-import org.torproject.descriptor.Descriptor;
-
-public abstract class DescriptorImpl implements Descriptor {
-
- protected static List<Descriptor> parseDescriptors(
- byte[] rawDescriptorBytes, String fileName,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<Descriptor> parsedDescriptors = new ArrayList<>();
- if (rawDescriptorBytes == null) {
- return parsedDescriptors;
- }
- byte[] first100Chars = new byte[Math.min(100,
- rawDescriptorBytes.length)];
- System.arraycopy(rawDescriptorBytes, 0, first100Chars, 0,
- 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"))) {
- 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"))) {
- parsedDescriptors.addAll(RelayNetworkStatusVoteImpl.
- parseVotes(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-network-status 1.")
- || firstLines.startsWith("r ")) {
- parsedDescriptors.add(new BridgeNetworkStatusImpl(
- rawDescriptorBytes, fileName, failUnrecognizedDescriptorLines));
- } 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 ")) {
- parsedDescriptors.addAll(RelayServerDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-extra-info 1.")) {
- parsedDescriptors.addAll(BridgeExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type extra-info 1.") ||
- firstLines.startsWith("extra-info ") ||
- firstLines.contains("\nextra-info ")) {
- parsedDescriptors.addAll(RelayExtraInfoDescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type microdescriptor 1.") ||
- firstLines.startsWith("onion-key\n") ||
- firstLines.contains("\nonion-key\n")) {
- parsedDescriptors.addAll(MicrodescriptorImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type bridge-pool-assignment 1.") ||
- firstLines.startsWith("bridge-pool-assignment ") ||
- firstLines.contains("\nbridge-pool-assignment ")) {
- 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 ")) {
- parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
- parseDescriptors(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type tordnsel 1.") ||
- firstLines.startsWith("ExitNode ") ||
- firstLines.contains("\nExitNode ")) {
- 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")) {
- parsedDescriptors.add(new RelayNetworkStatusImpl(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type directory 1.") ||
- firstLines.startsWith("signed-directory\n") ||
- firstLines.contains("\nsigned-directory\n")) {
- parsedDescriptors.add(new RelayDirectoryImpl(rawDescriptorBytes,
- failUnrecognizedDescriptorLines));
- } else if (firstLines.startsWith("@type torperf 1.")) {
- parsedDescriptors.addAll(TorperfResultImpl.parseTorperfResults(
- rawDescriptorBytes, failUnrecognizedDescriptorLines));
- } else {
- throw new DescriptorParseException("Could not detect descriptor "
- + "type in descriptor starting with '" + firstLines + "'.");
- }
- return parsedDescriptors;
- }
-
- protected static List<byte[]> splitRawDescriptorBytes(
- byte[] rawDescriptorBytes, String startToken) {
- List<byte[]> rawDescriptors = new ArrayList<>();
- String splitToken = "\n" + startToken;
- String ascii;
- try {
- ascii = new String(rawDescriptorBytes, "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- return rawDescriptors;
- }
- int endAllDescriptors = rawDescriptorBytes.length,
- startAnnotations = 0;
- boolean containsAnnotations = ascii.startsWith("@") ||
- ascii.contains("\n@");
- while (startAnnotations < endAllDescriptors) {
- int startDescriptor;
- if (ascii.indexOf(startToken, startAnnotations) == 0) {
- startDescriptor = startAnnotations;
- } else {
- startDescriptor = ascii.indexOf(splitToken, startAnnotations - 1);
- if (startDescriptor < 0) {
- break;
- } else {
- startDescriptor += 1;
- }
- }
- int endDescriptor = -1;
- if (containsAnnotations) {
- endDescriptor = ascii.indexOf("\n@", startDescriptor);
- }
- if (endDescriptor < 0) {
- endDescriptor = ascii.indexOf(splitToken, startDescriptor);
- }
- if (endDescriptor < 0) {
- endDescriptor = endAllDescriptors - 1;
- }
- endDescriptor += 1;
- byte[] rawDescriptor = new byte[endDescriptor - startAnnotations];
- System.arraycopy(rawDescriptorBytes, startAnnotations,
- rawDescriptor, 0, endDescriptor - startAnnotations);
- startAnnotations = endDescriptor;
- rawDescriptors.add(rawDescriptor);
- }
- return rawDescriptors;
- }
-
- protected byte[] rawDescriptorBytes;
- @Override
- public byte[] getRawDescriptorBytes() {
- return this.rawDescriptorBytes;
- }
-
- protected boolean failUnrecognizedDescriptorLines = false;
-
- protected List<String> unrecognizedLines;
- @Override
- public List<String> getUnrecognizedLines() {
- return this.unrecognizedLines == null ? new ArrayList<String>() :
- new ArrayList<>(this.unrecognizedLines);
- }
-
- protected DescriptorImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines, boolean blankLinesAllowed)
- throws DescriptorParseException {
- this.rawDescriptorBytes = rawDescriptorBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.cutOffAnnotations(rawDescriptorBytes);
- this.countKeywords(rawDescriptorBytes, blankLinesAllowed);
- }
-
- /* Parse annotation lines from the descriptor bytes. */
- private List<String> annotations = new ArrayList<>();
- private void cutOffAnnotations(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- 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);
- if (end < 0) {
- throw new DescriptorParseException("Annotation line does not "
- + "contain a newline.");
- }
- this.annotations.add(ascii.substring(start, end));
- start = end + 1;
- }
- if (start > 0) {
- int length = rawDescriptorBytes.length;
- byte[] rawDescriptor = new byte[length - start];
- System.arraycopy(rawDescriptorBytes, start, rawDescriptor, 0,
- length - start);
- this.rawDescriptorBytes = rawDescriptor;
- }
- }
- @Override
- public List<String> getAnnotations() {
- return new ArrayList<>(this.annotations);
- }
-
- /* Count parsed keywords for consistency checks by subclasses. */
- private String firstKeyword, lastKeyword;
- private Map<String, Integer> parsedKeywords = new HashMap<>();
- private void countKeywords(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"))) {
- throw new DescriptorParseException("Blank lines are not allowed.");
- }
- boolean skipCrypto = false;
- Scanner s = new Scanner(descriptorString).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("-----BEGIN")) {
- skipCrypto = true;
- } else if (line.startsWith("-----END")) {
- 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];
- if (keyword.equals("")) {
- throw new DescriptorParseException("Illegal keyword in line '"
- + line + "'.");
- }
- if (this.firstKeyword == null) {
- this.firstKeyword = keyword;
- }
- lastKeyword = keyword;
- if (parsedKeywords.containsKey(keyword)) {
- parsedKeywords.put(keyword, parsedKeywords.get(keyword) + 1);
- } else {
- parsedKeywords.put(keyword, 1);
- }
- }
- }
- }
-
- protected void checkFirstKeyword(String keyword)
- throws DescriptorParseException {
- if (this.firstKeyword == null ||
- !this.firstKeyword.equals(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' must "
- + "be contained in the first line.");
- }
- }
-
- protected void checkLastKeyword(String keyword)
- throws DescriptorParseException {
- if (this.lastKeyword == null ||
- !this.lastKeyword.equals(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' must "
- + "be contained in the last line.");
- }
- }
-
- protected void checkExactlyOnceKeywords(Set<String> keywords)
- throws DescriptorParseException {
- for (String keyword : keywords) {
- int contained = 0;
- if (this.parsedKeywords.containsKey(keyword)) {
- contained = this.parsedKeywords.get(keyword);
- }
- if (contained != 1) {
- throw new DescriptorParseException("Keyword '" + keyword + "' is "
- + "contained " + contained + " times, but must be contained "
- + "exactly once.");
- }
- }
- }
-
- protected void checkAtLeastOnceKeywords(Set<String> keywords)
- throws DescriptorParseException {
- for (String keyword : keywords) {
- if (!this.parsedKeywords.containsKey(keyword)) {
- throw new DescriptorParseException("Keyword '" + keyword + "' is "
- + "contained 0 times, but must be contained at least once.");
- }
- }
- }
-
- protected void checkAtMostOnceKeywords(Set<String> keywords)
- 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, "
- + "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 "
- + "not.");
- }
- }
- }
-
- protected int getKeywordCount(String keyword) {
- if (!this.parsedKeywords.containsKey(keyword)) {
- return 0;
- } else {
- return this.parsedKeywords.get(keyword);
- }
- }
-
- protected void clearParsedKeywords() {
- this.parsedKeywords = null;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorParseException.java b/src/org/torproject/descriptor/impl/DescriptorParseException.java
deleted file mode 100644
index 0f9add2..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorParseException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-/**
- * @deprecated Replaced by
- * org.torproject.descriptor.DescriptorParseException
- */
-@Deprecated public class DescriptorParseException extends Exception {
- private static final long serialVersionUID = 100L;
- protected DescriptorParseException(String message) {
- super(message);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/org/torproject/descriptor/impl/DescriptorParserImpl.java
deleted file mode 100644
index 6ac53f8..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorParserImpl.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorParser;
-
-public class DescriptorParserImpl implements DescriptorParser {
-
- private boolean failUnrecognizedDescriptorLines;
-
- @Override
- public void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines) {
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- }
-
- @Override
- public List<Descriptor> parseDescriptors(byte[] rawDescriptorBytes,
- String fileName) throws DescriptorParseException {
- return DescriptorImpl.parseDescriptors(rawDescriptorBytes, fileName,
- this.failUnrecognizedDescriptorLines);
- }
-}
diff --git a/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java
deleted file mode 100644
index 8da88e9..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorReaderImpl.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.Stack;
-import java.util.TreeMap;
-
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
-import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorReader;
-
-public class DescriptorReaderImpl implements DescriptorReader {
-
- private boolean hasStartedReading = false;
-
- private List<File> directories = new ArrayList<>();
- @Override
- public void addDirectory(File directory) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.directories.add(directory);
- }
-
- private List<File> tarballs = new ArrayList<>();
- @Override
- public void addTarball(File tarball) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.tarballs.add(tarball);
- }
-
- private File historyFile;
- @Override
- public void setExcludeFiles(File historyFile) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.historyFile = historyFile;
- }
-
- private SortedMap<String, Long> excludedFiles;
- @Override
- public void setExcludedFiles(SortedMap<String, Long> excludedFiles) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.excludedFiles = excludedFiles;
- }
-
- @Override
- public SortedMap<String, Long> getExcludedFiles() {
- if (this.reader == null || !this.reader.hasFinishedReading) {
- throw new IllegalStateException("Operation is not permitted before "
- + "finishing to read.");
- }
- return new TreeMap<>(this.reader.excludedFilesAfter);
- }
-
- @Override
- public SortedMap<String, Long> getParsedFiles() {
- if (this.reader == null || !this.reader.hasFinishedReading) {
- throw new IllegalStateException("Operation is not permitted before "
- + "finishing to read.");
- }
- return new TreeMap<>(this.reader.parsedFilesAfter);
- }
-
- private boolean failUnrecognizedDescriptorLines = false;
- @Override
- public void setFailUnrecognizedDescriptorLines() {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.failUnrecognizedDescriptorLines = true;
- }
-
- private Integer maxDescriptorFilesInQueue = null;
- @Override
- public void setMaxDescriptorFilesInQueue(int max) {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Reconfiguration is not permitted "
- + "after starting to read.");
- }
- this.maxDescriptorFilesInQueue = max;
- }
-
- private DescriptorReaderRunnable reader;
- @Override
- public Iterator<DescriptorFile> readDescriptors() {
- if (this.hasStartedReading) {
- throw new IllegalStateException("Initiating reading is only "
- + "permitted once.");
- }
- this.hasStartedReading = true;
- BlockingIteratorImpl<DescriptorFile> descriptorQueue =
- this.maxDescriptorFilesInQueue == null
- ? new BlockingIteratorImpl<DescriptorFile>()
- : new BlockingIteratorImpl<DescriptorFile>(
- this.maxDescriptorFilesInQueue);
- this.reader = new DescriptorReaderRunnable(this.directories,
- this.tarballs, descriptorQueue, this.historyFile,
- this.excludedFiles, this.failUnrecognizedDescriptorLines);
- new Thread(this.reader).start();
- return descriptorQueue;
- }
-
- private static class DescriptorReaderRunnable implements Runnable {
- private List<File> directories;
- private List<File> tarballs;
- private BlockingIteratorImpl<DescriptorFile> descriptorQueue;
- private File historyFile;
- private SortedMap<String, Long> excludedFilesBefore = new TreeMap<>(),
- excludedFilesAfter = new TreeMap<>(),
- parsedFilesAfter = new TreeMap<>();
- private DescriptorParser descriptorParser;
- private boolean hasFinishedReading = false;
- private DescriptorReaderRunnable(List<File> directories,
- List<File> tarballs,
- BlockingIteratorImpl<DescriptorFile> descriptorQueue,
- File historyFile, SortedMap<String, Long> excludedFiles,
- boolean failUnrecognizedDescriptorLines) {
- this.directories = directories;
- this.tarballs = tarballs;
- this.descriptorQueue = descriptorQueue;
- this.historyFile = historyFile;
- if (excludedFiles != null) {
- this.excludedFilesBefore = excludedFiles;
- }
- this.descriptorParser = new DescriptorParserImpl();
- this.descriptorParser.setFailUnrecognizedDescriptorLines(
- failUnrecognizedDescriptorLines);
- }
- public void run() {
- try {
- this.readOldHistory();
- this.readDescriptors();
- this.readTarballs();
- this.hasFinishedReading = true;
- } catch (Throwable t) {
- /* We're usually not writing to stdout or stderr, but we shouldn't
- * stay quiet about this potential bug. If we were to switch to a
- * logging API, this would qualify as ERROR. */
- System.err.println("Bug: uncaught exception or error while "
- + "reading descriptors:");
- t.printStackTrace();
- } finally {
- this.descriptorQueue.setOutOfDescriptors();
- }
- if (this.hasFinishedReading) {
- this.writeNewHistory();
- }
- }
- private void readOldHistory() {
- if (this.historyFile == null) {
- return;
- }
- try {
- BufferedReader br = new BufferedReader(new FileReader(
- this.historyFile));
- String line;
- while ((line = br.readLine()) != null) {
- if (!line.contains(" ")) {
- /* TODO Handle this problem? */
- continue;
- }
- long lastModifiedMillis = Long.parseLong(line.substring(0,
- line.indexOf(" ")));
- String absolutePath = line.substring(line.indexOf(" ") + 1);
- this.excludedFilesBefore.put(absolutePath, lastModifiedMillis);
- }
- br.close();
- } catch (IOException e) {
- /* TODO Handle this exception. */
- } catch (NumberFormatException e) {
- /* TODO Handle this exception. */
- }
- }
- private void writeNewHistory() {
- if (this.historyFile == null) {
- return;
- }
- try {
- if (this.historyFile.getParentFile() != null) {
- this.historyFile.getParentFile().mkdirs();
- }
- BufferedWriter bw = new BufferedWriter(new FileWriter(
- this.historyFile));
- SortedMap<String, Long> newHistory = new TreeMap<>();
- newHistory.putAll(this.excludedFilesAfter);
- newHistory.putAll(this.parsedFilesAfter);
- for (Map.Entry<String, Long> e : newHistory.entrySet()) {
- String absolutePath = e.getKey();
- long lastModifiedMillis = e.getValue();
- bw.write(String.valueOf(lastModifiedMillis) + " " + absolutePath
- + "\n");
- }
- bw.close();
- } catch (IOException e) {
- /* TODO Handle this exception. */
- }
- }
- private void readDescriptors() {
- for (File directory : this.directories) {
- if (!directory.exists() || !directory.isDirectory()) {
- continue;
- }
- Stack<File> files = new Stack<>();
- files.add(directory);
- boolean abortReading = false;
- while (!abortReading && !files.isEmpty()) {
- File file = files.pop();
- if (file.isDirectory()) {
- files.addAll(Arrays.asList(file.listFiles()));
- } else if (file.getName().endsWith(".tar") ||
- file.getName().endsWith(".tar.bz2") ||
- file.getName().endsWith(".tar.xz")) {
- this.tarballs.add(file);
- } else {
- String absolutePath = file.getAbsolutePath();
- long lastModifiedMillis = file.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
- this.excludedFilesAfter.put(absolutePath,
- lastModifiedMillis);
- continue;
- }
- this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
- DescriptorFileImpl descriptorFile = new DescriptorFileImpl();
- try {
- descriptorFile.setDirectory(directory);
- descriptorFile.setFile(file);
- descriptorFile.setFileName(file.getAbsolutePath());
- descriptorFile.setLastModified(lastModifiedMillis);
- descriptorFile.setDescriptors(this.readFile(file));
- } catch (DescriptorParseException e) {
- descriptorFile.setException(e);
- } catch (IOException e) {
- descriptorFile.setException(e);
- abortReading = true;
- }
- this.descriptorQueue.add(descriptorFile);
- }
- }
- }
- }
- private void readTarballs() {
- List<File> files = new ArrayList<>(this.tarballs);
- boolean abortReading = false;
- while (!abortReading && !files.isEmpty()) {
- File tarball = files.remove(0);
- if (!tarball.getName().endsWith(".tar") &&
- !tarball.getName().endsWith(".tar.bz2") &&
- !tarball.getName().endsWith(".tar.xz")) {
- continue;
- }
- String absolutePath = tarball.getAbsolutePath();
- long lastModifiedMillis = tarball.lastModified();
- if (this.excludedFilesBefore.containsKey(absolutePath) &&
- this.excludedFilesBefore.get(absolutePath) ==
- lastModifiedMillis) {
- this.excludedFilesAfter.put(absolutePath, lastModifiedMillis);
- continue;
- }
- this.parsedFilesAfter.put(absolutePath, lastModifiedMillis);
- try {
- FileInputStream in = new FileInputStream(tarball);
- if (in.available() > 0) {
- TarArchiveInputStream tais = null;
- if (tarball.getName().endsWith(".tar.bz2")) {
- tais = new TarArchiveInputStream(
- new BZip2CompressorInputStream(in));
- } else if (tarball.getName().endsWith(".tar.xz")) {
- tais = new TarArchiveInputStream(
- new XZCompressorInputStream(in));
- } else if (tarball.getName().endsWith(".tar")) {
- tais = new TarArchiveInputStream(in);
- }
- BufferedInputStream bis = new BufferedInputStream(tais);
- TarArchiveEntry tae = null;
- while ((tae = tais.getNextTarEntry()) != null) {
- if (tae.isDirectory()) {
- continue;
- }
- DescriptorFileImpl descriptorFile =
- new DescriptorFileImpl();
- descriptorFile.setTarball(tarball);
- descriptorFile.setFileName(tae.getName());
- descriptorFile.setLastModified(tae.getLastModifiedDate().
- getTime());
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- while ((len = bis.read(data, 0, 1024)) >= 0) {
- baos.write(data, 0, len);
- }
- byte[] rawDescriptorBytes = baos.toByteArray();
- if (rawDescriptorBytes.length < 1) {
- continue;
- }
- try {
- String fileName = tae.getName().substring(
- tae.getName().lastIndexOf("/") + 1);
- List<Descriptor> parsedDescriptors =
- this.descriptorParser.parseDescriptors(
- rawDescriptorBytes, fileName);
- descriptorFile.setDescriptors(parsedDescriptors);
- } catch (DescriptorParseException e) {
- descriptorFile.setException(e);
- }
- this.descriptorQueue.add(descriptorFile);
- }
- }
- } catch (IOException e) {
- abortReading = true;
- }
- }
- }
- private List<Descriptor> readFile(File file) throws IOException,
- DescriptorParseException {
- FileInputStream fis = new FileInputStream(file);
- BufferedInputStream bis = new BufferedInputStream(fis);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[1024];
- while ((len = bis.read(data, 0, 1024)) >= 0) {
- baos.write(data, 0, len);
- }
- bis.close();
- byte[] rawDescriptorBytes = baos.toByteArray();
- return this.descriptorParser.parseDescriptors(rawDescriptorBytes,
- file.getName());
- }
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java
deleted file mode 100644
index 0238f24..0000000
--- a/src/org/torproject/descriptor/impl/DescriptorRequestImpl.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.List;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorRequest;
-
-public class DescriptorRequestImpl implements DescriptorRequest {
-
- private String requestedResource;
- protected void setRequestedResource(String requestedResource) {
- this.requestedResource = requestedResource;
- }
- protected String getRequestedResource() {
- return this.requestedResource;
- }
-
- private String descriptorType;
- protected void setDescriptorType(String descriptorType) {
- this.descriptorType = descriptorType;
- }
- protected String getDescriptorType() {
- return this.descriptorType;
- }
-
- private byte[] responseBytes;
- protected byte[] getResponseBytes() {
- return this.responseBytes;
- }
- protected void setResponseBytes(byte[] responseBytes) {
- this.responseBytes = responseBytes;
- }
-
- private String requestUrl;
- @Override
- public String getRequestUrl() {
- return this.requestUrl;
- }
-
- private String directoryNickname;
- protected void setDirectoryNickname(String directoryNickname) {
- this.directoryNickname = directoryNickname;
- }
- @Override
- public String getDirectoryNickname() {
- return this.directoryNickname;
- }
-
- private int responseCode;
- protected void setResponseCode(int responseCode) {
- this.responseCode = responseCode;
- }
- @Override
- public int getResponseCode() {
- return this.responseCode;
- }
-
- private long requestStart;
- protected void setRequestStart(long requestStart) {
- this.requestStart = requestStart;
- }
- @Override
- public long getRequestStart() {
- return this.requestStart;
- }
-
- private long requestEnd;
- protected void setRequestEnd(long requestEnd) {
- this.requestEnd = requestEnd;
- }
- @Override
- public long getRequestEnd() {
- return this.requestEnd;
- }
-
- private boolean connectTimeoutHasExpired;
- @Override
- public boolean connectTimeoutHasExpired() {
- return this.connectTimeoutHasExpired;
- }
-
- private boolean readTimeoutHasExpired;
- @Override
- public boolean readTimeoutHasExpired() {
- return this.readTimeoutHasExpired;
- }
-
- private boolean globalTimeoutHasExpired;
- @Override
- public boolean globalTimeoutHasExpired() {
- return this.globalTimeoutHasExpired;
- }
-
- private List<Descriptor> descriptors;
- protected void setDescriptors(List<Descriptor> descriptors) {
- this.descriptors = descriptors;
- }
- @Override
- public List<Descriptor> getDescriptors() {
- return this.descriptors;
- }
-
- private Exception exception;
- protected void setException(Exception exception) {
- this.exception = exception;
- }
- @Override
- public Exception getException() {
- return this.exception;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java
deleted file mode 100644
index fb2f5ad..0000000
--- a/src/org/torproject/descriptor/impl/DirSourceEntryImpl.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.DirSourceEntry;
-
-public class DirSourceEntryImpl implements DirSourceEntry {
-
- private byte[] dirSourceEntryBytes;
- @Override
- public byte[] getDirSourceEntryBytes() {
- return this.dirSourceEntryBytes;
- }
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected DirSourceEntryImpl(byte[] dirSourceEntryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.dirSourceEntryBytes = dirSourceEntryBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseDirSourceEntryBytes();
- this.checkAndClearKeywords();
- }
-
- private SortedSet<String> exactlyOnceKeywords, atMostOnceKeywords;
- 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 void parsedExactlyOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.exactlyOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in dir-source.");
- }
- this.exactlyOnceKeywords.remove(keyword);
- }
-
- private void parsedAtMostOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.atMostOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate " + keyword + "line "
- + "in dir-source.");
- }
- this.atMostOnceKeywords.remove(keyword);
- }
-
- private void checkAndClearKeywords() throws DescriptorParseException {
- if (!this.exactlyOnceKeywords.isEmpty()) {
- throw new DescriptorParseException("dir-source does not contain a '"
- + this.exactlyOnceKeywords.first() + "' line.");
- }
- this.exactlyOnceKeywords = null;
- this.atMostOnceKeywords = null;
- }
-
- private void parseDirSourceEntryBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.dirSourceEntryBytes)).
- useDelimiter("\n");
- boolean skipCrypto = false;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ");
- switch (parts[0]) {
- case "dir-source":
- this.parseDirSourceLine(line);
- break;
- case "contact":
- this.parseContactLine(line);
- break;
- case "vote-digest":
- this.parseVoteDigestLine(line);
- break;
- case "-----BEGIN":
- skipCrypto = true;
- break;
- case "-----END":
- skipCrypto = false;
- break;
- default:
- if (!skipCrypto) {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseDirSourceLine(String line)
- throws DescriptorParseException {
- this.parsedExactlyOnceKeyword("dir-source");
- String[] parts = line.split("[ \t]+");
- if (parts.length != 7) {
- throw new DescriptorParseException("Invalid line '" + line + "'.");
- }
- String nickname = parts[1];
- if (nickname.endsWith("-legacy")) {
- nickname = nickname.substring(0, nickname.length()
- - "-legacy".length());
- this.isLegacy = true;
- this.parsedExactlyOnceKeyword("vote-digest");
- }
- this.nickname = ParseHelper.parseNickname(line, nickname);
- this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
- if (parts[3].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.hostname = parts[3];
- this.ip = ParseHelper.parseIpv4Address(line, parts[4]);
- this.dirPort = ParseHelper.parsePort(line, parts[5]);
- this.orPort = ParseHelper.parsePort(line, parts[6]);
- }
-
- private void parseContactLine(String line)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("contact");
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseVoteDigestLine(String line)
- throws DescriptorParseException {
- this.parsedExactlyOnceKeyword("vote-digest");
- String[] parts = line.split("[ \t]+");
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "'.");
- }
- this.voteDigest = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private boolean isLegacy;
- @Override
- public boolean isLegacy() {
- return this.isLegacy;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String ip;
- @Override
- public String getIp() {
- return this.ip;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private String voteDigest;
- @Override
- public String getVoteDigest() {
- return this.voteDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/org/torproject/descriptor/impl/DirectoryDownloader.java
deleted file mode 100644
index a27ed76..0000000
--- a/src/org/torproject/descriptor/impl/DirectoryDownloader.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.zip.InflaterInputStream;
-
-import org.torproject.descriptor.DescriptorParser;
-import org.torproject.descriptor.DescriptorSourceFactory;
-
-/* Download descriptors from one directory authority or mirror. First,
- * ask the coordinator thread to create a request, run it, and deliver
- * the response. Repeat until the coordinator thread says there are no
- * further requests to make. */
-public class DirectoryDownloader implements Runnable {
-
- private String nickname;
- private String ipPort;
- private DescriptorParser descriptorParser;
- protected DirectoryDownloader(String nickname, String ip, int dirPort) {
- this.nickname = nickname;
- this.ipPort = ip + ":" + String.valueOf(dirPort);
- this.descriptorParser =
- DescriptorSourceFactory.createDescriptorParser();
- }
-
- private DownloadCoordinator downloadCoordinator;
- protected void setDownloadCoordinator(
- DownloadCoordinator downloadCoordinator) {
- this.downloadCoordinator = downloadCoordinator;
- }
-
- private long connectTimeout;
- protected void setConnectTimeout(long connectTimeout) {
- this.connectTimeout = connectTimeout;
- }
-
- private long readTimeout;
- protected void setReadTimeout(long readTimeout) {
- this.readTimeout = readTimeout;
- }
-
- protected void setFailUnrecognizedDescriptorLines(
- boolean failUnrecognizedDescriptorLines) {
- this.descriptorParser.setFailUnrecognizedDescriptorLines(
- failUnrecognizedDescriptorLines);
- }
-
- @Override
- public void run() {
- boolean keepRunning = true;
- do {
- DescriptorRequestImpl request =
- this.downloadCoordinator.createRequest(this.nickname);
- if (request != null) {
- String url = "http://" + this.ipPort
- + request.getRequestedResource();
- request.setRequestStart(System.currentTimeMillis());
- HttpURLConnection huc = null;
- try {
- URL u = new URL(url);
- huc = (HttpURLConnection) u.openConnection();
- huc.setConnectTimeout((int) this.connectTimeout);
- huc.setReadTimeout((int) this.readTimeout);
- huc.setRequestMethod("GET");
- huc.connect();
- int responseCode = huc.getResponseCode();
- request.setResponseCode(responseCode);
- if (responseCode == 200) {
- BufferedInputStream in = new BufferedInputStream(
- new InflaterInputStream(huc.getInputStream()));
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int len;
- byte[] data = new byte[8192];
- while ((len = in.read(data, 0, 8192)) >= 0) {
- baos.write(data, 0, len);
- }
- in.close();
- byte[] responseBytes = baos.toByteArray();
- request.setResponseBytes(responseBytes);
- request.setRequestEnd(System.currentTimeMillis());
- request.setDescriptors(this.descriptorParser.parseDescriptors(
- responseBytes, null));
- }
- } catch (Exception e) {
- request.setException(e);
- if (huc != null) {
- huc.disconnect();
- }
- /* Stop downloading from this directory if there are any
- * problems, e.g., refused connections. */
- keepRunning = false;
- }
- this.downloadCoordinator.deliverResponse(request);
- } else {
- keepRunning = false;
- }
- } while (keepRunning);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
deleted file mode 100644
index b62fc8e..0000000
--- a/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectoryKeyCertificate;
-
-/* TODO Add test class. */
-
-public class DirectoryKeyCertificateImpl extends DescriptorImpl
- implements DirectoryKeyCertificate {
-
- protected static List<DirectoryKeyCertificate> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<DirectoryKeyCertificate> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DirectoryKeyCertificateImpl.splitRawDescriptorBytes(
- descriptorsBytes, "dir-key-certificate-version ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- DirectoryKeyCertificate parsedDescriptor =
- new DirectoryKeyCertificateImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected DirectoryKeyCertificateImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- 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();
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-key-published":
- this.parseDirKeyPublishedLine(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
- case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
- break;
- case "dir-signing-key":
- this.dirSigningKey = cryptoString;
- break;
- case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
- break;
- case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
- break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in directory key certificate.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in directory key certificate.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseDirKeyCertificateVersionLine(String line,
- String[] parts) throws DescriptorParseException {
- if (!line.equals("dir-key-certificate-version 3")) {
- throw new DescriptorParseException("Illegal directory key "
- + "certificate version number in line '" + line + "'.");
- }
- this.dirKeyCertificateVersion = 3;
- }
-
- private void parseDirAddressLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || parts[1].split(":").length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in directory key certificate.");
- }
- this.address = ParseHelper.parseIpv4Address(line,
- parts[1].split(":")[0]);
- this.port = ParseHelper.parsePort(line, parts[1].split(":")[1]);
- }
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in directory key certificate.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parseDirIdentityKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-identity-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyPublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirKeyExpiresLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCrosscertLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-crosscert")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCertificationLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-certification")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- 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";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.certificateDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.certificateDigest == null) {
- throw new DescriptorParseException("Could not calculate "
- + "certificate digest.");
- }
- }
-
- private int dirKeyCertificateVersion;
- @Override
- public int getDirKeyCertificateVersion() {
- return this.dirKeyCertificateVersion;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int port = -1;
- @Override
- public int getPort() {
- return this.port;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String dirIdentityKey;
- @Override
- public String getDirIdentityKey() {
- return this.dirIdentityKey;
- }
-
- private long dirKeyPublishedMillis;
- @Override
- public long getDirKeyPublishedMillis() {
- return this.dirKeyPublishedMillis;
- }
-
- private long dirKeyExpiresMillis;
- @Override
- public long getDirKeyExpiresMillis() {
- return this.dirKeyExpiresMillis;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String dirKeyCrosscert;
- @Override
- public String getDirKeyCrosscert() {
- return this.dirKeyCrosscert;
- }
-
- private String dirKeyCertification;
- @Override
- public String getDirKeyCertification() {
- return this.dirKeyCertification;
- }
-
- private String certificateDigest;
- @Override
- public String getCertificateDigest() {
- return this.certificateDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
deleted file mode 100644
index a955f62..0000000
--- a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
-
-import org.torproject.descriptor.DirectorySignature;
-
-public class DirectorySignatureImpl implements DirectorySignature {
-
- private byte[] directorySignatureBytes;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected DirectorySignatureImpl(byte[] directorySignatureBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.directorySignatureBytes = directorySignatureBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.parseDirectorySignatureBytes();
- }
-
- private void parseDirectorySignatureBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.directorySignatureBytes)).
- useDelimiter("\n");
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ", -1);
- String keyword = parts[0];
- switch (keyword) {
- case "directory-signature":
- int algorithmOffset = 0;
- switch (parts.length) {
- case 4:
- this.algorithm = parts[1];
- algorithmOffset = 1;
- break;
- case 3:
- break;
- default:
- throw new DescriptorParseException("Illegal line '" + line
- + "'.");
- }
- this.identity = ParseHelper.parseHexString(line,
- parts[1 + algorithmOffset]);
- this.signingKeyDigest = ParseHelper.parseHexString(
- line, parts[2 + algorithmOffset]);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- this.signature = cryptoString;
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in dir-source entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- static final String DEFAULT_ALGORITHM = "sha1";
-
- private String algorithm;
- @Override
- public String getAlgorithm() {
- return this.algorithm == null ? DEFAULT_ALGORITHM : this.algorithm;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private String signingKeyDigest;
- @Override
- public String getSigningKeyDigest() {
- return this.signingKeyDigest;
- }
-
- private String signature;
- @Override
- public String getSignature() {
- return this.signature;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/org/torproject/descriptor/impl/DownloadCoordinator.java
deleted file mode 100644
index 72cfeae..0000000
--- a/src/org/torproject/descriptor/impl/DownloadCoordinator.java
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-public interface DownloadCoordinator {
-
- public DescriptorRequestImpl createRequest(String nickname);
-
- public void deliverResponse(DescriptorRequestImpl request);
-}
diff --git a/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
deleted file mode 100644
index a8e3731..0000000
--- a/src/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorRequest;
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* TODO This whole download logic is a mess and needs a cleanup. */
-public class DownloadCoordinatorImpl implements DownloadCoordinator {
-
- private BlockingIteratorImpl<DescriptorRequest> descriptorQueue =
- new BlockingIteratorImpl<>();
- protected Iterator<DescriptorRequest> getDescriptorQueue() {
- return this.descriptorQueue;
- }
-
- private SortedSet<String> runningDirectories;
- private SortedMap<String, DirectoryDownloader> directoryAuthorities;
- private SortedMap<String, DirectoryDownloader> directoryMirrors;
- private boolean downloadConsensusFromAllAuthorities;
- private boolean includeCurrentReferencedVotes;
- private long connectTimeoutMillis;
- private long readTimeoutMillis;
- private long globalTimeoutMillis;
- private boolean failUnrecognizedDescriptorLines;
-
- protected DownloadCoordinatorImpl(
- SortedMap<String, DirectoryDownloader> directoryAuthorities,
- SortedMap<String, DirectoryDownloader> directoryMirrors,
- boolean downloadConsensus,
- boolean downloadConsensusFromAllAuthorities,
- Set<String> downloadVotes, boolean includeCurrentReferencedVotes,
- long connectTimeoutMillis, long readTimeoutMillis,
- long globalTimeoutMillis, boolean failUnrecognizedDescriptorLines) {
- this.directoryAuthorities = directoryAuthorities;
- this.directoryMirrors = directoryMirrors;
- this.runningDirectories = new TreeSet<>();
- this.runningDirectories.addAll(directoryAuthorities.keySet());
- this.runningDirectories.addAll(directoryMirrors.keySet());
- this.missingConsensus = downloadConsensus;
- this.downloadConsensusFromAllAuthorities =
- downloadConsensusFromAllAuthorities;
- this.missingVotes = downloadVotes;
- this.includeCurrentReferencedVotes = includeCurrentReferencedVotes;
- this.connectTimeoutMillis = connectTimeoutMillis;
- this.readTimeoutMillis = readTimeoutMillis;
- this.globalTimeoutMillis = globalTimeoutMillis;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- if (this.directoryMirrors.isEmpty() &&
- this.directoryAuthorities.isEmpty()) {
- this.descriptorQueue.setOutOfDescriptors();
- /* TODO Should we say anything if we don't have any directories
- * configured? */
- } else {
- GlobalTimer globalTimer = new GlobalTimer(this.globalTimeoutMillis,
- this);
- this.globalTimerThread = new Thread(globalTimer);
- this.globalTimerThread.start();
- for (DirectoryDownloader directoryMirror :
- this.directoryMirrors.values()) {
- directoryMirror.setDownloadCoordinator(this);
- directoryMirror.setConnectTimeout(this.connectTimeoutMillis);
- directoryMirror.setReadTimeout(this.readTimeoutMillis);
- directoryMirror.setFailUnrecognizedDescriptorLines(
- this.failUnrecognizedDescriptorLines);
- new Thread(directoryMirror).start();
- }
- for (DirectoryDownloader directoryAuthority :
- this.directoryAuthorities.values()) {
- directoryAuthority.setDownloadCoordinator(this);
- directoryAuthority.setConnectTimeout(this.connectTimeoutMillis);
- directoryAuthority.setReadTimeout(this.readTimeoutMillis);
- directoryAuthority.setFailUnrecognizedDescriptorLines(
- this.failUnrecognizedDescriptorLines);
- new Thread(directoryAuthority).start();
- }
- }
- }
-
- /* Interrupt all downloads if the total download time exceeds a given
- * time. */
- private Thread globalTimerThread;
- private static class GlobalTimer implements Runnable {
- private long timeoutMillis;
- private DownloadCoordinatorImpl downloadCoordinator;
- private GlobalTimer(long timeoutMillis,
- DownloadCoordinatorImpl downloadCoordinator) {
- this.timeoutMillis = timeoutMillis;
- this.downloadCoordinator = downloadCoordinator;
- }
- public void run() {
- long started = System.currentTimeMillis(), sleep;
- while ((sleep = started + this.timeoutMillis
- - System.currentTimeMillis()) > 0L) {
- try {
- Thread.sleep(sleep);
- } catch (InterruptedException e) {
- return;
- }
- }
- this.downloadCoordinator.interruptAllDownloads();
- }
- }
-
- /* Are we missing the consensus, and should the next directory that
- * hasn't tried downloading it before attempt to download it? */
- private boolean missingConsensus = false;
-
- /* Which directories are currently attempting to download the
- * consensus? */
- private Set<String> requestingConsensuses = new HashSet<>();
-
- /* Which directories have attempted to download the consensus so far,
- * including those directories that are currently attempting it? */
- private Set<String> requestedConsensuses = new HashSet<>();
-
- /* Which votes are we currently missing? */
- private Set<String> missingVotes = new HashSet<>();
-
- /* Which vote (map value) is a given directory (map key) currently
- * attempting to download? */
- private Map<String, String> requestingVotes = new HashMap<>();
-
- /* Which votes (map value) has a given directory (map key) attempted or
- * is currently attempting to download? */
- private Map<String, Set<String>> requestedVotes = new HashMap<>();
-
- private boolean hasFinishedDownloading = false;
-
- /* Look up what request a directory should make next. If there is
- * nothing to do right now, but maybe later, block the caller. If
- * we're done downloading, return null to notify the caller. */
- @Override
- public synchronized DescriptorRequestImpl createRequest(
- String nickname) {
- while (!this.hasFinishedDownloading) {
- DescriptorRequestImpl request = new DescriptorRequestImpl();
- request.setDirectoryNickname(nickname);
- if ((this.missingConsensus ||
- (this.downloadConsensusFromAllAuthorities &&
- this.directoryAuthorities.containsKey(nickname))) &&
- !this.requestedConsensuses.contains(nickname)) {
- if (!this.downloadConsensusFromAllAuthorities) {
- this.missingConsensus = false;
- }
- this.requestingConsensuses.add(nickname);
- this.requestedConsensuses.add(nickname);
- request.setRequestedResource(
- "/tor/status-vote/current/consensus.z");
- request.setDescriptorType("consensus");
- return request;
- }
- if (!this.missingVotes.isEmpty() &&
- this.directoryAuthorities.containsKey(nickname)) {
- String requestingVote = null;
- for (String missingVote : this.missingVotes) {
- if (!this.requestedVotes.containsKey(nickname) ||
- !this.requestedVotes.get(nickname).contains(missingVote)) {
- requestingVote = missingVote;
- }
- }
- if (requestingVote != null) {
- this.requestingVotes.put(nickname, requestingVote);
- if (!this.requestedVotes.containsKey(nickname)) {
- this.requestedVotes.put(nickname, new HashSet<String>());
- }
- this.requestedVotes.get(nickname).add(requestingVote);
- this.missingVotes.remove(requestingVote);
- request.setRequestedResource("/tor/status-vote/current/"
- + requestingVote + ".z");
- request.setDescriptorType("vote");
- return request;
- }
- }
- /* TODO Add server descriptors and extra-info descriptors later. */
- try {
- this.wait();
- } catch (InterruptedException e) {
- /* TODO What shall we do? */
- }
- }
- return null;
- }
-
- /* Deliver a response which may either contain one or more descriptors
- * or a failure response code. Update the lists of missing descriptors,
- * decide if there are more descriptors to download, and wake up any
- * waiting downloader threads. */
- @Override
- public synchronized void deliverResponse(
- DescriptorRequestImpl response) {
- String nickname = response.getDirectoryNickname();
- if (response.getException() != null) {
- this.runningDirectories.remove(nickname);
- }
- switch (response.getDescriptorType()) {
- case "consensus":
- this.requestingConsensuses.remove(nickname);
- if (response.getResponseCode() == 200 &&
- response.getDescriptors() != null) {
- if (this.includeCurrentReferencedVotes) {
- /* TODO Only add votes if the consensus is not older than one
- * hour. Or does that make no sense? */
- for (Descriptor parsedDescriptor :
- response.getDescriptors()) {
- if (!(parsedDescriptor instanceof
- RelayNetworkStatusConsensus)) {
- continue;
- }
- RelayNetworkStatusConsensus parsedConsensus =
- (RelayNetworkStatusConsensus) parsedDescriptor;
- for (DirSourceEntry dirSource :
- parsedConsensus.getDirSourceEntries().values()) {
- String identity = dirSource.getIdentity();
- if (!this.missingVotes.contains(identity)) {
- boolean alreadyRequested = false;
- for (Set<String> requestedBefore :
- this.requestedVotes.values()) {
- if (requestedBefore.contains(identity)) {
- alreadyRequested = true;
- break;
- }
- }
- if (!alreadyRequested) {
- this.missingVotes.add(identity);
- }
- }
- }
- }
- /* TODO Later, add referenced server descriptors. */
- }
- } else {
- this.missingConsensus = true;
- }
- break;
- case "vote":
- String requestedVote = requestingVotes.remove(nickname);
- if (response.getResponseCode() != 200) {
- this.missingVotes.add(requestedVote);
- }
- }
- if (response.getRequestEnd() != 0L) {
- this.descriptorQueue.add(response);
- }
- boolean doneDownloading = true;
- if ((this.missingConsensus ||
- this.downloadConsensusFromAllAuthorities) &&
- (!this.requestedConsensuses.containsAll(
- this.runningDirectories) ||
- !this.requestingConsensuses.isEmpty())) {
- doneDownloading = false;
- }
- if (!this.requestingVotes.isEmpty()) {
- doneDownloading = false;
- } else if (!this.missingVotes.isEmpty()) {
- if (!this.requestedVotes.keySet().containsAll(
- this.runningDirectories)) {
- doneDownloading = false;
- } else {
- for (String missingVote : this.missingVotes) {
- for (String runningDirectory : this.runningDirectories) {
- Set<String> reqVotes = this.requestedVotes.get(
- runningDirectory);
- if (!reqVotes.contains(missingVote)) {
- doneDownloading = false;
- }
- }
- }
- }
- }
- if (doneDownloading) {
- this.hasFinishedDownloading = true;
- this.globalTimerThread.interrupt();
- this.descriptorQueue.setOutOfDescriptors();
- }
- /* Wake up all waiting downloader threads. Maybe they can now
- * download something, or they'll realize we're done downloading. */
- this.notifyAll();
- }
-
- private synchronized void interruptAllDownloads() {
- this.hasFinishedDownloading = true;
- this.notifyAll();
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/org/torproject/descriptor/impl/ExitListEntryImpl.java
deleted file mode 100644
index efbf31c..0000000
--- a/src/org/torproject/descriptor/impl/ExitListEntryImpl.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExitList;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListEntryImpl implements ExitListEntry, ExitList.Entry {
-
- private byte[] exitListEntryBytes;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- @Deprecated
- private ExitListEntryImpl(String fingerprint, long publishedMillis,
- long lastStatusMillis, String exitAddress, long scanMillis) {
- this.fingerprint = fingerprint;
- this.publishedMillis = publishedMillis;
- this.lastStatusMillis = lastStatusMillis;
- this.exitAddresses.put(exitAddress, scanMillis);
- }
-
- @Deprecated
- List<ExitListEntry> oldEntries() {
- List<ExitListEntry> result = new ArrayList<>();
- if (this.exitAddresses.size() > 1) {
- for (Map.Entry<String, Long> entry :
- this.exitAddresses.entrySet()) {
- result.add(new ExitListEntryImpl(this.fingerprint,
- this.publishedMillis, this.lastStatusMillis, entry.getKey(),
- entry.getValue()));
- }
- } else {
- result.add(this);
- }
- return result;
- }
-
- protected ExitListEntryImpl(byte[] exitListEntryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.exitListEntryBytes = exitListEntryBytes;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseExitListEntryBytes();
- this.checkAndClearKeywords();
- }
-
- private SortedSet<String> keywordCountingSet;
- private void initializeKeywords() {
- this.keywordCountingSet = new TreeSet<>();
- this.keywordCountingSet.add("ExitNode");
- this.keywordCountingSet.add("Published");
- this.keywordCountingSet.add("LastStatus");
- this.keywordCountingSet.add("ExitAddress");
- }
-
- private void parsedExactlyOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.keywordCountingSet.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in exit list entry.");
- }
- this.keywordCountingSet.remove(keyword);
- }
-
- private void checkAndClearKeywords() throws DescriptorParseException {
- for (String missingKeyword : this.keywordCountingSet) {
- throw new DescriptorParseException("Missing '" + missingKeyword
- + "' line in exit list entry.");
- }
- this.keywordCountingSet = null;
- }
-
- private void parseExitListEntryBytes()
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.exitListEntryBytes)).
- useDelimiter(ExitList.EOL);
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split(" ");
- String keyword = parts[0];
- switch (keyword) {
- case "ExitNode":
- this.parseExitNodeLine(line, parts);
- break;
- case "Published":
- this.parsePublishedLine(line, parts);
- break;
- case "LastStatus":
- this.parseLastStatusLine(line, parts);
- break;
- case "ExitAddress":
- this.parseExitAddressLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in exit list entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseExitNodeLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseLastStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.parsedExactlyOnceKeyword(parts[0]);
- this.lastStatusMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseExitAddressLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 4) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "exit list entry.");
- }
- this.keywordCountingSet.remove(parts[0]);
- this.exitAddresses.put(ParseHelper.parseIpv4Address(line, parts[1]),
- ParseHelper.parseTimestampAtIndex(line, parts, 2, 3));
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long lastStatusMillis;
- @Override
- public long getLastStatusMillis() {
- return this.lastStatusMillis;
- }
-
- private String exitAddress;
- @Override
- public String getExitAddress() {
- if (null == exitAddress) {
- Map.Entry<String, Long> randomEntry =
- this.exitAddresses.entrySet().iterator().next();
- this.exitAddress = randomEntry.getKey();
- this.scanMillis = randomEntry.getValue();
- }
- return this.exitAddress;
- }
-
- private Map<String, Long> exitAddresses = new HashMap<>();
- @Override
- public Map<String, Long> getExitAddresses(){
- return new HashMap<>(this.exitAddresses);
- }
-
- private long scanMillis;
- @Override
- public long getScanMillis() {
- if (null == exitAddress) {
- getExitAddress();
- }
- return scanMillis;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExitListImpl.java b/src/org/torproject/descriptor/impl/ExitListImpl.java
deleted file mode 100644
index 10619ba..0000000
--- a/src/org/torproject/descriptor/impl/ExitListImpl.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.TimeZone;
-
-import org.torproject.descriptor.ExitList;
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListImpl extends DescriptorImpl implements ExitList {
-
- protected ExitListImpl(byte[] rawDescriptorBytes, String fileName,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
- this.splitAndParseExitListEntries(rawDescriptorBytes);
- this.setPublishedMillisFromFileName(fileName);
- }
-
- private void setPublishedMillisFromFileName(String fileName)
- throws DescriptorParseException {
- if (this.downloadedMillis == 0L &&
- fileName.length() == "2012-02-01-04-06-24".length()) {
- try {
- SimpleDateFormat fileNameFormat = new SimpleDateFormat(
- "yyyy-MM-dd-HH-mm-ss");
- fileNameFormat.setLenient(false);
- fileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- this.downloadedMillis = fileNameFormat.parse(fileName).getTime();
- } catch (ParseException e) {
- /* Handle below. */
- }
- }
- if (this.downloadedMillis == 0L) {
- throw new DescriptorParseException("Unrecognized exit list file "
- + "name '" + fileName + "'.");
- }
- }
-
- private void splitAndParseExitListEntries(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- Scanner s = new Scanner(descriptorString).useDelimiter(EOL);
- StringBuilder sb = new StringBuilder();
- boolean firstEntry = true;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) { /* Skip annotation. */
- if (!s.hasNext()) {
- throw new DescriptorParseException("Descriptor is empty.");
- } else {
- line = s.next();
- }
- }
- String[] parts = line.split(" ");
- String keyword = parts[0];
- switch (keyword) {
- case "Downloaded":
- this.downloadedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- break;
- case "ExitNode":
- if (!firstEntry) {
- this.parseExitListEntry(sb.toString().getBytes());
- } else {
- firstEntry = false;
- }
- sb = new StringBuilder();
- sb.append(line).append(ExitList.EOL);
- break;
- case "Published":
- sb.append(line).append(ExitList.EOL);
- break;
- case "LastStatus":
- sb.append(line).append(ExitList.EOL);
- break;
- case "ExitAddress":
- sb.append(line).append(ExitList.EOL);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in exit list.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- /* Parse the last entry. */
- this.parseExitListEntry(sb.toString().getBytes());
- }
-
- protected void parseExitListEntry(byte[] exitListEntryBytes)
- throws DescriptorParseException {
- ExitListEntryImpl exitListEntry = new ExitListEntryImpl(
- exitListEntryBytes, this.failUnrecognizedDescriptorLines);
- this.exitListEntries.add(exitListEntry);
- this.oldExitListEntries.addAll(exitListEntry.oldEntries());
- List<String> unrecognizedExitListEntryLines = exitListEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedExitListEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedExitListEntryLines);
- }
- }
-
- private long downloadedMillis;
- @Override
- public long getDownloadedMillis() {
- return this.downloadedMillis;
- }
-
- private Set<ExitListEntry> oldExitListEntries = new HashSet<>();
- @Deprecated
- @Override
- public Set<ExitListEntry> getExitListEntries() {
- return new HashSet<>(this.oldExitListEntries);
- }
-
- private Set<ExitList.Entry> exitListEntries = new HashSet<>();
- @Override
- public Set<ExitList.Entry> getEntries() {
- return new HashSet<>(this.exitListEntries);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
deleted file mode 100644
index 3f72616..0000000
--- a/src/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,1284 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-
-public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
- implements ExtraInfoDescriptor {
-
- protected ExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- 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);
- 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();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- List<String> cryptoLines = null;
- while (s.hasNext()) {
- String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "extra-info":
- this.parseExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-db-digest":
- this.parseGeoipDbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip6-db-digest":
- this.parseGeoip6DbDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-start-time":
- this.parseGeoipStartTimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "geoip-client-origins":
- this.parseGeoipClientOriginsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-stats-end":
- this.parseDirreqStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-ips":
- this.parseDirreqV2IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-ips":
- this.parseDirreqV3IpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-reqs":
- this.parseDirreqV2ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-reqs":
- this.parseDirreqV3ReqsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-share":
- this.parseDirreqV2ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-share":
- this.parseDirreqV3ShareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-resp":
- this.parseDirreqV2RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-resp":
- this.parseDirreqV3RespLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-direct-dl":
- this.parseDirreqV2DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-direct-dl":
- this.parseDirreqV3DirectDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v2-tunneled-dl":
- this.parseDirreqV2TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-v3-tunneled-dl":
- this.parseDirreqV3TunneledDlLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-read-history":
- this.parseDirreqReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dirreq-write-history":
- this.parseDirreqWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-stats-end":
- this.parseEntryStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "entry-ips":
- this.parseEntryIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-stats-end":
- this.parseCellStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-processed-cells":
- this.parseCellProcessedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-queued-cells":
- this.parseCellQueuedCellsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-time-in-queue":
- this.parseCellTimeInQueueLine(line, lineNoOpt, partsNoOpt);
- break;
- case "cell-circuits-per-decile":
- this.parseCellCircuitsPerDecileLine(line, lineNoOpt, partsNoOpt);
- break;
- case "conn-bi-direct":
- this.parseConnBiDirectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-stats-end":
- this.parseExitStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-written":
- this.parseExitKibibytesWrittenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-kibibytes-read":
- this.parseExitKibibytesReadLine(line, lineNoOpt, partsNoOpt);
- break;
- case "exit-streams-opened":
- this.parseExitStreamsOpenedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-stats-end":
- this.parseBridgeStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ips":
- this.parseBridgeStatsIpsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-versions":
- this.parseBridgeIpVersionsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bridge-ip-transports":
- this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "transport":
- this.parseTransportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-stats-end":
- this.parseHidservStatsEndLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidserv-rend-relayed-cells":
- this.parseHidservRendRelayedCellsLine(line, lineNoOpt,
- partsNoOpt);
- break;
- case "hidserv-dir-onions-seen":
- this.parseHidservDirOnionsSeenLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
- case "router-signature":
- this.routerSignature = cryptoString;
- break;
- case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in extra-info "
- + "descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- cryptoLines = null;
- nextCrypto = "";
- }
- break;
- default:
- if (cryptoLines != null) {
- cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in extra-info descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseExtraInfoLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[2]);
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseGeoipDbDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoipDbDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseGeoip6DbDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoip6DbDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseGeoipStartTimeLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in extra-info descriptor.");
- }
- this.geoipStartTimeMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseGeoipClientOriginsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.geoipClientOrigins =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.dirreqStatsEndMillis = parsedStatsEndData[0];
- this.dirreqStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private long[] parseStatsEndLine(String line, String partsNoOpt[],
- int partsNoOptExpectedLength) throws DescriptorParseException {
- if (partsNoOpt.length != partsNoOptExpectedLength ||
- partsNoOpt[3].length() < 2 || !partsNoOpt[3].startsWith("(") ||
- !partsNoOpt[4].equals("s)")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- long[] result = new long[2];
- result[0] = ParseHelper.parseTimestampAtIndex(line, partsNoOpt, 1, 2);
- result[1] = ParseHelper.parseSeconds(line,
- partsNoOpt[3].substring(1));
- if (result[1] <= 0) {
- throw new DescriptorParseException("Interval length must be "
- + "positive in line '" + line + "'.");
- }
- return result;
- }
-
- private void parseDirreqV2IpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV3IpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Ips = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV2ReqsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Reqs =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV3ReqsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Reqs =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirreqV2ShareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Share = this.parseShareLine(line, partsNoOpt);
- }
-
- private void parseDirreqV3ShareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Share = this.parseShareLine(line, partsNoOpt);
- }
-
- private double parseShareLine(String line, String[] partsNoOpt)
- throws DescriptorParseException {
- double share = -1.0;
- if (partsNoOpt.length == 2 && partsNoOpt[1].length() >= 2 &&
- partsNoOpt[1].endsWith("%")) {
- String shareString = partsNoOpt[1];
- shareString = shareString.substring(0, shareString.length() - 1);
- try {
- share = Double.parseDouble(shareString);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (share < 0.0) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- return share;
- }
-
- private void parseDirreqV2RespLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2Resp =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3RespLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3Resp =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV2DirectDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2DirectDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3DirectDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3DirectDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV2TunneledDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV2TunneledDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseDirreqV3TunneledDlLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqV3TunneledDl =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line,partsNoOpt, 1, 0);
- }
-
- private void parseDirreqReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqReadHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseDirreqWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.dirreqWriteHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseEntryStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.entryStatsEndMillis = parsedStatsEndData[0];
- this.entryStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseEntryIpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.entryIps = ParseHelper.parseCommaSeparatedKeyIntegerValueList(
- line, partsNoOpt, 1, 2);
- }
-
- private void parseCellStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.cellStatsEndMillis = parsedStatsEndData[0];
- this.cellStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseCellProcessedCellsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellProcessedCells = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
- if (this.cellProcessedCells.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellQueuedCellsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellQueuedCells = ParseHelper.parseCommaSeparatedDoubleValueList(
- line, partsNoOpt, 1);
- if (this.cellQueuedCells.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellTimeInQueueLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.cellTimeInQueue = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 1);
- if (this.cellTimeInQueue.length != 10) {
- throw new DescriptorParseException("There must be exact ten values "
- + "in line '" + line + "'.");
- }
- }
-
- private void parseCellCircuitsPerDecileLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- int circuits = -1;
- if (partsNoOpt.length == 2) {
- try {
- circuits = Integer.parseInt(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (circuits < 0) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.cellCircuitsPerDecile = circuits;
- }
-
- private void parseConnBiDirectLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 6);
- this.connBiDirectStatsEndMillis = parsedStatsEndData[0];
- this.connBiDirectStatsIntervalLength = parsedStatsEndData[1];
- Integer[] parsedConnBiDirectStats = ParseHelper.
- parseCommaSeparatedIntegerValueList(line, partsNoOpt, 5);
- if (parsedConnBiDirectStats.length != 4) {
- throw new DescriptorParseException("Illegal line '" + line + "' in "
- + "extra-info descriptor.");
- }
- this.connBiDirectBelow = parsedConnBiDirectStats[0];
- this.connBiDirectRead = parsedConnBiDirectStats[1];
- this.connBiDirectWrite = parsedConnBiDirectStats[2];
- this.connBiDirectBoth = parsedConnBiDirectStats[3];
- }
-
- private void parseExitStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.exitStatsEndMillis = parsedStatsEndData[0];
- this.exitStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseExitKibibytesWrittenLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- this.exitKibibytesWritten = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitKibibytesWritten.keySet());
- this.verifyBytesOrStreams(line, this.exitKibibytesWritten.values());
- }
-
- private void parseExitKibibytesReadLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.exitKibibytesRead = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitKibibytesRead.keySet());
- this.verifyBytesOrStreams(line, this.exitKibibytesRead.values());
- }
-
- private void parseExitStreamsOpenedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.exitStreamsOpened = this.sortByPorts(ParseHelper.
- parseCommaSeparatedKeyLongValueList(line, partsNoOpt, 1, 0));
- this.verifyPorts(line, this.exitStreamsOpened.keySet());
- this.verifyBytesOrStreams(line, this.exitStreamsOpened.values());
- }
-
- private SortedMap<String, Long> sortByPorts(
- SortedMap<String, Long> naturalOrder) {
- SortedMap<String, Long> byPortNumber =
- new TreeMap<String, Long>(new Comparator<String>() {
- public int compare(String arg0, String arg1) {
- int port0 = 0, port1 = 0;
- try {
- port1 = Integer.parseInt(arg1);
- } catch (NumberFormatException e) {
- return -1;
- }
- try {
- port0 = Integer.parseInt(arg0);
- } catch (NumberFormatException e) {
- return 1;
- }
- if (port0 < port1) {
- return -1;
- } else if (port0 > port1) {
- return 1;
- } else {
- return 0;
- }
- }});
- byPortNumber.putAll(naturalOrder);
- return byPortNumber;
- }
-
- private void verifyPorts(String line, Set<String> ports)
- throws DescriptorParseException {
- boolean valid = true;
- try {
- for (String port : ports) {
- if (!port.equals("other") && Integer.parseInt(port) <= 0) {
- valid = false;
- break;
- }
- }
- } catch (NumberFormatException e) {
- valid = false;
- }
- if (!valid) {
- throw new DescriptorParseException("Invalid port in line '" + line
- + "'.");
- }
- }
-
- private void verifyBytesOrStreams(String line,
- Collection<Long> bytesOrStreams) throws DescriptorParseException {
- boolean valid = true;
- for (long s : bytesOrStreams) {
- if (s < 0L) {
- valid = false;
- break;
- }
- }
- if (!valid) {
- throw new DescriptorParseException("Invalid value in line '" + line
- + "'.");
- }
- }
-
- private void parseBridgeStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.bridgeStatsEndMillis = parsedStatsEndData[0];
- this.bridgeStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseBridgeStatsIpsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIps =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseBridgeIpVersionsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIpVersions =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseBridgeIpTransportsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.bridgeIpTransports =
- ParseHelper.parseCommaSeparatedKeyIntegerValueList(line,
- partsNoOpt, 1, 0);
- }
-
- private void parseTransportLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.transports.add(partsNoOpt[1]);
- }
-
- private void parseHidservStatsEndLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
- 5);
- this.hidservStatsEndMillis = parsedStatsEndData[0];
- this.hidservStatsIntervalLength = parsedStatsEndData[1];
- }
-
- private void parseHidservRendRelayedCellsLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.hidservRendRelayedCells = Double.parseDouble(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hidservRendRelayedCellsParameters =
- ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
- partsNoOpt, 2);
- }
-
- private void parseHidservDirOnionsSeenLine(String line,
- String lineNoOpt, String[] partsNoOpt)
- throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.hidservDirOnionsSeen = Double.parseDouble(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hidservDirOnionsSeenParameters =
- ParseHelper.parseSpaceSeparatedStringKeyDoubleValueMap(line,
- partsNoOpt, 2);
- }
-
- private void parseRouterSignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("router-signature")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseRouterDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- }
-
- private void parseIdentityEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseIdentityEd25519CryptoBlock(String cryptoString)
- throws DescriptorParseException {
- String masterKeyEd25519FromIdentityEd25519 =
- ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
- cryptoString);
- if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
- masterKeyEd25519FromIdentityEd25519)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
- }
-
- private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
- if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
- masterKeyEd25519FromMasterKeyEd25519Line)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
- }
-
- private void parseRouterSigEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.routerSignatureEd25519 = partsNoOpt[1];
- }
-
- private void parseRouterDigestSha256Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
- this.extraInfoDigestSha256 = partsNoOpt[1];
- }
-
- private void calculateDigest() throws DescriptorParseException {
- if (this.extraInfoDigest != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "extra-info ";
- String sigToken = "\nrouter-signature\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.extraInfoDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.extraInfoDigest == null) {
- throw new DescriptorParseException("Could not calculate extra-info "
- + "descriptor digest.");
- }
- }
-
- private void calculateDigestSha256() throws DescriptorParseException {
- if (this.extraInfoDigestSha256 != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest-sha256" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "extra-info ";
- String sigToken = "\n-----END SIGNATURE-----\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
- 0, sig - start);
- this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.extraInfoDigestSha256 == null) {
- throw new DescriptorParseException("Could not calculate extra-info "
- + "descriptor SHA-256 digest.");
- }
- }
-
- private String extraInfoDigest;
- @Override
- public String getExtraInfoDigest() {
- return this.extraInfoDigest;
- }
-
- private String extraInfoDigestSha256;
- @Override
- public String getExtraInfoDigestSha256() {
- return this.extraInfoDigestSha256;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private BandwidthHistory readHistory;
- @Override
- public BandwidthHistory getReadHistory() {
- return this.readHistory;
- }
-
- private BandwidthHistory writeHistory;
- @Override
- public BandwidthHistory getWriteHistory() {
- return this.writeHistory;
- }
-
- private String geoipDbDigest;
- @Override
- public String getGeoipDbDigest() {
- return this.geoipDbDigest;
- }
-
- private String geoip6DbDigest;
- @Override
- public String getGeoip6DbDigest() {
- return this.geoip6DbDigest;
- }
-
- private long dirreqStatsEndMillis = -1L;
- @Override
- public long getDirreqStatsEndMillis() {
- return this.dirreqStatsEndMillis;
- }
-
- private long dirreqStatsIntervalLength = -1L;
- @Override
- public long getDirreqStatsIntervalLength() {
- return this.dirreqStatsIntervalLength;
- }
-
- private String dirreqV2Ips;
- @Override
- public SortedMap<String, Integer> getDirreqV2Ips() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Ips);
- }
-
- private String dirreqV3Ips;
- @Override
- public SortedMap<String, Integer> getDirreqV3Ips() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Ips);
- }
-
- private String dirreqV2Reqs;
- @Override
- public SortedMap<String, Integer> getDirreqV2Reqs() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Reqs);
- }
-
- private String dirreqV3Reqs;
- @Override
- public SortedMap<String, Integer> getDirreqV3Reqs() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Reqs);
- }
-
- private double dirreqV2Share = -1.0;
- @Override
- public double getDirreqV2Share() {
- return this.dirreqV2Share;
- }
-
- private double dirreqV3Share = -1.0;
- @Override
- public double getDirreqV3Share() {
- return this.dirreqV3Share;
- }
-
- private String dirreqV2Resp;
- @Override
- public SortedMap<String, Integer> getDirreqV2Resp() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2Resp);
- }
-
- private String dirreqV3Resp;
- @Override
- public SortedMap<String, Integer> getDirreqV3Resp() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3Resp);
- }
-
- private String dirreqV2DirectDl;
- @Override
- public SortedMap<String, Integer> getDirreqV2DirectDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2DirectDl);
- }
-
- private String dirreqV3DirectDl;
- @Override
- public SortedMap<String, Integer> getDirreqV3DirectDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3DirectDl);
- }
-
- private String dirreqV2TunneledDl;
- @Override
- public SortedMap<String, Integer> getDirreqV2TunneledDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV2TunneledDl);
- }
-
- private String dirreqV3TunneledDl;
- @Override
- public SortedMap<String, Integer> getDirreqV3TunneledDl() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.dirreqV3TunneledDl);
- }
-
- private BandwidthHistory dirreqReadHistory;
- @Override
- public BandwidthHistory getDirreqReadHistory() {
- return this.dirreqReadHistory;
- }
-
- private BandwidthHistory dirreqWriteHistory;
- @Override
- public BandwidthHistory getDirreqWriteHistory() {
- return this.dirreqWriteHistory;
- }
-
- private long entryStatsEndMillis = -1L;
- @Override
- public long getEntryStatsEndMillis() {
- return this.entryStatsEndMillis;
- }
-
- private long entryStatsIntervalLength = -1L;
- @Override
- public long getEntryStatsIntervalLength() {
- return this.entryStatsIntervalLength;
- }
-
- private String entryIps;
- @Override
- public SortedMap<String, Integer> getEntryIps() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.entryIps);
- }
-
- private long cellStatsEndMillis = -1L;
- @Override
- public long getCellStatsEndMillis() {
- return this.cellStatsEndMillis;
- }
-
- private long cellStatsIntervalLength = -1L;
- @Override
- public long getCellStatsIntervalLength() {
- return this.cellStatsIntervalLength;
- }
-
- private Integer[] cellProcessedCells;
- @Override
- public List<Integer> getCellProcessedCells() {
- return this.cellProcessedCells == null ? null :
- Arrays.asList(this.cellProcessedCells);
- }
-
- private Double[] cellQueuedCells;
- @Override
- public List<Double> getCellQueuedCells() {
- return this.cellQueuedCells == null ? null :
- Arrays.asList(this.cellQueuedCells);
- }
-
- private Integer[] cellTimeInQueue;
- @Override
- public List<Integer> getCellTimeInQueue() {
- return this.cellTimeInQueue == null ? null :
- Arrays.asList(this.cellTimeInQueue);
- }
-
- private int cellCircuitsPerDecile = -1;
- @Override
- public int getCellCircuitsPerDecile() {
- return this.cellCircuitsPerDecile;
- }
-
- private long connBiDirectStatsEndMillis = -1L;
- @Override
- public long getConnBiDirectStatsEndMillis() {
- return this.connBiDirectStatsEndMillis;
- }
-
- private long connBiDirectStatsIntervalLength = -1L;
- @Override
- public long getConnBiDirectStatsIntervalLength() {
- return this.connBiDirectStatsIntervalLength;
- }
-
- private int connBiDirectBelow = -1;
- @Override
- public int getConnBiDirectBelow() {
- return this.connBiDirectBelow;
- }
-
- private int connBiDirectRead = -1;
- @Override
- public int getConnBiDirectRead() {
- return this.connBiDirectRead;
- }
-
- private int connBiDirectWrite = -1;
- @Override
- public int getConnBiDirectWrite() {
- return this.connBiDirectWrite;
- }
-
- private int connBiDirectBoth = -1;
- @Override
- public int getConnBiDirectBoth() {
- return this.connBiDirectBoth;
- }
-
- private long exitStatsEndMillis = -1L;
- @Override
- public long getExitStatsEndMillis() {
- return this.exitStatsEndMillis;
- }
-
- private long exitStatsIntervalLength = -1L;
- @Override
- public long getExitStatsIntervalLength() {
- return this.exitStatsIntervalLength;
- }
-
- private SortedMap<String, Long> exitKibibytesWritten;
- @Override
- public SortedMap<String, Long> getExitKibibytesWritten() {
- return this.exitKibibytesWritten == null ? null :
- new TreeMap<>(this.exitKibibytesWritten);
- }
-
- private SortedMap<String, Long> exitKibibytesRead;
- @Override
- public SortedMap<String, Long> getExitKibibytesRead() {
- return this.exitKibibytesRead == null ? null :
- new TreeMap<>(this.exitKibibytesRead);
- }
-
- private SortedMap<String, Long> exitStreamsOpened;
- @Override
- public SortedMap<String, Long> getExitStreamsOpened() {
- return this.exitStreamsOpened == null ? null :
- new TreeMap<>(this.exitStreamsOpened);
- }
-
- private long geoipStartTimeMillis = -1L;
- @Override
- public long getGeoipStartTimeMillis() {
- return this.geoipStartTimeMillis;
- }
-
- private String geoipClientOrigins;
- @Override
- public SortedMap<String, Integer> getGeoipClientOrigins() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.geoipClientOrigins);
- }
-
- private long bridgeStatsEndMillis = -1L;
- @Override
- public long getBridgeStatsEndMillis() {
- return this.bridgeStatsEndMillis;
- }
-
- private long bridgeStatsIntervalLength = -1L;
- @Override
- public long getBridgeStatsIntervalLength() {
- return this.bridgeStatsIntervalLength;
- }
-
- private String bridgeIps;
- @Override
- public SortedMap<String, Integer> getBridgeIps() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIps);
- }
-
- private String bridgeIpVersions;
- @Override
- public SortedMap<String, Integer> getBridgeIpVersions() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIpVersions);
- }
-
- private String bridgeIpTransports;
- @Override
- public SortedMap<String, Integer> getBridgeIpTransports() {
- return ParseHelper.convertCommaSeparatedKeyIntegerValueList(
- this.bridgeIpTransports);
- }
-
- private List<String> transports = new ArrayList<>();
- @Override
- public List<String> getTransports() {
- return new ArrayList<>(this.transports);
- }
-
- private long hidservStatsEndMillis = -1L;
- @Override
- public long getHidservStatsEndMillis() {
- return this.hidservStatsEndMillis;
- }
-
- private long hidservStatsIntervalLength = -1L;
- @Override
- public long getHidservStatsIntervalLength() {
- return this.hidservStatsIntervalLength;
- }
-
- private Double hidservRendRelayedCells;
- @Override
- public Double getHidservRendRelayedCells() {
- return this.hidservRendRelayedCells;
- }
-
- private Map<String, Double> hidservRendRelayedCellsParameters;
- @Override
- public Map<String, Double> getHidservRendRelayedCellsParameters() {
- return this.hidservRendRelayedCellsParameters == null ? null :
- new HashMap<>(this.hidservRendRelayedCellsParameters);
- }
-
- private Double hidservDirOnionsSeen;
- @Override
- public Double getHidservDirOnionsSeen() {
- return this.hidservDirOnionsSeen;
- }
-
- private Map<String, Double> hidservDirOnionsSeenParameters;
- @Override
- public Map<String, Double> getHidservDirOnionsSeenParameters() {
- return this.hidservDirOnionsSeenParameters == null ? null :
- new HashMap<>(this.hidservDirOnionsSeenParameters);
- }
-
- private String routerSignature;
- @Override
- public String getRouterSignature() {
- return this.routerSignature;
- }
-
- private String identityEd25519;
- @Override
- public String getIdentityEd25519() {
- return this.identityEd25519;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-
- private String routerSignatureEd25519;
- @Override
- public String getRouterSignatureEd25519() {
- return this.routerSignatureEd25519;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
deleted file mode 100644
index 4931c31..0000000
--- a/src/org/torproject/descriptor/impl/MicrodescriptorImpl.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/* Copyright 2014--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.Microdescriptor;
-
-/* Contains a microdescriptor. */
-public class MicrodescriptorImpl extends DescriptorImpl
- implements Microdescriptor {
-
- protected static List<Microdescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<Microdescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "onion-key\n");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- Microdescriptor parsedDescriptor =
- new MicrodescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected MicrodescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseDescriptorBytes();
- this.calculateDigest();
- Set<String> exactlyOnceKeywords = new HashSet<>(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();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) {
- continue;
- }
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "onion-key":
- this.parseOnionKeyLine(line, parts);
- nextCrypto = "onion-key";
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, parts);
- break;
- case "a":
- this.parseALine(line, parts);
- break;
- case "family":
- this.parseFamilyLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "p6":
- this.parseP6Line(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("onion-key")) {
- this.onionKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in microdescriptor.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- ParseHelper.parseKeyword(line, parts[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in microdescriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseOnionKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("onion-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.ntorOnionKey = parts[1].replaceAll("=", "");
- }
-
- private void parseALine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(parts[1]);
- }
-
- private void parseFamilyLine(String line, String[] parts)
- throws DescriptorParseException {
- String[] familyEntries = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- if (parts[i].startsWith("$")) {
- if (parts[i].contains("=") ^ parts[i].contains("~")) {
- String separator = parts[i].contains("=") ? "=" : "~";
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[i].substring(1, parts[i].indexOf(separator)));
- String nickname = ParseHelper.parseNickname(line,
- parts[i].substring(parts[i].indexOf(separator) + 1));
- familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
- } else {
- familyEntries[i - 1] = "$"
- + ParseHelper.parseTwentyByteHexString(line,
- parts[i].substring(1));
- }
- } else {
- familyEntries[i - 1] = ParseHelper.parseNickname(line, parts[i]);
- }
- }
- this.familyEntries = familyEntries;
- }
-
- private void parsePLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validatePOrP6Line(line, parts);
- this.defaultPolicy = parts[1];
- this.portList = parts[2];
- }
-
- private void parseP6Line(String line, String[] parts)
- throws DescriptorParseException {
- this.validatePOrP6Line(line, parts);
- this.ipv6DefaultPolicy = parts[1];
- this.ipv6PortList = parts[2];
- }
-
- private void validatePOrP6Line(String line, String[] parts)
- throws DescriptorParseException {
- boolean isValid = true;
- if (parts.length != 3) {
- isValid = false;
- } else {
- switch (parts[1]) {
- case "accept":
- case "reject":
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseIdLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else {
- switch (parts[1]) {
- case "ed25519":
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
- this.ed25519Identity = parts[2];
- break;
- case "rsa1024":
- ParseHelper.parseTwentyByteBase64String(line, parts[2]);
- this.rsa1024Identity = parts[2];
- break;
- default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "onion-key\n";
- int start = ascii.indexOf(startToken);
- int end = ascii.length();
- if (start >= 0 && end > start) {
- byte[] forDigest = new byte[end - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, end - start);
- this.microdescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.microdescriptorDigest == null) {
- throw new DescriptorParseException("Could not calculate "
- + "microdescriptor digest.");
- }
- }
-
- private String microdescriptorDigest;
- @Override
- public String getMicrodescriptorDigest() {
- return this.microdescriptorDigest;
- }
-
- private String onionKey;
- @Override
- public String getOnionKey() {
- return this.onionKey;
- }
-
- private String ntorOnionKey;
- @Override
- public String getNtorOnionKey() {
- return this.ntorOnionKey;
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private String[] familyEntries;
- @Override
- public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
- }
- private String defaultPolicy;
- @Override
- public String getDefaultPolicy() {
- return this.defaultPolicy;
- }
-
- private String portList;
- @Override
- public String getPortList() {
- return this.portList;
- }
-
- private String ipv6DefaultPolicy;
- @Override
- public String getIpv6DefaultPolicy() {
- return this.ipv6DefaultPolicy;
- }
-
- private String ipv6PortList;
- @Override
- public String getIpv6PortList() {
- return this.ipv6PortList;
- }
-
- private String rsa1024Identity;
- @Override
- public String getRsa1024Identity() {
- return this.rsa1024Identity;
- }
-
- private String ed25519Identity;
- @Override
- public String getEd25519Identity() {
- return this.ed25519Identity;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
deleted file mode 100644
index b73d211..0000000
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.NetworkStatusEntry;
-
-public class NetworkStatusEntryImpl implements NetworkStatusEntry {
-
- private byte[] statusEntryBytes;
- @Override
- public byte[] getStatusEntryBytes() {
- return this.statusEntryBytes;
- }
-
- private boolean microdescConsensus;
-
- private boolean failUnrecognizedDescriptorLines;
- private List<String> unrecognizedLines;
- protected List<String> getAndClearUnrecognizedLines() {
- List<String> lines = this.unrecognizedLines;
- this.unrecognizedLines = null;
- return lines;
- }
-
- protected NetworkStatusEntryImpl(byte[] statusEntryBytes,
- boolean microdescConsensus, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- this.statusEntryBytes = statusEntryBytes;
- this.microdescConsensus = microdescConsensus;
- this.failUnrecognizedDescriptorLines =
- failUnrecognizedDescriptorLines;
- this.initializeKeywords();
- this.parseStatusEntryBytes();
- this.clearAtMostOnceKeywords();
- }
-
- private SortedSet<String> atMostOnceKeywords;
- private void initializeKeywords() {
- this.atMostOnceKeywords = new TreeSet<>();
- this.atMostOnceKeywords.add("s");
- this.atMostOnceKeywords.add("v");
- this.atMostOnceKeywords.add("w");
- this.atMostOnceKeywords.add("p");
- }
-
- private void parsedAtMostOnceKeyword(String keyword)
- throws DescriptorParseException {
- if (!this.atMostOnceKeywords.contains(keyword)) {
- throw new DescriptorParseException("Duplicate '" + keyword
- + "' line in status entry.");
- }
- this.atMostOnceKeywords.remove(keyword);
- }
-
- private void parseStatusEntryBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.statusEntryBytes)).
- useDelimiter("\n");
- String line = null;
- if (!s.hasNext() || !(line = s.next()).startsWith("r ")) {
- throw new DescriptorParseException("Status entry must start with "
- + "an r line.");
- }
- String[] rLineParts = line.split("[ \t]+");
- this.parseRLine(line, rLineParts);
- while (s.hasNext()) {
- line = s.next();
- String[] parts = !line.startsWith("opt ") ? line.split("[ \t]+") :
- line.substring("opt ".length()).split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "a":
- this.parseALine(line, parts);
- break;
- case "s":
- this.parseSLine(line, parts);
- break;
- case "v":
- this.parseVLine(line, parts);
- break;
- case "w":
- this.parseWLine(line, parts);
- break;
- case "p":
- this.parsePLine(line, parts);
- break;
- case "m":
- this.parseMLine(line, parts);
- break;
- case "id":
- this.parseIdLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in status entry.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseRLine(String line, String[] parts)
- throws DescriptorParseException {
- if ((!this.microdescConsensus && parts.length != 9) ||
- (this.microdescConsensus && parts.length != 8)) {
- throw new DescriptorParseException("r line '" + line + "' has "
- + "fewer space-separated elements than expected.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- this.fingerprint = ParseHelper.parseTwentyByteBase64String(line,
- parts[2]);
- int descriptorOffset = 0;
- if (!this.microdescConsensus) {
- this.descriptor = ParseHelper.parseTwentyByteBase64String(line,
- parts[3]);
- descriptorOffset = 1;
- }
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 3 + descriptorOffset, 4 + descriptorOffset);
- this.address = ParseHelper.parseIpv4Address(line,
- parts[5 + descriptorOffset]);
- this.orPort = ParseHelper.parsePort(line,
- parts[6 + descriptorOffset]);
- this.dirPort = ParseHelper.parsePort(line,
- parts[7 + descriptorOffset]);
- }
-
- private void parseALine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "status entry.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(parts[1]);
- }
-
- private static Map<String, Integer> flagIndexes = new HashMap<>();
- private static Map<Integer, String> flagStrings = new HashMap<>();
-
- private void parseSLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("s");
- BitSet flags = new BitSet(flagIndexes.size());
- for (int i = 1; i < parts.length; i++) {
- String flag = parts[i];
- if (!flagIndexes.containsKey(flag)) {
- flagStrings.put(flagIndexes.size(), flag);
- flagIndexes.put(flag, flagIndexes.size());
- }
- flags.set(flagIndexes.get(flag));
- }
- this.flags = flags;
- }
-
- private void parseVLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("v");
- String noOptLine = line;
- if (noOptLine.startsWith("opt ")) {
- noOptLine = noOptLine.substring(4);
- }
- if (noOptLine.length() < 3) {
- throw new DescriptorParseException("Invalid line '" + line + "' in "
- + "status entry.");
- } else {
- this.version = noOptLine.substring(2);
- }
- }
-
- private void parseWLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("w");
- SortedMap<String, Integer> pairs =
- ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "=");
- if (pairs.isEmpty()) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- if (pairs.containsKey("Bandwidth")) {
- this.bandwidth = pairs.remove("Bandwidth");
- }
- if (pairs.containsKey("Measured")) {
- this.measured = pairs.remove("Measured");
- }
- if (pairs.containsKey("Unmeasured")) {
- this.unmeasured = pairs.remove("Unmeasured") == 1L;
- }
- if (!pairs.isEmpty()) {
- /* Ignore unknown key-value pair. */
- }
- }
-
- private void parsePLine(String line, String[] parts)
- throws DescriptorParseException {
- this.parsedAtMostOnceKeyword("p");
- boolean isValid = true;
- if (parts.length != 3) {
- isValid = false;
- } else {
- switch (parts[1]) {
- case "accept":
- case "reject":
- this.defaultPolicy = parts[1];
- this.portList = parts[2];
- String[] ports = parts[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseMLine(String line, String[] parts)
- throws DescriptorParseException {
- if (this.microdescriptorDigests == null) {
- this.microdescriptorDigests = new HashSet<>();
- }
- if (parts.length == 2) {
- this.microdescriptorDigests.add(
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[1]));
- } else if (parts.length == 3 && parts[2].length() > 7) {
- /* 7 == "sha256=".length() */
- this.microdescriptorDigests.add(
- ParseHelper.parseThirtyTwoByteBase64String(line,
- parts[2].substring(7)));
- }
- }
-
- private void parseIdLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3 || !"ed25519".equals(parts[1])) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else if ("none".equals(parts[2])) {
- this.masterKeyEd25519 = "none";
- } else {
- ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
- this.masterKeyEd25519 = parts[2];
- }
- }
-
- private void clearAtMostOnceKeywords() {
- this.atMostOnceKeywords = null;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String descriptor;
- @Override
- public String getDescriptor() {
- return this.descriptor;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private Set<String> microdescriptorDigests;
- @Override
- public Set<String> getMicrodescriptorDigests() {
- return this.microdescriptorDigests == null ? null :
- new HashSet<>(this.microdescriptorDigests);
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private BitSet flags;
- @Override
- public SortedSet<String> getFlags() {
- SortedSet<String> result = new TreeSet<>();
- if (this.flags != null) {
- for (int i = this.flags.nextSetBit(0); i >= 0;
- i = this.flags.nextSetBit(i + 1)) {
- result.add(flagStrings.get(i));
- }
- }
- return result;
- }
-
- private String version;
- @Override
- public String getVersion() {
- return this.version;
- }
-
- private long bandwidth = -1L;
- @Override
- public long getBandwidth() {
- return this.bandwidth;
- }
-
- private long measured = -1L;
- @Override
- public long getMeasured() {
- return this.measured;
- }
-
- private boolean unmeasured = false;
- @Override
- public boolean getUnmeasured() {
- return this.unmeasured;
- }
-
- private String defaultPolicy;
- @Override
- public String getDefaultPolicy() {
- return this.defaultPolicy;
- }
-
- private String portList;
- @Override
- public String getPortList() {
- return this.portList;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
deleted file mode 100644
index 5fa22c7..0000000
--- a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.DirSourceEntry;
-import org.torproject.descriptor.DirectorySignature;
-import org.torproject.descriptor.NetworkStatusEntry;
-
-/* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
- * consensuses, v2 statuses, and sanitized bridge network statuses and
- * delegate the specific parts to the subclasses. */
-public abstract class NetworkStatusImpl extends DescriptorImpl {
-
- protected NetworkStatusImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines,
- boolean containsDirSourceEntries, boolean blankLinesAllowed)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines,
- blankLinesAllowed);
- this.splitAndParseParts(this.rawDescriptorBytes,
- containsDirSourceEntries);
- }
-
- private void splitAndParseParts(byte[] rawDescriptorBytes,
- boolean containsDirSourceEntries) throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- int startIndex = 0;
- int firstDirSourceIndex = !containsDirSourceEntries ? -1 :
- this.findFirstIndexOfKeyword(descriptorString, "dir-source");
- int firstRIndex = this.findFirstIndexOfKeyword(descriptorString, "r");
- int directoryFooterIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-footer");
- int firstDirectorySignatureIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-signature");
- int endIndex = descriptorString.length();
- if (firstDirectorySignatureIndex < 0) {
- firstDirectorySignatureIndex = endIndex;
- }
- if (directoryFooterIndex < 0) {
- directoryFooterIndex = firstDirectorySignatureIndex;
- }
- if (firstRIndex < 0) {
- firstRIndex = directoryFooterIndex;
- }
- if (firstDirSourceIndex < 0) {
- firstDirSourceIndex = firstRIndex;
- }
- if (firstDirSourceIndex > startIndex) {
- this.parseHeaderBytes(descriptorString, startIndex,
- firstDirSourceIndex);
- }
- if (firstRIndex > firstDirSourceIndex) {
- this.parseDirSourceBytes(descriptorString, firstDirSourceIndex,
- firstRIndex);
- }
- if (directoryFooterIndex > firstRIndex) {
- this.parseStatusEntryBytes(descriptorString, firstRIndex,
- directoryFooterIndex);
- }
- if (firstDirectorySignatureIndex > directoryFooterIndex) {
- this.parseDirectoryFooterBytes(descriptorString,
- directoryFooterIndex, firstDirectorySignatureIndex);
- }
- if (endIndex > firstDirectorySignatureIndex) {
- this.parseDirectorySignatureBytes(descriptorString,
- firstDirectorySignatureIndex, endIndex);
- }
- }
-
- private int findFirstIndexOfKeyword(String descriptorString,
- 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 {
- return -1;
- }
- }
-
- private void parseHeaderBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- byte[] headerBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- headerBytes, 0, end - start);
- this.parseHeader(headerBytes);
- }
-
- private void parseDirSourceBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- List<byte[]> splitDirSourceBytes =
- this.splitByKeyword(descriptorString, "dir-source", start, end);
- for (byte[] dirSourceBytes : splitDirSourceBytes) {
- this.parseDirSource(dirSourceBytes);
- }
- }
-
- private void parseStatusEntryBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- List<byte[]> splitStatusEntryBytes =
- this.splitByKeyword(descriptorString, "r", start, end);
- for (byte[] statusEntryBytes : splitStatusEntryBytes) {
- this.parseStatusEntry(statusEntryBytes);
- }
- }
-
- private void parseDirectoryFooterBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- byte[] directoryFooterBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- directoryFooterBytes, 0, end - start);
- this.parseFooter(directoryFooterBytes);
- }
-
- private void parseDirectorySignatureBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
- descriptorString, "directory-signature", start, end);
- for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
- this.parseDirectorySignature(directorySignatureBytes);
- }
- }
-
- private List<byte[]> splitByKeyword(String descriptorString,
- String keyword, int start, int end) {
- List<byte[]> splitParts = new ArrayList<>();
- int from = start;
- while (from < end) {
- int to = descriptorString.indexOf("\n" + keyword + " ", from);
- if (to < 0) {
- to = descriptorString.indexOf("\n" + keyword + "\n", from);
- }
- if (to < 0) {
- to = end;
- } else {
- to += 1;
- }
- byte[] part = new byte[to - from];
- System.arraycopy(this.rawDescriptorBytes, from, part, 0,
- to - from);
- from = to;
- splitParts.add(part);
- }
- return splitParts;
- }
-
- protected abstract void parseHeader(byte[] headerBytes)
- throws DescriptorParseException;
-
- protected void parseDirSource(byte[] dirSourceBytes)
- throws DescriptorParseException {
- DirSourceEntryImpl dirSourceEntry = new DirSourceEntryImpl(
- dirSourceBytes, this.failUnrecognizedDescriptorLines);
- this.dirSourceEntries.put(dirSourceEntry.getIdentity(),
- dirSourceEntry);
- List<String> unrecognizedDirSourceLines = dirSourceEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedDirSourceLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedDirSourceLines);
- }
- }
-
- protected String[] parseClientOrServerVersions(String line,
- String[] parts) throws DescriptorParseException {
- String[] result = null;
- switch (parts.length) {
- case 1:
- result = new String[0];
- break;
- case 2:
- result = parts[1].split(",", -1);
- for (String version : result) {
- if (version.length() < 1) {
- throw new DescriptorParseException("Illegal versions line '"
- + line + "'.");
- }
- }
- break;
- default:
- throw new DescriptorParseException("Illegal versions line '" + line
- + "'.");
- }
- return result;
- }
-
- protected void parseStatusEntry(byte[] statusEntryBytes)
- throws DescriptorParseException {
- NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
- statusEntryBytes, false, this.failUnrecognizedDescriptorLines);
- this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected abstract void parseFooter(byte[] footerBytes)
- throws DescriptorParseException;
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- if (this.signatures == null) {
- this.signatures = new ArrayList<>();
- }
- DirectorySignatureImpl signature = new DirectorySignatureImpl(
- directorySignatureBytes, failUnrecognizedDescriptorLines);
- this.signatures.add(signature);
- List<String> unrecognizedStatusEntryLines = signature.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected SortedMap<String, DirSourceEntry> dirSourceEntries =
- new TreeMap<>();
- public SortedMap<String, DirSourceEntry> getDirSourceEntries() {
- return new TreeMap<>(this.dirSourceEntries);
- }
-
- protected SortedMap<String, NetworkStatusEntry> statusEntries =
- new TreeMap<>();
- public SortedMap<String, NetworkStatusEntry> getStatusEntries() {
- return new TreeMap<>(this.statusEntries);
- }
- public boolean containsStatusEntry(String fingerprint) {
- return this.statusEntries.containsKey(fingerprint);
- }
- public NetworkStatusEntry getStatusEntry(String fingerprint) {
- return this.statusEntries.get(fingerprint);
- }
-
- protected List<DirectorySignature> signatures;
- public List<DirectorySignature> getSignatures() {
- return this.signatures == null ? null
- : new ArrayList<>(this.signatures);
- }
- public SortedMap<String, DirectorySignature> getDirectorySignatures() {
- SortedMap<String, DirectorySignature> directorySignatures = null;
- if (this.signatures != null) {
- directorySignatures = new TreeMap<>();
- for (DirectorySignature signature : this.signatures) {
- directorySignatures.put(signature.getIdentity(), signature);
- }
- }
- return directorySignatures;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
deleted file mode 100644
index 82c0813..0000000
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.regex.Pattern;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-
-public class ParseHelper {
-
- private static Pattern keywordPattern =
- Pattern.compile("^[A-Za-z0-9-]+$");
- protected static String parseKeyword(String line, String keyword)
- throws DescriptorParseException {
- if (!keywordPattern.matcher(keyword).matches()) {
- throw new DescriptorParseException("Unrecognized character in "
- + "keyword '" + keyword + "' in line '" + line + "'.");
- }
- return keyword;
- }
-
- private static Pattern ipv4Pattern =
- Pattern.compile("^[0-9\\.]{7,15}$");
- protected static String parseIpv4Address(String line, String address)
- throws DescriptorParseException {
- boolean isValid = true;
- if (!ipv4Pattern.matcher(address).matches()) {
- isValid = false;
- } else {
- String[] parts = address.split("\\.", -1);
- if (parts.length != 4) {
- isValid = false;
- } else {
- for (int i = 0; i < 4; i++) {
- try {
- int octetValue = Integer.parseInt(parts[i]);
- if (octetValue < 0 || octetValue > 255) {
- isValid = false;
- }
- } catch (NumberFormatException e) {
- isValid = false;
- }
- }
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("'" + address + "' in line '"
- + line + "' is not a valid IPv4 address.");
- }
- return address;
- }
-
- protected static int parsePort(String line, String portString)
- throws DescriptorParseException {
- int port = -1;
- try {
- port = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("'" + portString + "' in line '"
- + line + "' is not a valid port number.");
- }
- if (port < 0 || port > 65535) {
- throw new DescriptorParseException("'" + portString + "' in line '"
- + line + "' is not a valid port number.");
- }
- return port;
- }
-
- protected static long parseSeconds(String line, String secondsString)
- throws DescriptorParseException {
- try {
- return Long.parseLong(secondsString);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("'" + secondsString + "' in "
- + "line '" + line + "' is not a valid time in seconds.");
- }
- }
-
- protected static String parseExitPattern(String line, String exitPattern)
- throws DescriptorParseException {
- if (!exitPattern.contains(":")) {
- throw new DescriptorParseException("'" + exitPattern + "' in line '"
- + line + "' must contain address and port.");
- }
- String[] parts = exitPattern.split(":");
- String addressPart = parts[0];
- /* TODO Extend to IPv6. */
- if (addressPart.equals("*")) {
- /* Nothing to check. */
- } else if (addressPart.contains("/")) {
- String[] addressParts = addressPart.split("/");
- String address = addressParts[0];
- String mask = addressParts[1];
- ParseHelper.parseIpv4Address(line, address);
- if (addressParts.length != 2) {
- throw new DescriptorParseException("'" + addressPart + "' in "
- + "line '" + line + "' is not a valid address part.");
- }
- if (mask.contains(".")) {
- ParseHelper.parseIpv4Address(line, mask);
- } else {
- int maskValue = -1;
- try {
- maskValue = Integer.parseInt(mask);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (maskValue < 0 || maskValue > 32) {
- throw new DescriptorParseException("'" + mask + "' in line '"
- + line + "' is not a valid IPv4 mask.");
- }
- }
- } else {
- ParseHelper.parseIpv4Address(line, addressPart);
- }
- String portPart = parts[1];
- if (portPart.equals("*")) {
- /* Nothing to check. */
- } else if (portPart.contains("-")) {
- String[] portParts = portPart.split("-");
- String fromPort = portParts[0];
- ParseHelper.parsePort(line, fromPort);
- String toPort = portParts[1];
- ParseHelper.parsePort(line, toPort);
- } else {
- ParseHelper.parsePort(line, portPart);
- }
- return exitPattern;
- }
-
- private static ThreadLocal<Map<String, DateFormat>> dateFormats =
- new ThreadLocal<Map<String, DateFormat>> () {
- public Map<String, DateFormat> get() {
- return super.get();
- }
- protected Map<String, DateFormat> initialValue() {
- return new HashMap<>();
- }
- public void remove() {
- super.remove();
- }
- public void set(Map<String, DateFormat> value) {
- super.set(value);
- }
- };
- static DateFormat getDateFormat(String format) {
- Map<String, DateFormat> threadDateFormats = dateFormats.get();
- if (!threadDateFormats.containsKey(format)) {
- DateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
- dateFormat.setLenient(false);
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- threadDateFormats.put(format, dateFormat);
- }
- return threadDateFormats.get(format);
- }
-
- protected static long parseTimestampAtIndex(String line, String[] parts,
- int dateIndex, int timeIndex) throws DescriptorParseException {
- if (dateIndex >= parts.length || timeIndex >= parts.length) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a timestamp at the expected position.");
- }
- long result = -1L;
- try {
- DateFormat dateTimeFormat = getDateFormat("yyyy-MM-dd HH:mm:ss");
- result = dateTimeFormat.parse(
- parts[dateIndex] + " " + parts[timeIndex]).getTime();
- } catch (ParseException e) {
- /* Leave result at -1L. */
- }
- if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
- throw new DescriptorParseException("Illegal timestamp format in "
- + "line '" + line + "'.");
- }
- return result;
- }
-
- protected static long parseDateAtIndex(String line, String[] parts,
- int dateIndex) throws DescriptorParseException {
- if (dateIndex >= parts.length) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a date at the expected position.");
- }
- long result = -1L;
- try {
- DateFormat dateFormat = getDateFormat("yyyy-MM-dd");
- result = dateFormat.parse(parts[dateIndex]).getTime();
- } catch (ParseException e) {
- /* Leave result at -1L. */
- }
- if (result < 0L || result / 1000L > (long) Integer.MAX_VALUE) {
- throw new DescriptorParseException("Illegal date format in line '"
- + line + "'.");
- }
- return result;
- }
-
- protected static String parseTwentyByteHexString(String line,
- String hexString) throws DescriptorParseException {
- return parseHexString(line, hexString, 40);
- }
-
- protected static String parseHexString(String line, String hexString)
- throws DescriptorParseException {
- return parseHexString(line, hexString, -1);
- }
-
- private static Pattern hexPattern = Pattern.compile("^[0-9a-fA-F]*$");
- private static String parseHexString(String line, String hexString,
- int expectedLength) throws DescriptorParseException {
- if (!hexPattern.matcher(hexString).matches() ||
- hexString.length() % 2 != 0 ||
- (expectedLength >= 0 && hexString.length() != expectedLength)) {
- throw new DescriptorParseException("Illegal hex string in line '"
- + line + "'.");
- }
- return hexString.toUpperCase();
- }
-
- protected static SortedMap<String, String> parseKeyValueStringPairs(
- String line, String[] parts, int startIndex, String separatorString)
- throws DescriptorParseException {
- SortedMap<String, String> result = new TreeMap<>();
- for (int i = startIndex; i < parts.length; i++) {
- String pair = parts[i];
- String[] pairParts = pair.split(separatorString);
- if (pairParts.length != 2) {
- throw new DescriptorParseException("Illegal key-value pair in "
- + "line '" + line + "'.");
- }
- result.put(pairParts[0], pairParts[1]);
- }
- return result;
- }
-
- protected static SortedMap<String, Integer> parseKeyValueIntegerPairs(
- String line, String[] parts, int startIndex, String separatorString)
- throws DescriptorParseException {
- SortedMap<String, Integer> result = new TreeMap<>();
- SortedMap<String, String> keyValueStringPairs =
- ParseHelper.parseKeyValueStringPairs(line, parts, startIndex,
- separatorString);
- for (Map.Entry<String, String> e : keyValueStringPairs.entrySet()) {
- try {
- result.put(e.getKey(), Integer.parseInt(e.getValue()));
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
- return result;
- }
-
- private static Pattern nicknamePattern =
- Pattern.compile("^[0-9a-zA-Z]{1,19}$");
- protected static String parseNickname(String line, String nickname)
- throws DescriptorParseException {
- if (!nicknamePattern.matcher(nickname).matches()) {
- throw new DescriptorParseException("Illegal nickname in line '"
- + line + "'.");
- }
- return nickname;
- }
-
- protected static boolean parseBoolean(String b, String line)
- throws DescriptorParseException {
- switch (b) {
- case "1":
- return true;
- case "0":
- return false;
- default:
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private static Pattern twentyByteBase64Pattern =
- Pattern.compile("^[0-9a-zA-Z+/]{27}$");
- protected static String parseTwentyByteBase64String(String line,
- String base64String) throws DescriptorParseException {
- if (!twentyByteBase64Pattern.matcher(base64String).matches()) {
- throw new DescriptorParseException("'" + base64String
- + "' in line '" + line + "' is not a valid base64-encoded "
- + "20-byte value.");
- }
- return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
- }
-
- private static Pattern thirtyTwoByteBase64Pattern =
- Pattern.compile("^[0-9a-zA-Z+/]{43}$");
- protected static String parseThirtyTwoByteBase64String(String line,
- String base64String) throws DescriptorParseException {
- if (!thirtyTwoByteBase64Pattern.matcher(base64String).matches()) {
- throw new DescriptorParseException("'" + base64String
- + "' in line '" + line + "' is not a valid base64-encoded "
- + "32-byte value.");
- }
- return DatatypeConverter.printHexBinary(
- DatatypeConverter.parseBase64Binary(base64String + "=")).
- toUpperCase();
- }
-
- private static Map<Integer, Pattern>
- commaSeparatedKeyValueListPatterns = new HashMap<>();
- protected static String parseCommaSeparatedKeyIntegerValueList(
- String line, String[] partsNoOpt, int index, int keyLength)
- throws DescriptorParseException {
- String result = "";
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a key-value list at index " + index + ".");
- } else if (partsNoOpt.length > index + 1 ) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected key-value list at "
- + "index " + index + ".");
- } else if (partsNoOpt.length > index) {
- if (!commaSeparatedKeyValueListPatterns.containsKey(keyLength)) {
- String keyPattern = "[0-9a-zA-Z?<>\\-_]"
- + (keyLength == 0 ? "+" : "{" + keyLength + "}");
- String valuePattern = "\\-?[0-9]{1,9}";
- String patternString = String.format("^%s=%s(,%s=%s)*$",
- keyPattern, valuePattern, keyPattern, valuePattern);
- commaSeparatedKeyValueListPatterns.put(keyLength,
- Pattern.compile(patternString));
- }
- Pattern pattern = commaSeparatedKeyValueListPatterns.get(
- keyLength);
- if (pattern.matcher(partsNoOpt[index]).matches()) {
- result = partsNoOpt[index];
- } else {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal key or value.");
- }
- }
- return result;
- }
-
- protected static SortedMap<String, Integer>
- convertCommaSeparatedKeyIntegerValueList(String validatedString) {
- SortedMap<String, Integer> result = null;
- if (validatedString != null) {
- result = new TreeMap<>();
- if (validatedString.contains("=")) {
- for (String listElement : validatedString.split(",", -1)) {
- String[] keyAndValue = listElement.split("=");
- result.put(keyAndValue[0], Integer.parseInt(keyAndValue[1]));
- }
- }
- }
- return result;
- }
-
- protected static SortedMap<String, Long>
- parseCommaSeparatedKeyLongValueList(String line,
- String[] partsNoOpt, int index, int keyLength)
- throws DescriptorParseException {
- SortedMap<String, Long> result = new TreeMap<>();
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a key-value list at index " + index + ".");
- } else if (partsNoOpt.length > index + 1 ) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected key-value list at "
- + "index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- for (String listElement : listElements) {
- String[] keyAndValue = listElement.split("=");
- String key = null;
- long value = -1;
- if (keyAndValue.length == 2 && (keyLength == 0 ||
- keyAndValue[0].length() == keyLength)) {
- 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 Integer[] parseCommaSeparatedIntegerValueList(
- String line, String[] partsNoOpt, int index)
- throws DescriptorParseException {
- Integer[] result = null;
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a comma-separated value list at index " + index
- + ".");
- } else if (partsNoOpt.length > index + 1) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected comma-separated "
- + "value list at index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- result = new Integer[listElements.length];
- for (int i = 0; i < listElements.length; i++) {
- try {
- result[i] = Integer.parseInt(listElements[i]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal value in list element '"
- + listElements[i] + "'.");
- }
- }
- }
- return result;
- }
-
- protected static Double[] parseCommaSeparatedDoubleValueList(
- String line, String[] partsNoOpt, int index)
- throws DescriptorParseException {
- Double[] result = null;
- if (partsNoOpt.length < index) {
- throw new DescriptorParseException("Line '" + line + "' does not "
- + "contain a comma-separated value list at index " + index
- + ".");
- } else if (partsNoOpt.length > index + 1) {
- throw new DescriptorParseException("Line '" + line + "' contains "
- + "unrecognized values beyond the expected comma-separated "
- + "value list at index " + index + ".");
- } else if (partsNoOpt.length > index) {
- String[] listElements = partsNoOpt[index].split(",", -1);
- result = new Double[listElements.length];
- for (int i = 0; i < listElements.length; i++) {
- try {
- result[i] = Double.parseDouble(listElements[i]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Line '" + line + "' "
- + "contains an illegal value in list element '"
- + listElements[i] + "'.");
- }
- }
- }
- return result;
- }
-
- protected static Map<String, Double>
- parseSpaceSeparatedStringKeyDoubleValueMap(String line,
- String[] partsNoOpt, int startIndex)
- throws DescriptorParseException {
- Map<String, Double> 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;
- Double value = null;
- if (keyAndValue.length == 2) {
- try {
- value = Double.parseDouble(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 {
- String identityEd25519CryptoBlockNoNewlines =
- identityEd25519CryptoBlock.replaceAll("\n", "");
- String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
- endEd25519CertLine = "-----END ED25519 CERT-----";
- if (!identityEd25519CryptoBlockNoNewlines.startsWith(
- beginEd25519CertLine)) {
- throw new DescriptorParseException("Illegal start of "
- + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
- + "'.");
- }
- if (!identityEd25519CryptoBlockNoNewlines.endsWith(
- endEd25519CertLine)) {
- throw new DescriptorParseException("Illegal end of "
- + "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
- + "'.");
- }
- String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
- substring(beginEd25519CertLine.length(),
- identityEd25519CryptoBlock.length()
- - endEd25519CertLine.length()).replaceAll("=", "");
- byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
- identityEd25519Base64);
- if (identityEd25519.length < 40) {
- throw new DescriptorParseException("Invalid length of "
- + "identity-ed25519 (in bytes): " + identityEd25519.length);
- } else if (identityEd25519[0] != 0x01) {
- throw new DescriptorParseException("Unknown version in "
- + "identity-ed25519: " + identityEd25519[0]);
- } else if (identityEd25519[1] != 0x04) {
- throw new DescriptorParseException("Unknown cert type in "
- + "identity-ed25519: " + identityEd25519[1]);
- } else if (identityEd25519[6] != 0x01) {
- throw new DescriptorParseException("Unknown certified key type in "
- + "identity-ed25519: " + identityEd25519[1]);
- } else if (identityEd25519[39] == 0x00) {
- throw new DescriptorParseException("No extensions in "
- + "identity-ed25519 (which would contain the encoded "
- + "master-key-ed25519): " + identityEd25519[39]);
- } else {
- int extensionStart = 40;
- for (int i = 0; i < (int) identityEd25519[39]; i++) {
- if (identityEd25519.length < extensionStart + 4) {
- throw new DescriptorParseException("Invalid extension with id "
- + i + " in identity-ed25519.");
- }
- int extensionLength = identityEd25519[extensionStart];
- extensionLength <<= 8;
- extensionLength += identityEd25519[extensionStart + 1];
- int extensionType = identityEd25519[extensionStart + 2];
- if (extensionLength == 32 && extensionType == 4) {
- if (identityEd25519.length < extensionStart + 4 + 32) {
- throw new DescriptorParseException("Invalid extension with "
- + "id " + i + " in identity-ed25519.");
- }
- byte[] masterKeyEd25519 = new byte[32];
- System.arraycopy(identityEd25519, extensionStart + 4,
- masterKeyEd25519, 0, masterKeyEd25519.length);
- String masterKeyEd25519Base64 = DatatypeConverter.
- printBase64Binary(masterKeyEd25519).replaceAll("=", "");
- String masterKeyEd25519Base64NoTrailingEqualSigns =
- masterKeyEd25519Base64.replaceAll("=", "");
- return masterKeyEd25519Base64NoTrailingEqualSigns;
- }
- extensionStart += 4 + extensionLength;
- }
- }
- throw new DescriptorParseException("Unable to locate "
- + "master-key-ed25519 in identity-ed25519.");
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java
deleted file mode 100644
index 1ff15cb..0000000
--- a/src/org/torproject/descriptor/impl/RelayDirectoryImpl.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayDirectory;
-import org.torproject.descriptor.RouterStatusEntry;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* TODO Write unit tests. */
-
-public class RelayDirectoryImpl extends DescriptorImpl
- implements RelayDirectory {
-
- protected static List<RelayDirectory> parseDirectories(
- byte[] directoriesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayDirectory> parsedDirectories = new ArrayList<>();
- List<byte[]> splitDirectoriesBytes =
- DescriptorImpl.splitRawDescriptorBytes(directoriesBytes,
- "signed-directory\n");
- for (byte[] directoryBytes : splitDirectoriesBytes) {
- RelayDirectory parsedDirectory =
- new RelayDirectoryImpl(directoryBytes,
- failUnrecognizedDescriptorLines);
- parsedDirectories.add(parsedDirectory);
- }
- return parsedDirectories;
- }
-
- protected RelayDirectoryImpl(byte[] directoryBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- 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();
- }
-
- private void calculateDigest() throws DescriptorParseException {
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "signed-directory\n";
- String sigToken = "\ndirectory-signature ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- sig = ascii.indexOf("\n", sig) + 1;
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.directoryDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.directoryDigest == null) {
- throw new DescriptorParseException("Could not calculate v1 "
- + "directory digest.");
- }
- }
-
- private void splitAndParseParts(byte[] rawDescriptorBytes)
- throws DescriptorParseException {
- if (this.rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- String descriptorString = new String(rawDescriptorBytes);
- int startIndex = 0;
- int firstRouterIndex = this.findFirstIndexOfKeyword(descriptorString,
- "router");
- int directorySignatureIndex = this.findFirstIndexOfKeyword(
- descriptorString, "directory-signature");
- int endIndex = descriptorString.length();
- if (directorySignatureIndex < 0) {
- directorySignatureIndex = endIndex;
- }
- if (firstRouterIndex < 0) {
- firstRouterIndex = directorySignatureIndex;
- }
- if (firstRouterIndex > startIndex) {
- this.parseHeaderBytes(descriptorString, startIndex,
- firstRouterIndex);
- }
- if (directorySignatureIndex > firstRouterIndex) {
- this.parseServerDescriptorBytes(descriptorString, firstRouterIndex,
- directorySignatureIndex);
- }
- if (endIndex > directorySignatureIndex) {
- this.parseDirectorySignatureBytes(descriptorString,
- directorySignatureIndex, endIndex);
- }
- }
-
- private int findFirstIndexOfKeyword(String descriptorString,
- 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 {
- return -1;
- }
- }
-
- private void parseHeaderBytes(String descriptorString, int start,
- int end) throws DescriptorParseException {
- byte[] headerBytes = new byte[end - start];
- System.arraycopy(this.rawDescriptorBytes, start,
- headerBytes, 0, end - start);
- this.parseHeader(headerBytes);
- }
-
- private void parseServerDescriptorBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitServerDescriptorBytes =
- this.splitByKeyword(descriptorString, "router", start, end);
- for (byte[] statusEntryBytes : splitServerDescriptorBytes) {
- this.parseServerDescriptor(statusEntryBytes);
- }
- }
-
- private void parseDirectorySignatureBytes(String descriptorString,
- int start, int end) throws DescriptorParseException {
- List<byte[]> splitDirectorySignatureBytes = this.splitByKeyword(
- descriptorString, "directory-signature", start, end);
- for (byte[] directorySignatureBytes : splitDirectorySignatureBytes) {
- this.parseDirectorySignature(directorySignatureBytes);
- }
- }
-
- private List<byte[]> splitByKeyword(String descriptorString,
- String keyword, int start, int end) {
- List<byte[]> splitParts = new ArrayList<>();
- int from = start;
- while (from < end) {
- int to = descriptorString.indexOf("\n" + keyword + " ", from);
- if (to < 0) {
- to = descriptorString.indexOf("\n" + keyword + "\n", from);
- }
- if (to < 0) {
- to = end;
- } else {
- to += 1;
- }
- int toNoNewline = to;
- while (toNoNewline > from &&
- descriptorString.charAt(toNoNewline - 1) == '\n') {
- toNoNewline--;
- }
- byte[] part = new byte[toNoNewline - from];
- System.arraycopy(this.rawDescriptorBytes, from, part, 0,
- toNoNewline - from);
- from = to;
- splitParts.add(part);
- }
- return splitParts;
- }
-
- private void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String publishedLine = null, nextCrypto = "",
- runningRoutersLine = null, routerStatusLine = null;
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.isEmpty() || line.startsWith("@")) {
- continue;
- }
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "signed-directory":
- this.parseSignedDirectoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- if (publishedLine != null) {
- throw new DescriptorParseException("Keyword 'published' is "
- + "contained more than once, but must be contained exactly "
- + "once.");
- } else {
- publishedLine = line;
- }
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "dir-signing-key";
- break;
- case "recommended-software":
- this.parseRecommendedSoftwareLine(line, lineNoOpt, partsNoOpt);
- break;
- case "running-routers":
- runningRoutersLine = line;
- break;
- case "router-status":
- routerStatusLine = line;
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key") &&
- this.dirSigningKey == null) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v1 directory.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in v1 directory.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- if (publishedLine == null) {
- 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[] publishedPartsNoOpt = publishedLineNoOpt.split("[ \t]+");
- this.parsePublishedLine(publishedLine, publishedLineNoOpt,
- publishedPartsNoOpt);
- }
- if (routerStatusLine != null) {
- String routerStatusLineNoOpt = routerStatusLine.startsWith("opt ") ?
- routerStatusLine.substring("opt ".length()) : routerStatusLine;
- String[] routerStatusPartsNoOpt =
- routerStatusLineNoOpt.split("[ \t]+");
- this.parseRouterStatusLine(routerStatusLine, routerStatusLineNoOpt,
- routerStatusPartsNoOpt);
- } else if (runningRoutersLine != null) {
- String runningRoutersLineNoOpt =
- runningRoutersLine.startsWith("opt ") ?
- runningRoutersLine.substring("opt ".length()) :
- runningRoutersLine;
- String[] runningRoutersPartsNoOpt =
- runningRoutersLineNoOpt.split("[ \t]+");
- this.parseRunningRoutersLine(runningRoutersLine,
- runningRoutersLineNoOpt, runningRoutersPartsNoOpt);
- } else {
- throw new DescriptorParseException("Either running-routers or "
- + "router-status line must be given.");
- }
- }
-
- protected void parseServerDescriptor(byte[] serverDescriptorBytes) {
- try {
- ServerDescriptorImpl serverDescriptor =
- new RelayServerDescriptorImpl(serverDescriptorBytes,
- this.failUnrecognizedDescriptorLines);
- this.serverDescriptors.add(serverDescriptor);
- } catch (DescriptorParseException e) {
- this.serverDescriptorParseExceptions.add(e);
- }
- }
-
- private void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseSignedDirectoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("signed-directory")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseDirSigningKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length > 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- } else if (partsNoOpt.length == 2) {
- /* Early directories didn't have a crypto object following the
- * "dir-signing-key" line, but had the key base64-encoded in the
- * same line. */
- StringBuilder sb = new StringBuilder();
- sb.append("-----BEGIN RSA PUBLIC KEY-----\n");
- String keyString = partsNoOpt[1];
- while (keyString.length() > 64) {
- sb.append(keyString.substring(0, 64)).append("\n");
- keyString = keyString.substring(64);
- }
- if (keyString.length() > 0) {
- sb.append(keyString).append("\n");
- }
- sb.append("-----END RSA PUBLIC KEY-----\n");
- this.dirSigningKey = sb.toString();
- }
- }
-
- private void parseRecommendedSoftwareLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- List<String> result = new ArrayList<>();
- if (partsNoOpt.length > 2) {
- throw new DescriptorParseException("Illegal versions line '" + line
- + "'.");
- } else if (partsNoOpt.length == 2) {
- String[] versions = partsNoOpt[1].split(",", -1);
- for (int i = 0; i < versions.length; i++) {
- String version = versions[i];
- if (version.length() < 1) {
- throw new DescriptorParseException("Illegal versions line '"
- + line + "'.");
- }
- result.add(version);
- }
- }
- this.recommendedSoftware = result;
- }
-
- private void parseRunningRoutersLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- for (int i = 1; i < partsNoOpt.length; i++) {
- String part = partsNoOpt[i];
- String debugLine = "running-routers [...] " + part + " [...]";
- boolean isLive = true;
- if (part.startsWith("!")) {
- isLive = false;
- part = part.substring(1);
- }
- boolean isVerified;
- String fingerprint = null, nickname = null;
- if (part.startsWith("$")) {
- isVerified = false;
- fingerprint = ParseHelper.parseTwentyByteHexString(debugLine,
- part.substring(1));
- } else {
- isVerified = true;
- nickname = ParseHelper.parseNickname(debugLine, part);
- }
- this.statusEntries.add(new RouterStatusEntryImpl(fingerprint,
- nickname, isLive, isVerified));
- }
- }
-
- private void parseRouterStatusLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- for (int i = 1; i < partsNoOpt.length; i++) {
- String part = partsNoOpt[i];
- String debugLine = "router-status [...] " + part + " [...]";
- RouterStatusEntry entry = null;
- if (part.contains("=")) {
- String[] partParts = part.split("=");
- if (partParts.length == 2) {
- boolean isVerified = true, isLive;
- String nickname;
- if (partParts[0].startsWith("!")) {
- isLive = false;
- nickname = ParseHelper.parseNickname(debugLine,
- partParts[0].substring(1));
- } else {
- isLive = true;
- nickname = ParseHelper.parseNickname(debugLine, partParts[0]);
- }
- String fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, partParts[1].substring(1));
- entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
- isVerified);
- }
- } else {
- boolean isVerified = false, isLive;
- String nickname = null, fingerprint;
- if (part.startsWith("!")) {
- isLive = false;
- fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, part.substring(2));
- } else {
- isLive = true;
- fingerprint = ParseHelper.parseTwentyByteHexString(
- debugLine, part.substring(1));;
- }
- entry = new RouterStatusEntryImpl(fingerprint, nickname, isLive,
- isVerified);
- }
- if (entry == null) {
- throw new DescriptorParseException("Illegal router-status entry '"
- + part + "' in v1 directory.");
- }
- this.statusEntries.add(entry);
- }
- }
-
- private void parseDirectorySignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private List<String> recommendedSoftware;
- @Override
- public List<String> getRecommendedSoftware() {
- return this.recommendedSoftware == null ? null :
- new ArrayList<>(this.recommendedSoftware);
- }
-
- private String directorySignature;
- @Override
- public String getDirectorySignature() {
- return this.directorySignature;
- }
-
- private List<RouterStatusEntry> statusEntries = new ArrayList<>();
- @Override
- public List<RouterStatusEntry> getRouterStatusEntries() {
- return new ArrayList<>(this.statusEntries);
- }
-
- private List<ServerDescriptor> serverDescriptors = new ArrayList<>();
- @Override
- public List<ServerDescriptor> getServerDescriptors() {
- return new ArrayList<>(this.serverDescriptors);
- }
-
- private List<Exception> serverDescriptorParseExceptions =
- new ArrayList<>();
- @Override
- public List<Exception> getServerDescriptorParseExceptions() {
- return new ArrayList<>(this.serverDescriptorParseExceptions);
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String directoryDigest;
- @Override
- public String getDirectoryDigest() {
- return this.directoryDigest;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
deleted file mode 100644
index 73d4dfa..0000000
--- a/src/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.RelayExtraInfoDescriptor;
-
-public class RelayExtraInfoDescriptorImpl
- extends ExtraInfoDescriptorImpl implements RelayExtraInfoDescriptor {
-
- protected static List<ExtraInfoDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ExtraInfoDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "extra-info ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ExtraInfoDescriptor parsedDescriptor =
- new RelayExtraInfoDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected RelayExtraInfoDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
deleted file mode 100644
index fe045c1..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* Contains a network status consensus or microdesc consensus. */
-public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
- implements RelayNetworkStatusConsensus {
-
- protected static List<RelayNetworkStatusConsensus> parseConsensuses(
- byte[] consensusesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatusConsensus> parsedConsensuses =
- new ArrayList<>();
- List<byte[]> splitConsensusBytes =
- DescriptorImpl.splitRawDescriptorBytes(consensusesBytes,
- "network-status-version 3");
- for (byte[] consensusBytes : splitConsensusBytes) {
- RelayNetworkStatusConsensus parsedConsensus =
- new RelayNetworkStatusConsensusImpl(consensusBytes,
- failUnrecognizedDescriptorLines);
- parsedConsensuses.add(parsedConsensus);
- }
- return parsedConsensuses;
- }
-
- protected RelayNetworkStatusConsensusImpl(byte[] consensusBytes,
- 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,params,directory-footer,"
- + "bandwidth-weights").split(",")));
- this.checkAtMostOnceKeywords(atMostOnceKeywords);
- this.checkFirstKeyword("network-status-version");
- this.clearParsedKeywords();
- 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 ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.consensusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.consensusDigest == null) {
- throw new DescriptorParseException("Could not calculate consensus "
- + "digest.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-method":
- this.parseConsensusMethodLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private boolean microdescConsensus = false;
- protected void parseStatusEntry(byte[] statusEntryBytes)
- throws DescriptorParseException {
- NetworkStatusEntryImpl statusEntry = new NetworkStatusEntryImpl(
- statusEntryBytes, this.microdescConsensus,
- this.failUnrecognizedDescriptorLines);
- this.statusEntries.put(statusEntry.getFingerprint(), statusEntry);
- List<String> unrecognizedStatusEntryLines = statusEntry.
- getAndClearUnrecognizedLines();
- if (unrecognizedStatusEntryLines != null) {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "directory-footer":
- break;
- case "bandwidth-weights":
- this.parseBandwidthWeightsLine(line, parts);
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in consensus.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.startsWith("network-status-version 3")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 3;
- if (parts.length == 3) {
- this.consensusFlavor = parts[2];
- if (this.consensusFlavor.equals("microdesc")) {
- this.microdescConsensus = true;
- }
- } else if (parts.length != 2) {
- throw new DescriptorParseException("Illegal network status version "
- + "line '" + line + "'.");
- }
- }
-
- private void parseVoteStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || !parts[1].equals("consensus")) {
- throw new DescriptorParseException("Line '" + line + "' indicates "
- + "that this is not a consensus.");
- }
- }
-
- private void parseConsensusMethodLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in consensus.");
- }
- try {
- this.consensusMethod = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- if (this.consensusMethod < 1) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- }
-
- private void parseValidAfterLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFreshUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseVotingDelayLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- try {
- this.voteSeconds = Long.parseLong(parts[1]);
- this.distSeconds = Long.parseLong(parts[2]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePackageLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 5) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- if (this.packageLines == null) {
- this.packageLines = new ArrayList<>();
- }
- this.packageLines.add(line.substring("package ".length()));
- }
-
- private void parseKnownFlagsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No known flags in line '" + line
- + "'.");
- }
- String[] knownFlags = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- knownFlags[i - 1] = parts[i];
- }
- this.knownFlags = knownFlags;
- }
-
- private void parseParamsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private void parseBandwidthWeightsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private String consensusDigest;
- @Override
- public String getConsensusDigest() {
- return this.consensusDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private String consensusFlavor;
- @Override
- public String getConsensusFlavor() {
- return this.consensusFlavor;
- }
-
- private int consensusMethod;
- @Override
- public int getConsensusMethod() {
- return this.consensusMethod;
- }
-
- private long validAfterMillis;
- @Override
- public long getValidAfterMillis() {
- return this.validAfterMillis;
- }
-
- private long freshUntilMillis;
- @Override
- public long getFreshUntilMillis() {
- return this.freshUntilMillis;
- }
-
- private long validUntilMillis;
- @Override
- public long getValidUntilMillis() {
- return this.validUntilMillis;
- }
-
- private long voteSeconds;
- @Override
- public long getVoteSeconds() {
- return this.voteSeconds;
- }
-
- private long distSeconds;
- @Override
- public long getDistSeconds() {
- return this.distSeconds;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private List<String> packageLines;
- @Override
- public List<String> getPackageLines() {
- return this.packageLines == null ? null
- : new ArrayList<>(this.packageLines);
- }
-
- private String[] knownFlags;
- @Override
- public SortedSet<String> getKnownFlags() {
- return new TreeSet<>(Arrays.asList(this.knownFlags));
- }
-
- private SortedMap<String, Integer> consensusParams;
- @Override
- public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
- }
-
- private SortedMap<String, Integer> bandwidthWeights;
- @Override
- public SortedMap<String, Integer> getBandwidthWeights() {
- return this.bandwidthWeights == null ? null :
- new TreeMap<>(this.bandwidthWeights);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
deleted file mode 100644
index a5469db..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayNetworkStatus;
-
-/* TODO Write unit tests. */
-
-public class RelayNetworkStatusImpl extends NetworkStatusImpl
- implements RelayNetworkStatus {
-
- protected static List<RelayNetworkStatus> parseStatuses(
- byte[] statusesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatus> parsedStatuses = new ArrayList<>();
- List<byte[]> splitStatusBytes =
- DescriptorImpl.splitRawDescriptorBytes(statusesBytes,
- "network-status-version 2");
- for (byte[] statusBytes : splitStatusBytes) {
- RelayNetworkStatus parsedStatus = new RelayNetworkStatusImpl(
- statusBytes, failUnrecognizedDescriptorLines);
- parsedStatuses.add(parsedStatus);
- }
- return parsedStatuses;
- }
-
- protected RelayNetworkStatusImpl(byte[] statusBytes,
- 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();
- 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 ";
- if (!ascii.contains(sigToken)) {
- return;
- }
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- sig = ascii.indexOf("\n", sig) + 1;
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.statusDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.statusDigest == null) {
- throw new DescriptorParseException("Could not calculate status "
- + "digest.");
- }
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.isEmpty()) {
- continue;
- }
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "dir-options":
- this.parseDirOptionsLine(line, parts);
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("dir-signing-key")) {
- this.dirSigningKey = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- throw new DescriptorParseException("No directory footer expected in "
- + "v2 network status.");
- }
-
- protected void parseDirectorySignature(byte[] directorySignatureBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(directorySignatureBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "directory-signature":
- this.parseDirectorySignatureLine(line, parts);
- nextCrypto = "directory-signature";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- if (nextCrypto.equals("directory-signature")) {
- this.directorySignature = cryptoString;
- } else {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in v2 network status.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '" + line
- + "' in v2 network status.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("network-status-version 2")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 2;
- }
-
- private void parseDirSourceLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 4) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in v2 network status.");
- }
- if (parts[1].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.address = ParseHelper.parseIpv4Address(line, parts[2]);
- this.dirPort = ParseHelper.parsePort(line, parts[3]);
- }
-
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in v2 network status.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- parts[1]);
- }
-
- private void parseContactLine(String line, String[] parts)
- throws DescriptorParseException {
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseDirOptionsLine(String line, String[] parts)
- throws DescriptorParseException {
- String[] dirOptions = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- dirOptions[i - 1] = parts[i];
- }
- this.dirOptions = dirOptions;
- }
-
- private void parseDirectorySignatureLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- }
-
- private String statusDigest;
- @Override
- public String getStatusDigest() {
- return this.statusDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int dirPort;
- @Override
- public int getDirport() {
- return this.dirPort;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String[] dirOptions;
- @Override
- public SortedSet<String> getDirOptions() {
- return new TreeSet<>(Arrays.asList(this.dirOptions));
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String directorySignature;
- @Override
- public String getDirectorySignature() {
- return this.directorySignature;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
deleted file mode 100644
index 384ad1f..0000000
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ /dev/null
@@ -1,761 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.torproject.descriptor.RelayNetworkStatusVote;
-
-/* Contains a network status vote. */
-public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
- implements RelayNetworkStatusVote {
-
- protected static List<RelayNetworkStatusVote> parseVotes(
- byte[] votesBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<RelayNetworkStatusVote> parsedVotes = new ArrayList<>();
- List<byte[]> splitVotesBytes =
- DescriptorImpl.splitRawDescriptorBytes(votesBytes,
- "network-status-version 3");
- for (byte[] voteBytes : splitVotesBytes) {
- RelayNetworkStatusVote parsedVote =
- new RelayNetworkStatusVoteImpl(voteBytes,
- failUnrecognizedDescriptorLines);
- parsedVotes.add(parsedVote);
- }
- return parsedVotes;
- }
-
- protected RelayNetworkStatusVoteImpl(byte[] voteBytes,
- 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,"
- + "flag-thresholds,params,contact,"
- + "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();
- }
-
- protected void parseHeader(byte[] headerBytes)
- throws DescriptorParseException {
- /* Initialize flag-thresholds values here for the case that the vote
- * doesn't contain those values. Initializing them in the constructor
- * or when declaring variables wouldn't work, because those parts are
- * evaluated later and would overwrite everything we parse here. */
- this.stableUptime = -1L;
- this.stableMtbf = -1L;
- this.fastBandwidth = -1L;
- this.guardWfu = -1.0;
- this.guardTk = -1L;
- this.guardBandwidthIncludingExits = -1L;
- this.guardBandwidthExcludingExits = -1L;
- this.enoughMtbfInfo = -1;
- this.ignoringAdvertisedBws = -1;
-
- Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
- String nextCrypto = "";
- StringBuilder crypto = null;
- while (s.hasNext()) {
- String line = s.next();
- String[] parts = line.split("[ \t]+");
- String keyword = parts[0];
- switch (keyword) {
- case "network-status-version":
- this.parseNetworkStatusVersionLine(line, parts);
- break;
- case "vote-status":
- this.parseVoteStatusLine(line, parts);
- break;
- case "consensus-methods":
- this.parseConsensusMethodsLine(line, parts);
- break;
- case "published":
- this.parsePublishedLine(line, parts);
- break;
- case "valid-after":
- this.parseValidAfterLine(line, parts);
- break;
- case "fresh-until":
- this.parseFreshUntilLine(line, parts);
- break;
- case "valid-until":
- this.parseValidUntilLine(line, parts);
- break;
- case "voting-delay":
- this.parseVotingDelayLine(line, parts);
- break;
- case "client-versions":
- this.parseClientVersionsLine(line, parts);
- break;
- case "server-versions":
- this.parseServerVersionsLine(line, parts);
- break;
- case "package":
- this.parsePackageLine(line, parts);
- break;
- case "known-flags":
- this.parseKnownFlagsLine(line, parts);
- break;
- case "flag-thresholds":
- this.parseFlagThresholdsLine(line, parts);
- break;
- case "params":
- this.parseParamsLine(line, parts);
- break;
- case "dir-source":
- this.parseDirSourceLine(line, parts);
- break;
- case "contact":
- this.parseContactLine(line, parts);
- break;
- case "dir-key-certificate-version":
- this.parseDirKeyCertificateVersionLine(line, parts);
- break;
- case "dir-address":
- this.parseDirAddressLine(line, parts);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, parts);
- break;
- case "legacy-dir-key":
- this.parseLegacyDirKeyLine(line, parts);
- break;
- case "dir-key-published":
- this.parseDirKeyPublished(line, parts);
- break;
- case "dir-key-expires":
- this.parseDirKeyExpiresLine(line, parts);
- break;
- case "dir-identity-key":
- this.parseDirIdentityKeyLine(line, parts);
- nextCrypto = "dir-identity-key";
- break;
- case "dir-signing-key":
- this.parseDirSigningKeyLine(line, parts);
- nextCrypto = "dir-signing-key";
- break;
- case "dir-key-crosscert":
- this.parseDirKeyCrosscertLine(line, parts);
- nextCrypto = "dir-key-crosscert";
- break;
- case "dir-key-certification":
- this.parseDirKeyCertificationLine(line, parts);
- nextCrypto = "dir-key-certification";
- break;
- case "-----BEGIN":
- crypto = new StringBuilder();
- crypto.append(line).append("\n");
- break;
- case "-----END":
- crypto.append(line).append("\n");
- String cryptoString = crypto.toString();
- crypto = null;
- switch (nextCrypto) {
- case "dir-identity-key":
- this.dirIdentityKey = cryptoString;
- break;
- case "dir-signing-key":
- this.dirSigningKey = cryptoString;
- break;
- case "dir-key-crosscert":
- this.dirKeyCrosscert = cryptoString;
- break;
- case "dir-key-certification":
- this.dirKeyCertification = cryptoString;
- break;
- default:
- throw new DescriptorParseException("Unrecognized crypto "
- + "block in vote.");
- }
- nextCrypto = "";
- break;
- default:
- if (crypto != null) {
- crypto.append(line).append("\n");
- } else {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in vote.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseNetworkStatusVersionLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("network-status-version 3")) {
- throw new DescriptorParseException("Illegal network status version "
- + "number in line '" + line + "'.");
- }
- this.networkStatusVersion = 3;
- }
-
- private void parseVoteStatusLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2 || !parts[1].equals("vote")) {
- throw new DescriptorParseException("Line '" + line + "' indicates "
- + "that this is not a vote.");
- }
- }
-
- private void parseConsensusMethodsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- Integer[] consensusMethods = new Integer[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- int consensusMethod = -1;
- try {
- consensusMethod = Integer.parseInt(parts[i]);
- } catch (NumberFormatException e) {
- /* We'll notice below that consensusMethod is still -1. */
- }
- if (consensusMethod < 1) {
- throw new DescriptorParseException("Illegal consensus method "
- + "number in line '" + line + "'.");
- }
- consensusMethods[i - 1] = consensusMethod;
- }
- this.consensusMethods = consensusMethods;
- }
-
- private void parsePublishedLine(String line, String[] parts)
- throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidAfterLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validAfterMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseFreshUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.freshUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseValidUntilLine(String line, String[] parts)
- throws DescriptorParseException {
- this.validUntilMillis = ParseHelper.parseTimestampAtIndex(line, parts,
- 1, 2);
- }
-
- private void parseVotingDelayLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 3) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- try {
- this.voteSeconds = Long.parseLong(parts[1]);
- this.distSeconds = Long.parseLong(parts[2]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parseClientVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedClientVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parseServerVersionsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.recommendedServerVersions = this.parseClientOrServerVersions(
- line, parts);
- }
-
- private void parsePackageLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 5) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- if (this.packageLines == null) {
- this.packageLines = new ArrayList<>();
- }
- this.packageLines.add(line.substring("package ".length()));
- }
-
- private void parseKnownFlagsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No known flags in line '" + line
- + "'.");
- }
- String[] knownFlags = new String[parts.length - 1];
- for (int i = 1; i < parts.length; i++) {
- knownFlags[i - 1] = parts[i];
- }
- this.knownFlags = knownFlags;
- }
-
- private void parseFlagThresholdsLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length < 2) {
- throw new DescriptorParseException("No flag thresholds in line '"
- + line + "'.");
- }
- SortedMap<String, String> flagThresholds =
- ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
- try {
- for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
- switch (e.getKey()) {
- case "stable-uptime":
- this.stableUptime = Long.parseLong(e.getValue());
- break;
- case "stable-mtbf":
- this.stableMtbf = Long.parseLong(e.getValue());
- break;
- case "fast-speed":
- this.fastBandwidth = Long.parseLong(e.getValue());
- break;
- case "guard-wfu":
- this.guardWfu = Double.parseDouble(e.getValue().
- replaceAll("%", ""));
- break;
- case "guard-tk":
- this.guardTk = Long.parseLong(e.getValue());
- break;
- case "guard-bw-inc-exits":
- this.guardBandwidthIncludingExits =
- Long.parseLong(e.getValue());
- break;
- case "guard-bw-exc-exits":
- this.guardBandwidthExcludingExits =
- Long.parseLong(e.getValue());
- break;
- case "enough-mtbf":
- this.enoughMtbfInfo = Integer.parseInt(e.getValue());
- break;
- case "ignoring-advertised-bws":
- this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
- break;
- default:
- // empty
- }
- }
- } catch (NumberFormatException ex) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
-
- private void parseParamsLine(String line, String[] parts)
- throws DescriptorParseException {
- this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
- parts, 1, "=");
- }
-
- private void parseDirSourceLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 7) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- this.nickname = ParseHelper.parseNickname(line, parts[1]);
- this.identity = ParseHelper.parseTwentyByteHexString(line, parts[2]);
- if (parts[3].length() < 1) {
- throw new DescriptorParseException("Illegal hostname in '" + line
- + "'.");
- }
- this.hostname = parts[3];
- this.address = ParseHelper.parseIpv4Address(line, parts[4]);
- this.dirPort = ParseHelper.parsePort(line, parts[5]);
- this.orPort = ParseHelper.parsePort(line, parts[6]);
- }
-
- private void parseContactLine(String line, String[] parts)
- throws DescriptorParseException {
- if (line.length() > "contact ".length()) {
- this.contactLine = line.substring("contact ".length());
- } else {
- this.contactLine = "";
- }
- }
-
- private void parseDirKeyCertificateVersionLine(String line,
- String[] parts) throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- try {
- this.dirKeyCertificateVersion = Integer.parseInt(parts[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal dir key certificate "
- + "version in line '" + line + "'.");
- }
- if (this.dirKeyCertificateVersion < 1) {
- throw new DescriptorParseException("Illegal dir key certificate "
- + "version in line '" + line + "'.");
- }
- }
-
- private void parseDirAddressLine(String line, String[] parts) {
- /* Nothing new to learn here. Also, this line hasn't been observed
- * "in the wild" yet. Maybe it's just an urban legend. */
- }
-
- private void parseFingerprintLine(String line, String[] parts)
- throws DescriptorParseException {
- /* Nothing new to learn here. We already know the fingerprint from
- * the dir-source line. But we should at least check that there's a
- * valid fingerprint in this line. */
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in vote.");
- }
- ParseHelper.parseTwentyByteHexString(line, parts[1]);
- }
-
- private void parseLegacyDirKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (parts.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.legacyDirKey = ParseHelper.parseTwentyByteHexString(line, parts[1]);
- }
-
- private void parseDirKeyPublished(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirKeyExpiresLine(String line, String[] parts)
- throws DescriptorParseException {
- this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
- parts, 1, 2);
- }
-
- private void parseDirIdentityKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-identity-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirSigningKeyLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCrosscertLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-crosscert")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseDirKeyCertificationLine(String line, String[] parts)
- throws DescriptorParseException {
- if (!line.equals("dir-key-certification")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- protected void parseFooter(byte[] footerBytes)
- throws DescriptorParseException {
- Scanner s = new Scanner(new String(footerBytes)).useDelimiter("\n");
- while (s.hasNext()) {
- String line = s.next();
- if (!line.equals("directory-footer")) {
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in vote.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String identity;
- @Override
- public String getIdentity() {
- return this.identity;
- }
-
- private String hostname;
- @Override
- public String getHostname() {
- return this.hostname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int dirPort;
- @Override
- public int getDirport() {
- return this.dirPort;
- }
-
- private int orPort;
- @Override
- public int getOrport() {
- return this.orPort;
- }
-
- private String contactLine;
- @Override
- public String getContactLine() {
- return this.contactLine;
- }
-
- private int dirKeyCertificateVersion;
- @Override
- public int getDirKeyCertificateVersion() {
- return this.dirKeyCertificateVersion;
- }
-
- private String legacyDirKey;
- @Override
- public String getLegacyDirKey() {
- return this.legacyDirKey;
- }
-
- private long dirKeyPublishedMillis;
- @Override
- public long getDirKeyPublishedMillis() {
- return this.dirKeyPublishedMillis;
- }
-
- private long dirKeyExpiresMillis;
- @Override
- public long getDirKeyExpiresMillis() {
- return this.dirKeyExpiresMillis;
- }
-
- private String dirIdentityKey;
- @Override
- public String getDirIdentityKey() {
- return this.dirIdentityKey;
- }
-
- private String dirSigningKey;
- @Override
- public String getDirSigningKey() {
- return this.dirSigningKey;
- }
-
- private String dirKeyCrosscert;
- @Override
- public String getDirKeyCrosscert() {
- return this.dirKeyCrosscert;
- }
-
- private String dirKeyCertification;
- @Override
- public String getDirKeyCertification() {
- return this.dirKeyCertification;
- }
-
- @Override
- public String getSigningKeyDigest() {
- String signingKeyDigest = null;
- if (this.signatures != null && !this.signatures.isEmpty()) {
- for (DirectorySignature signature : this.signatures) {
- if (DirectorySignatureImpl.DEFAULT_ALGORITHM.equals(
- signature.getAlgorithm())) {
- signingKeyDigest = signature.getSigningKeyDigest();
- break;
- }
- }
- }
- return signingKeyDigest;
- }
-
- private int networkStatusVersion;
- @Override
- public int getNetworkStatusVersion() {
- return this.networkStatusVersion;
- }
-
- private Integer[] consensusMethods;
- @Override
- public List<Integer> getConsensusMethods() {
- return this.consensusMethods == null ? null :
- Arrays.asList(this.consensusMethods);
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private long validAfterMillis;
- @Override
- public long getValidAfterMillis() {
- return this.validAfterMillis;
- }
-
- private long freshUntilMillis;
- @Override
- public long getFreshUntilMillis() {
- return this.freshUntilMillis;
- }
-
- private long validUntilMillis;
- @Override
- public long getValidUntilMillis() {
- return this.validUntilMillis;
- }
-
- private long voteSeconds;
- @Override
- public long getVoteSeconds() {
- return this.voteSeconds;
- }
-
- private long distSeconds;
- @Override
- public long getDistSeconds() {
- return this.distSeconds;
- }
-
- private String[] recommendedClientVersions;
- @Override
- public List<String> getRecommendedClientVersions() {
- return this.recommendedClientVersions == null ? null :
- Arrays.asList(this.recommendedClientVersions);
- }
-
- private String[] recommendedServerVersions;
- @Override
- public List<String> getRecommendedServerVersions() {
- return this.recommendedServerVersions == null ? null :
- Arrays.asList(this.recommendedServerVersions);
- }
-
- private List<String> packageLines;
- @Override
- public List<String> getPackageLines() {
- return this.packageLines == null ? null
- : new ArrayList<>(this.packageLines);
- }
-
- private String[] knownFlags;
- @Override
- public SortedSet<String> getKnownFlags() {
- return new TreeSet<>(Arrays.asList(this.knownFlags));
- }
-
- private long stableUptime;
- @Override
- public long getStableUptime() {
- return this.stableUptime;
- }
-
- private long stableMtbf;
- @Override
- public long getStableMtbf() {
- return this.stableMtbf;
- }
-
- private long fastBandwidth;
- @Override
- public long getFastBandwidth() {
- return this.fastBandwidth;
- }
-
- private double guardWfu;
- @Override
- public double getGuardWfu() {
- return this.guardWfu;
- }
-
- private long guardTk;
- @Override
- public long getGuardTk() {
- return this.guardTk;
- }
-
- private long guardBandwidthIncludingExits;
- @Override
- public long getGuardBandwidthIncludingExits() {
- return this.guardBandwidthIncludingExits;
- }
-
- private long guardBandwidthExcludingExits;
- @Override
- public long getGuardBandwidthExcludingExits() {
- return this.guardBandwidthExcludingExits;
- }
-
- private int enoughMtbfInfo;
- @Override
- public int getEnoughMtbfInfo() {
- return this.enoughMtbfInfo;
- }
-
- private int ignoringAdvertisedBws;
- @Override
- public int getIgnoringAdvertisedBws() {
- return this.ignoringAdvertisedBws;
- }
-
- private SortedMap<String, Integer> consensusParams;
- @Override
- public SortedMap<String, Integer> getConsensusParams() {
- return this.consensusParams == null ? null:
- new TreeMap<>(this.consensusParams);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
deleted file mode 100644
index 4957072..0000000
--- a/src/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.RelayServerDescriptor;
-import org.torproject.descriptor.ServerDescriptor;
-
-public class RelayServerDescriptorImpl extends ServerDescriptorImpl
- implements RelayServerDescriptor {
-
- protected static List<ServerDescriptor> parseDescriptors(
- byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- List<ServerDescriptor> parsedDescriptors = new ArrayList<>();
- List<byte[]> splitDescriptorsBytes =
- DescriptorImpl.splitRawDescriptorBytes(descriptorsBytes,
- "router ");
- for (byte[] descriptorBytes : splitDescriptorsBytes) {
- ServerDescriptor parsedDescriptor =
- new RelayServerDescriptorImpl(descriptorBytes,
- failUnrecognizedDescriptorLines);
- parsedDescriptors.add(parsedDescriptor);
- }
- return parsedDescriptors;
- }
-
- protected RelayServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines);
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
deleted file mode 100644
index a359c50..0000000
--- a/src/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.RouterStatusEntry;
-
-public class RouterStatusEntryImpl implements RouterStatusEntry {
-
- protected RouterStatusEntryImpl(String fingerprint, String nickname,
- boolean isLive, boolean isVerified) {
- this.fingerprint = fingerprint;
- this.nickname = nickname;
- this.isLive = isLive;
- this.isVerified = isVerified;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private boolean isLive;
- @Override
- public boolean isLive() {
- return this.isLive;
- }
-
- private boolean isVerified;
- @Override
- public boolean isVerified() {
- return this.isVerified;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
deleted file mode 100644
index 1805dca..0000000
--- a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ /dev/null
@@ -1,985 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-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.List;
-import java.util.Scanner;
-import java.util.Set;
-
-import javax.xml.bind.DatatypeConverter;
-
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* Contains a server descriptor. */
-public abstract class ServerDescriptorImpl extends DescriptorImpl
- implements ServerDescriptor {
-
- protected ServerDescriptorImpl(byte[] descriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(descriptorBytes, failUnrecognizedDescriptorLines, false);
- 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,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) {
- throw new DescriptorParseException("Either keyword 'accept' or "
- + "'reject' must be contained at least once.");
- }
- this.clearParsedKeywords();
- return;
- }
-
- private void parseDescriptorBytes() throws DescriptorParseException {
- Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
- useDelimiter("\n");
- String nextCrypto = "";
- List<String> cryptoLines = null;
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@")) {
- continue;
- }
- String lineNoOpt = line.startsWith("opt ") ?
- line.substring("opt ".length()) : line;
- String[] partsNoOpt = lineNoOpt.split("[ \t]+");
- String keyword = partsNoOpt[0];
- switch (keyword) {
- case "router":
- this.parseRouterLine(line, lineNoOpt, partsNoOpt);
- break;
- case "or-address":
- this.parseOrAddressLine(line, lineNoOpt, partsNoOpt);
- break;
- case "bandwidth":
- this.parseBandwidthLine(line, lineNoOpt, partsNoOpt);
- break;
- case "platform":
- this.parsePlatformLine(line, lineNoOpt, partsNoOpt);
- break;
- case "published":
- this.parsePublishedLine(line, lineNoOpt, partsNoOpt);
- break;
- case "fingerprint":
- this.parseFingerprintLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hibernating":
- this.parseHibernatingLine(line, lineNoOpt, partsNoOpt);
- break;
- case "uptime":
- this.parseUptimeLine(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key":
- this.parseOnionKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key";
- break;
- case "signing-key":
- this.parseSigningKeyLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "signing-key";
- break;
- case "accept":
- this.parseAcceptLine(line, lineNoOpt, partsNoOpt);
- break;
- case "reject":
- this.parseRejectLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-signature":
- this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
- nextCrypto = "router-signature";
- break;
- case "contact":
- this.parseContactLine(line, lineNoOpt, partsNoOpt);
- break;
- case "family":
- this.parseFamilyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "read-history":
- this.parseReadHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "write-history":
- this.parseWriteHistoryLine(line, lineNoOpt, partsNoOpt);
- break;
- case "eventdns":
- this.parseEventdnsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "caches-extra-info":
- this.parseCachesExtraInfoLine(line, lineNoOpt, partsNoOpt);
- break;
- case "extra-info-digest":
- this.parseExtraInfoDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "hidden-service-dir":
- this.parseHiddenServiceDirLine(line, lineNoOpt, partsNoOpt);
- break;
- case "protocols":
- this.parseProtocolsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "allow-single-hop-exits":
- this.parseAllowSingleHopExitsLine(line, lineNoOpt, partsNoOpt);
- break;
- case "dircacheport":
- this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest":
- this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
- break;
- case "router-digest-sha256":
- this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
- break;
- case "ipv6-policy":
- this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "ntor-onion-key":
- this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
- break;
- case "identity-ed25519":
- this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
- nextCrypto = "identity-ed25519";
- break;
- case "master-key-ed25519":
- this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "router-sig-ed25519":
- this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
- break;
- case "onion-key-crosscert":
- this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "onion-key-crosscert";
- break;
- case "ntor-onion-key-crosscert":
- this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt);
- nextCrypto = "ntor-onion-key-crosscert";
- break;
- case "tunnelled-dir-server":
- this.parseTunnelledDirServerLine(line, lineNoOpt, partsNoOpt);
- break;
- case "-----BEGIN":
- cryptoLines = new ArrayList<>();
- cryptoLines.add(line);
- break;
- case "-----END":
- cryptoLines.add(line);
- StringBuilder sb = new StringBuilder();
- for (String cryptoLine : cryptoLines) {
- sb.append("\n").append(cryptoLine);
- }
- String cryptoString = sb.toString().substring(1);
- switch (nextCrypto) {
- case "onion-key":
- this.onionKey = cryptoString;
- break;
- case "signing-key":
- this.signingKey = cryptoString;
- break;
- case "router-signature":
- this.routerSignature = cryptoString;
- break;
- case "identity-ed25519":
- this.identityEd25519 = cryptoString;
- this.parseIdentityEd25519CryptoBlock(cryptoString);
- break;
- case "onion-key-crosscert":
- this.onionKeyCrosscert = cryptoString;
- break;
- case "ntor-onion-key-crosscert":
- this.ntorOnionKeyCrosscert = cryptoString;
- break;
- default:
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized crypto "
- + "block '" + cryptoString + "' in server descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.addAll(cryptoLines);
- }
- }
- cryptoLines = null;
- nextCrypto = "";
- break;
- default:
- if (cryptoLines != null) {
- cryptoLines.add(line);
- } else {
- ParseHelper.parseKeyword(line, partsNoOpt[0]);
- if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized line '"
- + line + "' in server descriptor.");
- } else {
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- }
-
- private void parseRouterLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 6) {
- throw new DescriptorParseException("Illegal line '" + line
- + "' in server descriptor.");
- }
- this.nickname = ParseHelper.parseNickname(line, partsNoOpt[1]);
- this.address = ParseHelper.parseIpv4Address(line, partsNoOpt[2]);
- this.orPort = ParseHelper.parsePort(line, partsNoOpt[3]);
- this.socksPort = ParseHelper.parsePort(line, partsNoOpt[4]);
- this.dirPort = ParseHelper.parsePort(line, partsNoOpt[5]);
- }
-
- private void parseOrAddressLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- /* TODO Add more checks. */
- /* TODO Add tests. */
- this.orAddresses.add(partsNoOpt[1]);
- }
-
- private void parseBandwidthLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 3 || partsNoOpt.length > 4) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- boolean isValid = false;
- try {
- this.bandwidthRate = Integer.parseInt(partsNoOpt[1]);
- this.bandwidthBurst = Integer.parseInt(partsNoOpt[2]);
- if (partsNoOpt.length == 4) {
- this.bandwidthObserved = Integer.parseInt(partsNoOpt[3]);
- }
- if (this.bandwidthRate >= 0 && this.bandwidthBurst >= 0 &&
- this.bandwidthObserved >= 0) {
- isValid = true;
- }
- if (partsNoOpt.length < 4) {
- /* Tor versions 0.0.8 and older only wrote bandwidth lines with
- * rate and burst values, but no observed value. */
- this.bandwidthObserved = -1;
- }
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal values in line '" + line
- + "'.");
- }
- }
-
- private void parsePlatformLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (lineNoOpt.length() > "platform ".length()) {
- this.platform = lineNoOpt.substring("platform ".length());
- } else {
- this.platform = "";
- }
- }
-
- private void parsePublishedLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.publishedMillis = ParseHelper.parseTimestampAtIndex(line,
- partsNoOpt, 1, 2);
- }
-
- private void parseFingerprintLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (lineNoOpt.length() != "fingerprint".length() + 5 * 10) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
- lineNoOpt.substring("fingerprint ".length()).replaceAll(" ", ""));
- }
-
- private void parseHibernatingLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.hibernating = ParseHelper.parseBoolean(partsNoOpt[1], line);
- }
-
- private void parseUptimeLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Wrong number of values in line "
- + "'" + line + "'.");
- }
- boolean isValid = false;
- try {
- this.uptime = Long.parseLong(partsNoOpt[1]);
- isValid = true;
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal value in line '" + line
- + "'.");
- }
- }
-
- private void parseOnionKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("onion-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseSigningKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("signing-key")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseAcceptLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
- }
-
- private void parseRejectLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.parseExitPolicyLine(line, lineNoOpt, partsNoOpt);
- }
-
- private void parseExitPolicyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseExitPattern(line, partsNoOpt[1]);
- this.exitPolicyLines.add(lineNoOpt);
- }
-
- private void parseRouterSignatureLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("router-signature")) {
- 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());
- } else {
- this.contact = "";
- }
- }
-
- private void parseFamilyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- String[] familyEntries = new String[partsNoOpt.length - 1];
- for (int i = 1; i < partsNoOpt.length; i++) {
- if (partsNoOpt[i].startsWith("$")) {
- if (partsNoOpt[i].contains("=") ^ partsNoOpt[i].contains("~")) {
- String separator = partsNoOpt[i].contains("=") ? "=" : "~";
- String fingerprint = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[i].substring(1, partsNoOpt[i].indexOf(
- separator)));
- String nickname = ParseHelper.parseNickname(line,
- partsNoOpt[i].substring(partsNoOpt[i].indexOf(
- separator) + 1));
- familyEntries[i - 1] = "$" + fingerprint + separator + nickname;
- } else {
- familyEntries[i - 1] = "$"
- + ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[i].substring(1));
- }
- } else {
- familyEntries[i - 1] = ParseHelper.parseNickname(line,
- partsNoOpt[i]);
- }
- }
- this.familyEntries = familyEntries;
- }
-
- private void parseReadHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.readHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseWriteHistoryLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- this.writeHistory = new BandwidthHistoryImpl(line, lineNoOpt,
- partsNoOpt);
- }
-
- private void parseEventdnsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.usesEnhancedDnsLogic = ParseHelper.parseBoolean(partsNoOpt[1], line);
- }
-
- private void parseCachesExtraInfoLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("caches-extra-info")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.cachesExtraInfo = true;
- }
-
- private void parseExtraInfoDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length < 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
- partsNoOpt[1]);
- if (partsNoOpt.length >= 3) {
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[2]);
- this.extraInfoDigestSha256 = partsNoOpt[2];
- }
- }
-
- private void parseHiddenServiceDirLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length == 1) {
- this.hiddenServiceDirVersions = new Integer[] { 2 };
- } else {
- try {
- Integer[] result = new Integer[partsNoOpt.length - 1];
- for (int i = 1; i < partsNoOpt.length; i++) {
- result[i - 1] = Integer.parseInt(partsNoOpt[i]);
- }
- this.hiddenServiceDirVersions = result;
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in line '"
- + line + "'.");
- }
- }
- }
-
- private void parseProtocolsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- int linkIndex = -1, circuitIndex = -1;
- for (int i = 1; i < partsNoOpt.length; i++) {
- switch (partsNoOpt[i]) {
- case "Link":
- linkIndex = i;
- break;
- case "Circuit":
- circuitIndex = i;
- break;
- default:
- // empty
- }
- }
- if (linkIndex < 0 || circuitIndex < 0 || circuitIndex < linkIndex) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- Integer[] linkProtocolVersions =
- new Integer[circuitIndex - linkIndex - 1];
- for (int i = linkIndex + 1, j = 0; i < circuitIndex; i++, j++) {
- linkProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
- }
- Integer[] circuitProtocolVersions =
- new Integer[partsNoOpt.length - circuitIndex - 1];
- for (int i = circuitIndex + 1, j = 0; i < partsNoOpt.length;
- i++, j++) {
- circuitProtocolVersions[j] = Integer.parseInt(partsNoOpt[i]);
- }
- this.linkProtocolVersions = linkProtocolVersions;
- this.circuitProtocolVersions = circuitProtocolVersions;
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseAllowSingleHopExitsLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("allow-single-hop-exits")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.allowSingleHopExits = true;
- }
-
- private void parseDircacheportLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- /* The dircacheport line was only contained in server descriptors
- * published by Tor 0.0.8 and before. It's only specified in old
- * tor-spec.txt versions. */
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- if (this.dirPort != 0) {
- throw new DescriptorParseException("At most one of dircacheport "
- + "and the directory port in the router line may be non-zero.");
- }
- this.dirPort = ParseHelper.parsePort(line, partsNoOpt[1]);
- }
-
- private void parseRouterDigestLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.serverDescriptorDigest = ParseHelper.parseTwentyByteHexString(
- line, partsNoOpt[1]);
- }
-
- private void parseIpv6PolicyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- boolean isValid = true;
- if (partsNoOpt.length != 3) {
- isValid = false;
- } else {
- switch (partsNoOpt[1]) {
- case "accept":
- case "reject":
- this.ipv6DefaultPolicy = partsNoOpt[1];
- this.ipv6PortList = partsNoOpt[2];
- String[] ports = partsNoOpt[2].split(",", -1);
- for (int i = 0; i < ports.length; i++) {
- if (ports[i].length() < 1) {
- isValid = false;
- break;
- }
- }
- break;
- default:
- isValid = false;
- }
- }
- if (!isValid) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.ntorOnionKey = partsNoOpt[1].replaceAll("=", "");
- }
-
- private void parseIdentityEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseOnionKeyCrosscert(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 1) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseNtorOnionKeyCrosscert(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- try {
- this.ntorOnionKeyCrosscertSign = Integer.parseInt(partsNoOpt[1]);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- }
-
- private void parseTunnelledDirServerLine(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (!lineNoOpt.equals("tunnelled-dir-server")) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.tunnelledDirServer = true;
- }
-
- private void parseIdentityEd25519CryptoBlock(String cryptoString)
- throws DescriptorParseException {
- String masterKeyEd25519FromIdentityEd25519 =
- ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
- cryptoString);
- if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
- masterKeyEd25519FromIdentityEd25519)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
- }
-
- private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
- if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
- masterKeyEd25519FromMasterKeyEd25519Line)) {
- throw new DescriptorParseException("Mismatch between "
- + "identity-ed25519 and master-key-ed25519.");
- }
- this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
- }
-
- private void parseRouterSigEd25519Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- this.routerSignatureEd25519 = partsNoOpt[1];
- }
-
- private void parseRouterDigestSha256Line(String line, String lineNoOpt,
- String[] partsNoOpt) throws DescriptorParseException {
- if (partsNoOpt.length != 2) {
- throw new DescriptorParseException("Illegal line '" + line + "'.");
- }
- ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
- this.serverDescriptorDigestSha256 = partsNoOpt[1];
- }
-
- private void calculateDigest() throws DescriptorParseException {
- if (this.serverDescriptorDigest != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "router ";
- String sigToken = "\nrouter-signature\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start,
- forDigest, 0, sig - start);
- this.serverDescriptorDigest = DatatypeConverter.printHexBinary(
- MessageDigest.getInstance("SHA-1").digest(forDigest)).
- toLowerCase();
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.serverDescriptorDigest == null) {
- throw new DescriptorParseException("Could not calculate server "
- + "descriptor digest.");
- }
- }
-
- private void calculateDigestSha256() throws DescriptorParseException {
- if (this.serverDescriptorDigestSha256 != null) {
- /* We already learned the descriptor digest of this bridge
- * descriptor from a "router-digest-sha256" line. */
- return;
- }
- try {
- String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
- String startToken = "router ";
- String sigToken = "\n-----END SIGNATURE-----\n";
- int start = ascii.indexOf(startToken);
- int sig = ascii.indexOf(sigToken) + sigToken.length();
- if (start >= 0 && sig >= 0 && sig > start) {
- byte[] forDigest = new byte[sig - start];
- System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
- 0, sig - start);
- this.serverDescriptorDigestSha256 =
- DatatypeConverter.printBase64Binary(
- MessageDigest.getInstance("SHA-256").digest(forDigest)).
- replaceAll("=", "");
- }
- } catch (UnsupportedEncodingException e) {
- /* Handle below. */
- } catch (NoSuchAlgorithmException e) {
- /* Handle below. */
- }
- if (this.serverDescriptorDigestSha256 == null) {
- throw new DescriptorParseException("Could not calculate server "
- + "descriptor SHA-256 digest.");
- }
- }
-
- private String serverDescriptorDigest;
- @Override
- public String getServerDescriptorDigest() {
- return this.serverDescriptorDigest;
- }
-
- private String serverDescriptorDigestSha256;
- @Override
- public String getServerDescriptorDigestSha256() {
- return this.serverDescriptorDigestSha256;
- }
-
- private String nickname;
- @Override
- public String getNickname() {
- return this.nickname;
- }
-
- private String address;
- @Override
- public String getAddress() {
- return this.address;
- }
-
- private int orPort;
- @Override
- public int getOrPort() {
- return this.orPort;
- }
-
- private int socksPort;
- @Override
- public int getSocksPort() {
- return this.socksPort;
- }
-
- private int dirPort;
- @Override
- public int getDirPort() {
- return this.dirPort;
- }
-
- private List<String> orAddresses = new ArrayList<>();
- @Override
- public List<String> getOrAddresses() {
- return new ArrayList<>(this.orAddresses);
- }
-
- private int bandwidthRate;
- @Override
- public int getBandwidthRate() {
- return this.bandwidthRate;
- }
-
- private int bandwidthBurst;
- @Override
- public int getBandwidthBurst() {
- return this.bandwidthBurst;
- }
-
- private int bandwidthObserved;
- @Override
- public int getBandwidthObserved() {
- return this.bandwidthObserved;
- }
-
- private String platform;
- @Override
- public String getPlatform() {
- return this.platform;
- }
-
- private long publishedMillis;
- @Override
- public long getPublishedMillis() {
- return this.publishedMillis;
- }
-
- private String fingerprint;
- @Override
- public String getFingerprint() {
- return this.fingerprint;
- }
-
- private boolean hibernating;
- @Override
- public boolean isHibernating() {
- return this.hibernating;
- }
-
- private Long uptime;
- @Override
- public Long getUptime() {
- return this.uptime;
- }
-
- private String onionKey;
- @Override
- public String getOnionKey() {
- return this.onionKey;
- }
-
- private String signingKey;
- @Override
- public String getSigningKey() {
- return this.signingKey;
- }
-
- private List<String> exitPolicyLines = new ArrayList<>();
- @Override
- public List<String> getExitPolicyLines() {
- return new ArrayList<>(this.exitPolicyLines);
- }
-
- private String routerSignature;
- @Override
- public String getRouterSignature() {
- return this.routerSignature;
- }
-
- private String contact;
- @Override
- public String getContact() {
- return this.contact;
- }
-
- private String[] familyEntries;
- @Override
- public List<String> getFamilyEntries() {
- return this.familyEntries == null ? null :
- Arrays.asList(this.familyEntries);
- }
-
- private BandwidthHistory readHistory;
- @Override
- public BandwidthHistory getReadHistory() {
- return this.readHistory;
- }
-
- private BandwidthHistory writeHistory;
- @Override
- public BandwidthHistory getWriteHistory() {
- return this.writeHistory;
- }
-
- private boolean usesEnhancedDnsLogic;
- @Override
- public boolean getUsesEnhancedDnsLogic() {
- return this.usesEnhancedDnsLogic;
- }
-
- private boolean cachesExtraInfo;
- @Override
- public boolean getCachesExtraInfo() {
- return this.cachesExtraInfo;
- }
-
- private String extraInfoDigest;
- @Override
- public String getExtraInfoDigest() {
- return this.extraInfoDigest;
- }
-
- private String extraInfoDigestSha256;
- @Override
- public String getExtraInfoDigestSha256() {
- return this.extraInfoDigestSha256;
- }
-
- private Integer[] hiddenServiceDirVersions;
- @Override
- public List<Integer> getHiddenServiceDirVersions() {
- return this.hiddenServiceDirVersions == null ? null :
- Arrays.asList(this.hiddenServiceDirVersions);
- }
-
- private Integer[] linkProtocolVersions;
- @Override
- public List<Integer> getLinkProtocolVersions() {
- return this.linkProtocolVersions == null ? null :
- Arrays.asList(this.linkProtocolVersions);
- }
-
- private Integer[] circuitProtocolVersions;
- @Override
- public List<Integer> getCircuitProtocolVersions() {
- return this.circuitProtocolVersions == null ? null :
- Arrays.asList(this.circuitProtocolVersions);
- }
-
- private boolean allowSingleHopExits;
- @Override
- public boolean getAllowSingleHopExits() {
- return this.allowSingleHopExits;
- }
-
- private String ipv6DefaultPolicy;
- @Override
- public String getIpv6DefaultPolicy() {
- return this.ipv6DefaultPolicy;
- }
-
- private String ipv6PortList;
- @Override
- public String getIpv6PortList() {
- return this.ipv6PortList;
- }
-
- private String ntorOnionKey;
- @Override
- public String getNtorOnionKey() {
- return this.ntorOnionKey;
- }
-
- private String identityEd25519;
- @Override
- public String getIdentityEd25519() {
- return this.identityEd25519;
- }
-
- private String masterKeyEd25519;
- @Override
- public String getMasterKeyEd25519() {
- return this.masterKeyEd25519;
- }
-
- private String routerSignatureEd25519;
- @Override
- public String getRouterSignatureEd25519() {
- return this.routerSignatureEd25519;
- }
-
- private String onionKeyCrosscert;
- @Override
- public String getOnionKeyCrosscert() {
- return this.onionKeyCrosscert;
- }
-
- private String ntorOnionKeyCrosscert;
- @Override
- public String getNtorOnionKeyCrosscert() {
- return this.ntorOnionKeyCrosscert;
- }
-
- private int ntorOnionKeyCrosscertSign = -1;
- @Override
- public int getNtorOnionKeyCrosscertSign() {
- return ntorOnionKeyCrosscertSign;
- }
-
- private boolean tunnelledDirServer;
- @Override
- public boolean getTunnelledDirServer() {
- return this.tunnelledDirServer;
- }
-}
-
diff --git a/src/org/torproject/descriptor/impl/TorperfResultImpl.java b/src/org/torproject/descriptor/impl/TorperfResultImpl.java
deleted file mode 100644
index 0800de0..0000000
--- a/src/org/torproject/descriptor/impl/TorperfResultImpl.java
+++ /dev/null
@@ -1,546 +0,0 @@
-/* Copyright 2012--2016 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.TorperfResult;
-
-public class TorperfResultImpl extends DescriptorImpl
- implements TorperfResult {
-
- protected static List<Descriptor> parseTorperfResults(
- byte[] rawDescriptorBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- if (rawDescriptorBytes.length == 0) {
- throw new DescriptorParseException("Descriptor is empty.");
- }
- List<Descriptor> parsedDescriptors = new ArrayList<>();
- String descriptorString = new String(rawDescriptorBytes);
- Scanner s = new Scanner(descriptorString).useDelimiter("\r?\n");
- String typeAnnotation = "";
- while (s.hasNext()) {
- String line = s.next();
- if (line.startsWith("@type torperf ")) {
- String[] parts = line.split(" ");
- if (parts.length != 3) {
- throw new DescriptorParseException("Illegal line '" + line
- + "'.");
- }
- String version = parts[2];
- if (!version.startsWith("1.")) {
- throw new DescriptorParseException("Unsupported version in "
- + " line '" + line + "'.");
- }
- typeAnnotation = line + "\n";
- } else {
- parsedDescriptors.add(new TorperfResultImpl(
- (typeAnnotation + line).getBytes(),
- failUnrecognizedDescriptorLines));
- typeAnnotation = "";
- }
- }
- return parsedDescriptors;
- }
-
- protected TorperfResultImpl(byte[] rawDescriptorBytes,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- super(rawDescriptorBytes, failUnrecognizedDescriptorLines, false);
- this.parseTorperfResultLine(new String(rawDescriptorBytes));
- }
-
- private void parseTorperfResultLine(String inputLine)
- throws DescriptorParseException {
- String line = inputLine;
- while (line.startsWith("@") && line.contains("\n")) {
- line = line.split("\n")[1];
- }
- if (line.isEmpty()) {
- throw new DescriptorParseException("Blank lines are not allowed.");
- }
- String[] parts = line.split(" ");
- for (int i = 0; i < parts.length; i++) {
- String keyValue = parts[i];
- String[] keyValueParts = keyValue.split("=");
- if (keyValueParts.length != 2) {
- throw new DescriptorParseException("Illegal key-value pair in "
- + "line '" + line + "'.");
- }
- String key = keyValueParts[0];
- this.markKeyAsParsed(key, line);
- String value = keyValueParts[1];
- switch (key) {
- case "SOURCE":
- this.parseSource(value, keyValue, line);
- break;
- case "FILESIZE":
- this.parseFileSize(value, keyValue, line);
- break;
- case "START":
- this.parseStart(value, keyValue, line);
- break;
- case "SOCKET":
- this.parseSocket(value, keyValue, line);
- break;
- case "CONNECT":
- this.parseConnect(value, keyValue, line);
- break;
- case "NEGOTIATE":
- this.parseNegotiate(value, keyValue, line);
- break;
- case "REQUEST":
- this.parseRequest(value, keyValue, line);
- break;
- case "RESPONSE":
- this.parseResponse(value, keyValue, line);
- break;
- case "DATAREQUEST":
- this.parseDataRequest(value, keyValue, line);
- break;
- case "DATARESPONSE":
- this.parseDataResponse(value, keyValue, line);
- break;
- case "DATACOMPLETE":
- this.parseDataComplete(value, keyValue, line);
- break;
- case "WRITEBYTES":
- this.parseWriteBytes(value, keyValue, line);
- break;
- case "READBYTES":
- this.parseReadBytes(value, keyValue, line);
- break;
- case "DIDTIMEOUT":
- this.parseDidTimeout(value, keyValue, line);
- break;
- case "LAUNCH":
- this.parseLaunch(value, keyValue, line);
- break;
- case "USED_AT":
- this.parseUsedAt(value, keyValue, line);
- break;
- case "PATH":
- this.parsePath(value, keyValue, line);
- break;
- case "BUILDTIMES":
- this.parseBuildTimes(value, keyValue, line);
- break;
- case "TIMEOUT":
- this.parseTimeout(value, keyValue, line);
- break;
- case "QUANTILE":
- this.parseQuantile(value, keyValue, line);
- break;
- case "CIRC_ID":
- this.parseCircId(value, keyValue, line);
- break;
- case "USED_BY":
- this.parseUsedBy(value, keyValue, line);
- break;
- default:
- if (key.startsWith("DATAPERC")) {
- this.parseDataPercentile(value, keyValue, line);
- } else if (this.failUnrecognizedDescriptorLines) {
- throw new DescriptorParseException("Unrecognized key '" + key
- + "' in line '" + line + "'.");
- } else {
- if (this.unrecognizedKeys == null) {
- this.unrecognizedKeys = new TreeMap<>();
- }
- this.unrecognizedKeys.put(key, value);
- if (this.unrecognizedLines == null) {
- this.unrecognizedLines = new ArrayList<>();
- }
- if (!this.unrecognizedLines.contains(line)) {
- this.unrecognizedLines.add(line);
- }
- }
- }
- }
- this.checkAllRequiredKeysParsed(line);
- }
-
- private Set<String> parsedKeys = new HashSet<>();
- private Set<String> requiredKeys = new HashSet<>(Arrays.asList(
- ("SOURCE,FILESIZE,START,SOCKET,CONNECT,NEGOTIATE,REQUEST,RESPONSE,"
- + "DATAREQUEST,DATARESPONSE,DATACOMPLETE,WRITEBYTES,READBYTES").
- split(",")));
- private void markKeyAsParsed(String key, String line)
- throws DescriptorParseException {
- if (this.parsedKeys.contains(key)) {
- throw new DescriptorParseException("Key '" + key + "' is contained "
- + "at least twice in line '" + line + "', but must be "
- + "contained at most once.");
- }
- this.parsedKeys.add(key);
- this.requiredKeys.remove(key);
- }
- private void checkAllRequiredKeysParsed(String line)
- throws DescriptorParseException {
- for (String key : this.requiredKeys) {
- throw new DescriptorParseException("Key '" + key + "' is contained "
- + "contained 0 times in line '" + line + "', but must be "
- + "contained exactly once.");
- }
- }
-
- private void parseSource(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.source = value;
- }
-
- private void parseFileSize(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- this.fileSize = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private void parseStart(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.startMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseSocket(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.socketMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseConnect(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.connectMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseNegotiate(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.negotiateMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseRequest(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.requestMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseResponse(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.responseMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataRequest(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataRequestMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataResponse(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataResponseMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseDataComplete(String value, String keyValue,
- String line) throws DescriptorParseException {
- this.dataCompleteMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseWriteBytes(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.writeBytes = parseInt(value, keyValue, line);
- }
-
- private void parseReadBytes(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.readBytes = parseInt(value, keyValue, line);
- }
-
- private void parseDidTimeout(String value, String keyValue, String line)
- throws DescriptorParseException {
- if (value.equals("1")) {
- this.didTimeout = true;
- } else if (value.equals("0")) {
- this.didTimeout = false;
- } else {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private void parseDataPercentile(String value, String keyValue,
- String line) throws DescriptorParseException {
- String key = keyValue.substring(0, keyValue.indexOf("="));
- String percentileString = key.substring("DATAPERC".length());
- int percentile = -1;
- try {
- percentile = Integer.parseInt(percentileString);
- } catch (NumberFormatException e) {
- /* Treat key as unrecognized below. */
- percentile = -1;
- }
- if (percentile < 0 || percentile > 100) {
- if (this.unrecognizedKeys == null) {
- this.unrecognizedKeys = new TreeMap<>();
- }
- this.unrecognizedKeys.put(key, value);
- } else {
- long timestamp = this.parseTimestamp(value, keyValue, line);
- if (this.dataPercentiles == null) {
- this.dataPercentiles = new TreeMap<>();
- }
- this.dataPercentiles.put(percentile, timestamp);
- }
- }
-
- private void parseLaunch(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.launchMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parseUsedAt(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.usedAtMillis = this.parseTimestamp(value, keyValue, line);
- }
-
- private void parsePath(String value, String keyValue, String line)
- throws DescriptorParseException {
- String[] valueParts = value.split(",");
- String[] result = new String[valueParts.length];
- for (int i = 0; i < valueParts.length; i++) {
- if (valueParts[i].length() != 41) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- result[i] = ParseHelper.parseTwentyByteHexString(line,
- valueParts[i].substring(1));
- }
- this.path = result;
- }
-
- private void parseBuildTimes(String value, String keyValue, String line)
- throws DescriptorParseException {
- String[] valueParts = value.split(",");
- Long[] result = new Long[valueParts.length];
- for (int i = 0; i < valueParts.length; i++) {
- result[i] = this.parseTimestamp(valueParts[i], keyValue, line);
- }
- this.buildTimes = result;
- }
-
- private void parseTimeout(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.timeout = this.parseInt(value, keyValue, line);
- }
-
- private void parseQuantile(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.quantile = this.parseDouble(value, keyValue, line);
- }
-
- private void parseCircId(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.circId = this.parseInt(value, keyValue, line);
- }
-
- private void parseUsedBy(String value, String keyValue, String line)
- throws DescriptorParseException {
- this.usedBy = this.parseInt(value, keyValue, line);
- }
-
- private long parseTimestamp(String value, String keyValue, String line)
- throws DescriptorParseException {
- long timestamp = -1L;
- if (value.contains(".") && value.split("\\.").length == 2) {
- String zeroPaddedValue = (value + "000");
- String threeDecimalPlaces = zeroPaddedValue.substring(0,
- zeroPaddedValue.indexOf(".") + 4);
- String millisString = threeDecimalPlaces.replaceAll("\\.", "");
- try {
- timestamp = Long.parseLong(millisString);
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (timestamp < 0L) {
- throw new DescriptorParseException("Illegal timestamp '" + value
- + "' in '" + keyValue + "' in line '" + line + "'.");
- }
- return timestamp;
- }
-
- private int parseInt(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private double parseDouble(String value, String keyValue, String line)
- throws DescriptorParseException {
- try {
- return Double.parseDouble(value);
- } catch (NumberFormatException e) {
- throw new DescriptorParseException("Illegal value in '" + keyValue
- + "' in line '" + line + "'.");
- }
- }
-
- private SortedMap<String, String> unrecognizedKeys;
- @Override
- public SortedMap<String, String> getUnrecognizedKeys() {
- return this.unrecognizedKeys == null ? null
- : new TreeMap<>(this.unrecognizedKeys);
- }
-
- private String source;
- @Override
- public String getSource() {
- return this.source;
- }
-
- private int fileSize;
- @Override
- public int getFileSize() {
- return this.fileSize;
- }
-
- private long startMillis;
- @Override
- public long getStartMillis() {
- return this.startMillis;
- }
-
- private long socketMillis;
- @Override
- public long getSocketMillis() {
- return this.socketMillis;
- }
-
- private long connectMillis;
- @Override
- public long getConnectMillis() {
- return this.connectMillis;
- }
-
- private long negotiateMillis;
- @Override
- public long getNegotiateMillis() {
- return this.negotiateMillis;
- }
-
- private long requestMillis;
- @Override
- public long getRequestMillis() {
- return this.requestMillis;
- }
-
- private long responseMillis;
- @Override
- public long getResponseMillis() {
- return this.responseMillis;
- }
-
- private long dataRequestMillis;
- @Override
- public long getDataRequestMillis() {
- return this.dataRequestMillis;
- }
-
- private long dataResponseMillis;
- @Override
- public long getDataResponseMillis() {
- return this.dataResponseMillis;
- }
-
- private long dataCompleteMillis;
- @Override
- public long getDataCompleteMillis() {
- return this.dataCompleteMillis;
- }
-
- private int writeBytes;
- @Override
- public int getWriteBytes() {
- return this.writeBytes;
- }
-
- private int readBytes;
- @Override
- public int getReadBytes() {
- return this.readBytes;
- }
-
- private boolean didTimeout;
- @Override
- public Boolean didTimeout() {
- return this.didTimeout;
- }
-
- private SortedMap<Integer, Long> dataPercentiles;
- @Override
- public SortedMap<Integer, Long> getDataPercentiles() {
- return this.dataPercentiles == null ? null
- : new TreeMap<>(this.dataPercentiles);
- }
-
- private long launchMillis = -1L;
- @Override
- public long getLaunchMillis() {
- return this.launchMillis;
- }
-
- private long usedAtMillis = -1L;
- @Override
- public long getUsedAtMillis() {
- return this.usedAtMillis;
- }
-
- private String[] path;
- @Override
- public List<String> getPath() {
- return this.path == null ? null : Arrays.asList(this.path);
- }
-
- private Long[] buildTimes;
- @Override
- public List<Long> getBuildTimes() {
- return this.buildTimes == null ? null :
- Arrays.asList(this.buildTimes);
- }
-
- private long timeout = -1L;
- @Override
- public long getTimeout() {
- return this.timeout;
- }
-
- private double quantile = -1.0;
- @Override
- public double getQuantile() {
- return this.quantile;
- }
-
- private int circId = -1;
- @Override
- public int getCircId() {
- return this.circId;
- }
-
- private int usedBy = -1;
- @Override
- public int getUsedBy() {
- return this.usedBy;
- }
-}
-
diff --git a/src/org/torproject/descriptor/package-info.java b/src/org/torproject/descriptor/package-info.java
deleted file mode 100644
index 5b34554..0000000
--- a/src/org/torproject/descriptor/package-info.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2016 The Tor Project
- * See LICENSE for licensing information */
-
-/**
- * Interfaces and essential classes for obtaining and processing Tor
- * descriptors.
- *
- * <p>This package contains all relevant interfaces and
- * classes that an application would need to use this library.
- * Applications are strongly discouraged from accessing types from the
- * implementation package ({@code org.torproject.descriptor.impl})
- * directly, because those may change without prior notice.</p>
- *
- * <p>Interfaces and classes in this package can be grouped into
- * general-purpose types to obtain and process any type of descriptor and
- * descriptors produced by different components of the Tor network:</p>
- *
- * <ol>
- * <li>General-purpose types comprise
- * {@link org.torproject.descriptor.DescriptorSourceFactory} which is the
- * main entry point into using this library. This factory is used to
- * create the descriptor sources for obtaining remote descriptor data
- * ({@link org.torproject.descriptor.DescriptorDownloader} and
- * {@link org.torproject.descriptor.DescriptorCollector}) and descriptor
- * sources for processing local descriptor data
- * ({@link org.torproject.descriptor.DescriptorReader} and
- * {@link org.torproject.descriptor.DescriptorParser}). General-purpose
- * types also include descriptor containers
- * ({@link org.torproject.descriptor.DescriptorRequest} and
- * {@link org.torproject.descriptor.DescriptorFile}) and the
- * superinterface for all provided descriptors
- * ({@link org.torproject.descriptor.Descriptor}).</li>
- *
- * <li>The first group of descriptors is published by relays and servers
- * in the Tor network. These interfaces include server descriptors
- * ({@link org.torproject.descriptor.ServerDescriptor} with subinterfaces
- * {@link org.torproject.descriptor.RelayServerDescriptor} and
- * {@link org.torproject.descriptor.BridgeServerDescriptor}), extra-info
- * descriptors ({@link org.torproject.descriptor.ExtraInfoDescriptor} with
- * subinterfaces
- * {@link org.torproject.descriptor.RelayExtraInfoDescriptor} and
- * {@link org.torproject.descriptor.BridgeExtraInfoDescriptor}),
- * microdescriptors which are derived from server descriptors by the
- * directory authorities
- * ({@link org.torproject.descriptor.Microdescriptor}), and helper types
- * for parts of the aforementioned descriptors
- * ({@link org.torproject.descriptor.BandwidthHistory}).</li>
- *
- * <li>The second group of descriptors is generated by authoritative
- * directory servers that form an opinion about relays and bridges in the
- * Tor network. These include descriptors specified in version 3 of the
- * directory protocol
- * ({@link org.torproject.descriptor.RelayNetworkStatusConsensus},
- * {@link org.torproject.descriptor.RelayNetworkStatusVote},
- * {@link org.torproject.descriptor.DirectoryKeyCertificate}, and helper
- * types for descriptor parts
- * {@link org.torproject.descriptor.DirSourceEntry},
- * {@link org.torproject.descriptor.NetworkStatusEntry}, and
- * {@link org.torproject.descriptor.DirectorySignature}), descriptors from
- * earlier directory protocol version 2
- * ({@link org.torproject.descriptor.RelayNetworkStatus}) and version 1
- * ({@link org.torproject.descriptor.RelayDirectory} and
- * {@link org.torproject.descriptor.RouterStatusEntry}), as well as
- * descriptors published by the bridge authority and sanitized by the
- * CollecTor service
- * ({@link org.torproject.descriptor.BridgeNetworkStatus}).</li>
- *
- * <li>The third group of descriptors is created by auxiliary services
- * connected to the Tor network rather than by the Tor software. This
- * group comprises descriptors by the bridge distribution service BridgeDB
- * ({@link org.torproject.descriptor.BridgePoolAssignment}), the exit list
- * service TorDNSEL ({@link org.torproject.descriptor.ExitList}), and the
- * performance measurement service Torperf
- * ({@link org.torproject.descriptor.TorperfResult}).</li>
- * </ol>
- *
- * @since 1.0.0
- */
-package org.torproject.descriptor;
-
diff --git a/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java b/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java
new file mode 100644
index 0000000..a52020a
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/benchmark/MeasurePerformance.java
@@ -0,0 +1,278 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.benchmark;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorReader;
+import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.Microdescriptor;
+import org.torproject.descriptor.NetworkStatusEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+import org.torproject.descriptor.ServerDescriptor;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+
+public class MeasurePerformance {
+
+ /* Check if all necessary files are available and then measure
+ * performance of some more or less common use cases. */
+ public static void main(String[] args) {
+ if (!filesAvailable()) {
+ return;
+ }
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[0]));
+ pause();
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[1]));
+ pause();
+ measureAverageAdvertisedBandwidth(new File(resDir, resPaths[2]));
+ pause();
+ measureCountriesV3Requests(new File(resDir, resPaths[3]));
+ pause();
+ measureCountriesV3Requests(new File(resDir, resPaths[4]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[5]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[6]));
+ pause();
+ measureAverageRelaysExit(new File(resDir, resPaths[7]));
+ measureFractionRelaysExit80Microdescriptors(
+ new File(resDir, resPaths[8]));
+ measureFractionRelaysExit80Microdescriptors(
+ new File(resDir, resPaths[9]));
+ }
+
+ private static File resDir = new File("res");
+ private static String[] resPaths = new String[] {
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11.tar.xz",
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11.tar",
+ "archive/relay-descriptors/server-descriptors/"
+ + "server-descriptors-2015-11",
+ "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar.xz",
+ "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11.tar.xz",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11.tar",
+ "archive/relay-descriptors/consensuses/consensuses-2015-11",
+ "archive/relay-descriptors/microdescs/microdescs-2015-11.tar.xz",
+ "archive/relay-descriptors/microdescs/microdescs-2015-11.tar"
+ };
+
+ private static boolean filesAvailable() {
+ if (!resDir.exists() || !resDir.isDirectory()) {
+ return false;
+ }
+ for (String resPath : resPaths) {
+ if (!(new File(resDir, resPath).exists())) {
+ System.err.println("Missing resource: " + resDir + "/" + resPath);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void pause() {
+ try {
+ Thread.sleep(15L * 1000L);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void measureAverageAdvertisedBandwidth(
+ File tarballFileOrDirectory) {
+ System.out.println("Starting measureAverageAdvertisedBandwidth");
+ long startedMillis = System.currentTimeMillis();
+ long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFileOrDirectory);
+ descriptorReader.addDirectory(tarballFileOrDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ServerDescriptor)) {
+ continue;
+ }
+ ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+ sumAdvertisedBandwidth += (long) Math.min(Math.min(
+ serverDescriptor.getBandwidthRate(),
+ serverDescriptor.getBandwidthBurst()),
+ serverDescriptor.getBandwidthObserved());
+ countedServerDescriptors++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureAverageAdvertisedBandwidth");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed server descriptors: %d%n",
+ countedServerDescriptors);
+ System.out.printf("Average advertised bandwidth: %d%n",
+ sumAdvertisedBandwidth / countedServerDescriptors);
+ System.out.printf("Time per server descriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedServerDescriptors));
+ }
+
+ private static void measureCountriesV3Requests(File tarballFile) {
+ System.out.println("Starting measureCountriesV3Requests");
+ long startedMillis = System.currentTimeMillis();
+ Set<String> countries = new HashSet<>();
+ long countedExtraInfoDescriptors = 0;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFile);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ExtraInfoDescriptor)) {
+ continue;
+ }
+ ExtraInfoDescriptor extraInfoDescriptor =
+ (ExtraInfoDescriptor) descriptor;
+ SortedMap<String, Integer> dirreqV3Reqs =
+ extraInfoDescriptor.getDirreqV3Reqs();
+ if (dirreqV3Reqs != null) {
+ countries.addAll(dirreqV3Reqs.keySet());
+ }
+ countedExtraInfoDescriptors++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureCountriesV3Requests");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed extra-info descriptors: %d%n",
+ countedExtraInfoDescriptors);
+ System.out.printf("Number of countries: %d%n",
+ countries.size());
+ System.out.printf("Time per extra-info descriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedExtraInfoDescriptors));
+ }
+
+ private static void measureAverageRelaysExit(
+ File tarballFileOrDirectory) {
+ System.out.println("Starting measureAverageRelaysExit");
+ long startedMillis = System.currentTimeMillis();
+ long totalRelaysWithExitFlag = 0L, totalRelays = 0L,
+ countedConsensuses = 0L;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFileOrDirectory);
+ descriptorReader.addDirectory(tarballFileOrDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
+ continue;
+ }
+ RelayNetworkStatusConsensus consensus =
+ (RelayNetworkStatusConsensus) descriptor;
+ for (NetworkStatusEntry entry :
+ consensus.getStatusEntries().values()) {
+ if (entry.getFlags().contains("Exit")) {
+ totalRelaysWithExitFlag++;
+ }
+ totalRelays++;
+ }
+ countedConsensuses++;
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending measureAverageRelaysExit");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed consensuses: %d%n", countedConsensuses);
+ System.out.printf("Total number of status entries: %d%n",
+ totalRelays);
+ System.out.printf("Total number of status entries with Exit flag: "
+ + "%d%n", totalRelaysWithExitFlag);
+ System.out.printf("Average number of relays with Exit Flag: %.2f%n",
+ (double) totalRelaysWithExitFlag / (double) totalRelays);
+ System.out.printf("Time per consensus: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedConsensuses));
+ }
+
+ private static void measureFractionRelaysExit80Microdescriptors(
+ File tarballFile) {
+ System.out.println("Starting "
+ + "measureFractionRelaysExit80Microdescriptors");
+ long startedMillis = System.currentTimeMillis();
+ long totalRelaysWithExitFlag = 0L, countedMicrodescriptors = 0L;
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addTarball(tarballFile);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof Microdescriptor)) {
+ continue;
+ }
+ countedMicrodescriptors++;
+ Microdescriptor microdescriptor =
+ (Microdescriptor) descriptor;
+ String defaultPolicy = microdescriptor.getDefaultPolicy();
+ if (defaultPolicy == null) {
+ continue;
+ }
+ boolean accept = "accept".equals(
+ microdescriptor.getDefaultPolicy());
+ for (String ports : microdescriptor.getPortList().split(",")) {
+ if (ports.contains("-")) {
+ String[] parts = ports.split("-");
+ int from = Integer.parseInt(parts[0]);
+ int to = Integer.parseInt(parts[1]);
+ if (from <= 80 && to >= 80) {
+ if (accept) {
+ totalRelaysWithExitFlag++;
+ }
+ } else if (to > 80) {
+ if (!accept) {
+ totalRelaysWithExitFlag++;
+ }
+ break;
+ }
+ } else if ("80".equals(ports)) {
+ if (accept) {
+ totalRelaysWithExitFlag++;
+ }
+ break;
+ }
+ }
+ }
+ }
+ long endedMillis = System.currentTimeMillis();
+ System.out.println("Ending "
+ + "measureFractionRelaysExit80Microdescriptors");
+ System.out.printf("Total time: %d millis%n",
+ endedMillis - startedMillis);
+ System.out.printf("Processed microdescriptors: %d%n",
+ countedMicrodescriptors);
+ System.out.printf("Total number of microdescriptors that exit to 80: "
+ + "%d%n", totalRelaysWithExitFlag);
+ System.out.printf("Average number of relays that exit to 80: %.2f%n",
+ (double) totalRelaysWithExitFlag
+ / (double) countedMicrodescriptors);
+ System.out.printf("Time per microdescriptor: %.6f millis%n",
+ ((double) (endedMillis - startedMillis))
+ / ((double) countedMicrodescriptors));
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
new file mode 100644
index 0000000..0847e13
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
@@ -0,0 +1,151 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.BridgeNetworkStatus;
+import org.torproject.descriptor.DescriptorParseException;
+
+/* Test parsing of bridge network statuses. Some of the parsing code is
+ * already tested in the consensus/vote-parsing tests. */
+public class BridgeNetworkStatusTest {
+
+ /* Helper class to build a bridge network status based on default data
+ * and modifications requested by test methods. */
+ private static class StatusBuilder {
+ private String fileName = "20151121-173936-"
+ + "4A0CCD2DDC7995083D73F5D667100C8A5831F16D";
+ private static BridgeNetworkStatus
+ createWithFileName(String fileName)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.fileName = fileName;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private String publishedLine = "published 2015-11-21 17:39:36";
+ private static BridgeNetworkStatus
+ createWithPublishedLine(String line)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.publishedLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private String flagThresholdsLine = "flag-thresholds "
+ + "stable-uptime=3105080 stable-mtbf=2450615 fast-speed=55000 "
+ + "guard-wfu=98.000% guard-tk=691200 guard-bw-inc-exits=337000 "
+ + "guard-bw-exc-exits=339000 enough-mtbf=1 "
+ + "ignoring-advertised-bws=0";
+ private static BridgeNetworkStatus
+ createWithFlagThresholdsLine(String line)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.flagThresholdsLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ true);
+ }
+ private List<String> statusEntries = new ArrayList<>();
+ private String unrecognizedHeaderLine = null;
+ protected static BridgeNetworkStatus
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.unrecognizedHeaderLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static BridgeNetworkStatus
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.unrecognizedStatusEntryLine = line;
+ return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+ failUnrecognizedDescriptorLines);
+ }
+
+ private StatusBuilder() {
+ this.statusEntries.add("r Unnamed ABk0wg4j6BLCdZKleVtmNrfzJGI "
+ + "bh7gVU1Cz6+JG+7j4qGsF4prDi8 2015-11-21 15:46:25 "
+ + "10.153.163.200 443 0\ns Fast Running Stable Valid\n"
+ + "w Bandwidth=264\np reject 1-65535");
+ }
+ private byte[] buildStatus() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendStatusEntries(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.flagThresholdsLine != null) {
+ sb.append(this.flagThresholdsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ }
+
+ @Test()
+ public void testSampleStatus() throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ BridgeNetworkStatus status =
+ new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName, true);
+ assertEquals(1448127576000L, status.getPublishedMillis());
+ assertEquals(3105080L, status.getStableUptime());
+ assertEquals(2450615L, status.getStableMtbf());
+ assertEquals(55000L, status.getFastBandwidth());
+ assertEquals(98.0, status.getGuardWfu(), 0.001);
+ assertEquals(691200L, status.getGuardTk());
+ assertEquals(337000L, status.getGuardBandwidthIncludingExits());
+ assertEquals(339000L, status.getGuardBandwidthExcludingExits());
+ assertEquals(1, status.getEnoughMtbfInfo());
+ assertEquals(0, status.getIgnoringAdvertisedBws());
+ assertEquals(264, status.getStatusEntries().get(
+ "001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
+ assertTrue(status.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test()
+ public void testPublishedNoLine() throws DescriptorParseException {
+ BridgeNetworkStatus status =
+ StatusBuilder.createWithPublishedLine(null);
+ assertEquals(1448127576000L, status.getPublishedMillis());
+ }
+
+ @Test()
+ public void testFlagThresholdsNoLine() throws DescriptorParseException {
+ BridgeNetworkStatus status =
+ StatusBuilder.createWithFlagThresholdsLine(null);
+ assertEquals(-1L, status.getStableUptime());
+ assertEquals(-1L, status.getStableMtbf());
+ assertEquals(-1L, status.getFastBandwidth());
+ assertEquals(-1.0, status.getGuardWfu(), 0.001);
+ assertEquals(-1L, status.getGuardTk());
+ assertEquals(-1L, status.getGuardBandwidthIncludingExits());
+ assertEquals(-1L, status.getGuardBandwidthExcludingExits());
+ assertEquals(-1, status.getEnoughMtbfInfo());
+ assertEquals(-1, status.getIgnoringAdvertisedBws());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
new file mode 100644
index 0000000..29a2d47
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
@@ -0,0 +1,321 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* Helper class to build a consensus based on default data and
+ * modifications requested by test methods. */
+public class ConsensusBuilder {
+ String networkStatusVersionLine = "network-status-version 3";
+ protected static RelayNetworkStatusConsensus
+ createWithNetworkStatusVersionLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.networkStatusVersionLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String voteStatusLine = "vote-status consensus";
+ protected static RelayNetworkStatusConsensus
+ createWithVoteStatusLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.voteStatusLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String consensusMethodLine = "consensus-method 11";
+ protected static RelayNetworkStatusConsensus
+ createWithConsensusMethodLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.consensusMethodLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String validAfterLine = "valid-after 2011-11-30 09:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithValidAfterLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.validAfterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithFreshUntilLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.freshUntilLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String validUntilLine = "valid-until 2011-11-30 12:00:00";
+ protected static RelayNetworkStatusConsensus
+ createWithValidUntilLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.validUntilLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String votingDelayLine = "voting-delay 300 300";
+ protected static RelayNetworkStatusConsensus
+ createWithVotingDelayLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.votingDelayLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ String clientVersionsLine = "client-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ protected static RelayNetworkStatusConsensus
+ createWithClientVersionsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.clientVersionsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ String serverVersionsLine = "server-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ protected static RelayNetworkStatusConsensus
+ createWithServerVersionsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.serverVersionsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String packageLines = null;
+ protected static RelayNetworkStatusConsensus
+ createWithPackageLines(String lines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.packageLines = lines;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String knownFlagsLine = "known-flags Authority BadExit Exit "
+ + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
+ protected static RelayNetworkStatusConsensus
+ createWithKnownFlagsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.knownFlagsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String paramsLine = "params "
+ + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
+ + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
+ + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
+ + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
+ protected static RelayNetworkStatusConsensus
+ createWithParamsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.paramsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ List<String> dirSources = new ArrayList<>();
+ List<String> statusEntries = new ArrayList<>();
+ private String directoryFooterLine = "directory-footer";
+ protected void setDirectoryFooterLine(String line) {
+ this.directoryFooterLine = line;
+ }
+ protected static RelayNetworkStatusConsensus
+ createWithDirectoryFooterLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.directoryFooterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private String bandwidthWeightsLine = "bandwidth-weights Wbd=285 "
+ + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=1021 Wee=10000 "
+ + "Weg=1021 Wem=10000 Wgb=10000 Wgd=8694 Wgg=10000 Wgm=10000 "
+ + "Wmb=10000 Wmd=285 Wme=0 Wmg=0 Wmm=10000";
+ protected void setBandwidthWeightsLine(String line) {
+ this.bandwidthWeightsLine = line;
+ }
+ protected static RelayNetworkStatusConsensus
+ createWithBandwidthWeightsLine(String line)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.bandwidthWeightsLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+ private List<String> directorySignatures = new ArrayList<>();
+ protected void addDirectorySignature(String directorySignatureString) {
+ this.directorySignatures.add(directorySignatureString);
+ }
+ private String unrecognizedHeaderLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedHeaderLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirSourceLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedDirSourceLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedDirSourceLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedStatusEntryLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedFooterLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedFooterLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedFooterLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirectorySignatureLine = null;
+ protected static RelayNetworkStatusConsensus
+ createWithUnrecognizedDirectorySignatureLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.unrecognizedDirectorySignatureLine = line;
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ failUnrecognizedDescriptorLines);
+ }
+
+ protected ConsensusBuilder() {
+ this.dirSources.add("dir-source tor26 "
+ + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 "
+ + "86.59.21.38 80 443\ncontact Peter Palfrader\nvote-digest "
+ + "0333880AA67ED7E07C11108656D0C8D6DD1C7E5D");
+ this.dirSources.add("dir-source ides "
+ + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 216.224.124.114 "
+ + "216.224.124.114 9030 9090\ncontact Mike Perry "
+ + "<mikeperryTAfsckedTODorg>\nvote-digest "
+ + "1A8827ECD53184F7A771EFA9B3D30DC473FE8670");
+ this.statusEntries.add("r ANONIONROUTER "
+ + "AHhuQ8zFQJdT8l42Axxc6m6kNwI yEMZ5B/JQixNZgC1+2rLe0pR9rU "
+ + "2011-11-30 02:52:58 93.128.66.111 24051 24052\ns Exit Fast "
+ + "Named Running V2Dir Valid\nv Tor 0.2.2.34\nw "
+ + "Bandwidth=1100\np reject 25,119,135-139,6881-6999");
+ this.statusEntries.add("r Magellan AHlabo2RwnD8I7MPOIpJVVPgGJQ "
+ + "rB/7uzI4mU38bZ9cSXEy+Z/4Cuk 2011-11-30 05:37:35 "
+ + "188.177.149.216 9001 9030\ns Fast Named Running V2Dir "
+ + "Valid\nv Tor 0.2.2.34\nw Bandwidth=367\np reject 1-65535");
+ this.directorySignatures.add("directory-signature "
+ + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ + "3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "NYRcTWAMRiYYiGW0hIbzeZKU6sefg98AwwXrQUCudO8wfA1cfgttTDoscB9I"
+ + "TbOY\nr+c30jV/qQCMamTAEDGgJTw8KghI32vytupKallI1EjCOF8UvL1UnA"
+ + "LgpaR7sZ3W\n7WQZVVrWDtnYaULOEKfwnGnRC7WwE+YRSysbzwwCVs0=\n"
+ + "-----END SIGNATURE-----");
+ this.directorySignatures.add("directory-signature "
+ + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 "
+ + "D5C30C15BB3F1DA27669C2D88439939E8F418FCF\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "DzFPj3vyYrCv0W3r8qDPJPlmeLnadY+drjWkdOqO66Ih/hAWBb9KcBJAX1sX"
+ + "aDA7\n/iSaDhduBXuJdcu8lbmMP8d6uYBdRjHXqWDXySUZAkSfPB4JJPNGvf"
+ + "oQA/qeby7E\n5374pPPL6WwCLJHkKtk21S9oHDmFBdlZq7JWQelWlVM=\n"
+ + "-----END SIGNATURE-----");
+ }
+ protected byte[] buildConsensus() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendDirSources(sb);
+ this.appendStatusEntries(sb);
+ this.appendFooter(sb);
+ this.appendDirectorySignatures(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.networkStatusVersionLine != null) {
+ sb.append(this.networkStatusVersionLine).append("\n");
+ }
+ if (this.voteStatusLine != null) {
+ sb.append(this.voteStatusLine).append("\n");
+ }
+ if (this.consensusMethodLine != null) {
+ sb.append(this.consensusMethodLine).append("\n");
+ }
+ if (this.validAfterLine != null) {
+ sb.append(this.validAfterLine).append("\n");
+ }
+ if (this.freshUntilLine != null) {
+ sb.append(this.freshUntilLine).append("\n");
+ }
+ if (this.validUntilLine != null) {
+ sb.append(this.validUntilLine).append("\n");
+ }
+ if (this.votingDelayLine != null) {
+ sb.append(this.votingDelayLine).append("\n");
+ }
+ if (this.clientVersionsLine != null) {
+ sb.append(this.clientVersionsLine).append("\n");
+ }
+ if (this.serverVersionsLine != null) {
+ sb.append(this.serverVersionsLine).append("\n");
+ }
+ if (this.packageLines != null) {
+ sb.append(this.packageLines).append("\n");
+ }
+ if (this.knownFlagsLine != null) {
+ sb.append(this.knownFlagsLine).append("\n");
+ }
+ if (this.paramsLine != null) {
+ sb.append(this.paramsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendDirSources(StringBuilder sb) {
+ for (String dirSource : this.dirSources) {
+ sb.append(dirSource).append("\n");
+ }
+ if (this.unrecognizedDirSourceLine != null) {
+ sb.append(this.unrecognizedDirSourceLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ private void appendFooter(StringBuilder sb) {
+ if (this.directoryFooterLine != null) {
+ sb.append(this.directoryFooterLine).append("\n");
+ }
+ if (this.bandwidthWeightsLine != null) {
+ sb.append(this.bandwidthWeightsLine).append("\n");
+ }
+ if (this.unrecognizedFooterLine != null) {
+ sb.append(this.unrecognizedFooterLine).append("\n");
+ }
+ }
+ private void appendDirectorySignatures(StringBuilder sb) {
+ for (String directorySignature : this.directorySignatures) {
+ sb.append(directorySignature).append("\n");
+ }
+ if (this.unrecognizedDirectorySignatureLine != null) {
+ sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
+ }
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
new file mode 100644
index 0000000..fde8e57
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
@@ -0,0 +1,134 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.SortedMap;
+
+import org.junit.Test;
+
+public class DescriptorCollectorImplTest {
+
+ private static final String REMOTE_DIRECTORY_CONSENSUSES =
+ "/recent/relay-descriptors/consensuses/";
+
+ @Test()
+ public void testOneFile() {
+ String remoteFilename = "2015-05-24-12-00-00-consensus";
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"" + remoteFilename + "\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">24-May-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertSame(1, remoteFiles.size());
+ assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
+ remoteFiles.firstKey());
+ assertEquals((Long) 1432469280000L,
+ remoteFiles.get(remoteFiles.firstKey()));
+ }
+
+ @Test()
+ public void testSameFileTwoTimestampsLastWins() {
+ String remoteFilename = "2015-05-24-12-00-00-consensus";
+ String firstTimestamp = "24-May-2015 12:04";
+ String secondTimestamp = "24-May-2015 12:08";
+ String lineFormat = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"%s\">2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">%s </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>\n";
+ String directoryListing = String.format(lineFormat + lineFormat,
+ remoteFilename, firstTimestamp, remoteFilename, secondTimestamp);
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertSame(1, remoteFiles.size());
+ assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
+ remoteFiles.firstKey());
+ assertEquals((Long) 1432469280000L,
+ remoteFiles.get(remoteFiles.firstKey()));
+ }
+
+ @Test()
+ public void testSubDirectoryOnly() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/folder.gif\" alt=\"[DIR]\"></td><td>"
+ + "<a href=\"subdir/\">subdir/</a></td>"
+ + "<td align=\"right\">27-May-2015 14:07 </td>"
+ + "<td align=\"right\"> - </td><td> </td></tr>";
+ DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
+ SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testParentDirectoryOnly() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/back.gif\" alt=\"[DIR]\"></td><td>"
+ + "<a href=\"/recent/relay-descriptors/\">Parent Directory</a>"
+ + "</td><td> </td><td align=\"right\"> - </td>"
+ + "<td> </td></tr>";
+ DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
+ SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testUnexpectedDateFormat() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">2015-05-24 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNotNull(remoteFiles);
+ assertTrue(remoteFiles.isEmpty());
+ }
+
+ @Test()
+ public void testInvalidDate() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">34-May-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNull(remoteFiles);
+ }
+
+ @Test()
+ public void testInvalidLocaleDe() {
+ String directoryListing = "<tr><td valign=\"top\">"
+ + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
+ + "<a href=\"2015-05-24-12-00-00-consensus\">"
+ + "2015-05-24-12-00-00-consensus</a></td>"
+ + "<td align=\"right\">24-Mai-2015 12:08 </td>"
+ + "<td align=\"right\">1.5M</td><td> </td></tr>";
+ SortedMap<String, Long> remoteFiles =
+ new DescriptorCollectorImpl().parseDirectoryListing(
+ REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
+ assertNull(remoteFiles);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
new file mode 100644
index 0000000..a563857
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
@@ -0,0 +1,131 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExitListEntry;
+
+public class ExitListImplTest {
+
+ @Test()
+ public void testAnnotatedInput() throws Exception {
+ ExitListImpl result = new ExitListImpl((tordnselAnnotation + input)
+ .getBytes("US-ASCII"), fileName, false);
+ assertEquals("Expected one annotation.", 1,
+ result.getAnnotations().size());
+ assertEquals(tordnselAnnotation.substring(0, 18),
+ result.getAnnotations().get(0));
+ assertEquals(1441065722000L, result.getDownloadedMillis());
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ assertEquals("Found: " + result.getExitListEntries(), 7,
+ result.getExitListEntries().size());
+ assertEquals("Found: " + result.getEntries(), 5,
+ result.getEntries().size());
+ }
+
+ @Test()
+ public void testMultipleOldExitAddresses() throws Exception {
+ ExitListImpl result = new ExitListImpl(
+ (tordnselAnnotation + multiExitAddressInput)
+ .getBytes("US-ASCII"), fileName, false);
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ assertEquals("Found: " + result.getExitListEntries(),
+ 3, result.getExitListEntries().size());
+ Map<String, Long> testMap = new HashMap();
+ testMap.put("81.7.17.171", 1441044592000L);
+ testMap.put("81.7.17.172", 1441044652000L);
+ testMap.put("81.7.17.173", 1441044712000L);
+ for (ExitListEntry ele : result.getExitListEntries()) {
+ Map<String, Long> map = ele.getExitAddresses();
+ assertEquals("Found: " + map, 1, map.size());
+ Map.Entry<String, Long> ea = map.entrySet().iterator().next();
+ assertTrue("Map: " + testMap,
+ testMap.keySet().contains(ea.getKey()));
+ assertTrue("Map: " + testMap + " exitaddress: " + ea,
+ testMap.values().contains(ea.getValue()));
+ testMap.remove(ea.getKey());
+ }
+ assertTrue("Map: " + testMap, testMap.isEmpty());
+ }
+
+ @Test()
+ public void testMultipleExitAddresses() throws Exception {
+ ExitListImpl result = new ExitListImpl(
+ (tordnselAnnotation + multiExitAddressInput)
+ .getBytes("US-ASCII"), fileName, false);
+ assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
+ result.getUnrecognizedLines().isEmpty());
+ Map<String, Long> map = result.getEntries()
+ .iterator().next().getExitAddresses();
+ assertEquals("Found: " + map, 3, map.size());
+ assertTrue("Map: " + map, map.containsKey("81.7.17.171"));
+ assertTrue("Map: " + map, map.containsKey("81.7.17.172"));
+ assertTrue("Map: " + map, map.containsKey("81.7.17.173"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testInsufficientInput0() throws Exception {
+ new ExitListImpl((tordnselAnnotation + insufficientInput[0])
+ .getBytes("US-ASCII"), fileName, false);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testInsufficientInput1() throws Exception {
+ new ExitListImpl((tordnselAnnotation + insufficientInput[1])
+ .getBytes("US-ASCII"), fileName, false);
+ }
+
+ private static final String tordnselAnnotation = "@type tordnsel 1.0\n";
+ private static final String fileName = "2015-09-01-00-02-02";
+ private static final String[] insufficientInput = new String[] {
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n",
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n" };
+
+ private static final String multiExitAddressInput =
+ "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
+ + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n";
+ private static final String input = "Downloaded 2015-09-01 00:02:02\n"
+ + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
+ + "Published 2015-08-31 16:17:30\n"
+ + "LastStatus 2015-08-31 17:03:18\n"
+ + "ExitAddress 162.247.72.201 2015-08-31 17:09:23\n"
+ + "ExitNode 0098C475875ABC4AA864738B1D1079F711C38287\n"
+ + "Published 2015-08-31 13:59:24\n"
+ + "LastStatus 2015-08-31 15:03:20\n"
+ + "ExitAddress 162.248.160.151 2015-08-31 15:07:27\n"
+ + "ExitNode 00C4B4731658D3B4987132A3F77100CFCB190D97\n"
+ + "Published 2015-08-31 17:47:52\n"
+ + "LastStatus 2015-08-31 18:03:17\n"
+ + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
+ + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
+ + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n"
+ + "ExitNode 00F2D93EBAF2F51D6EE4DCB0F37D91D72F824B16\n"
+ + "Published 2015-08-31 14:39:05\n"
+ + "LastStatus 2015-08-31 16:02:18\n"
+ + "ExitAddress 23.239.18.57 2015-08-31 16:06:07\n"
+ + "ExitNode 011B1D1E876B2C835D01FB9D407F2E00B28077F6\n"
+ + "Published 2015-08-31 05:14:35\n"
+ + "LastStatus 2015-08-31 06:03:29\n"
+ + "ExitAddress 104.131.51.150 2015-08-31 06:04:07\n";
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
new file mode 100644
index 0000000..6843196
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
@@ -0,0 +1,1737 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.junit.Test;
+import org.torproject.descriptor.BridgeExtraInfoDescriptor;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.ExtraInfoDescriptor;
+import org.torproject.descriptor.RelayExtraInfoDescriptor;
+
+/* Test parsing of extra-info descriptors. */
+public class ExtraInfoDescriptorImplTest {
+
+ /* Helper class to build a descriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String extraInfoLine = "extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26";
+ private static ExtraInfoDescriptor createWithExtraInfoLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.extraInfoLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String publishedLine = "published 2012-02-11 09:08:36";
+ private static ExtraInfoDescriptor createWithPublishedLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.publishedLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String writeHistoryLine = "write-history 2012-02-11 09:03:39 "
+ + "(900 s) 4713350144,4723824640,4710717440,4572675072";
+ private static ExtraInfoDescriptor createWithWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.writeHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String readHistoryLine = "read-history 2012-02-11 09:03:39 "
+ + "(900 s) 4707695616,4699666432,4650004480,4489718784";
+ private static ExtraInfoDescriptor createWithReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.readHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqWriteHistoryLine = "dirreq-write-history "
+ + "2012-02-11 09:03:39 (900 s) 81281024,64996352,60625920,"
+ + "67922944";
+ private static ExtraInfoDescriptor createWithDirreqWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqWriteHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqReadHistoryLine = "dirreq-read-history "
+ + "2012-02-11 09:03:39 (900 s) 17074176,16235520,16005120,"
+ + "16209920";
+ private static ExtraInfoDescriptor createWithDirreqReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqReadHistoryLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoipDbDigestLine = null;
+ private static ExtraInfoDescriptor createWithGeoipDbDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoipDbDigestLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoip6DbDigestLine = null;
+ private static ExtraInfoDescriptor createWithGeoip6DbDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoip6DbDigestLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String geoipStatsLines = null;
+ private static ExtraInfoDescriptor createWithGeoipStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.geoipStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String dirreqStatsLines = null;
+ private static ExtraInfoDescriptor createWithDirreqStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.dirreqStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String entryStatsLines = null;
+ private static ExtraInfoDescriptor createWithEntryStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.entryStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String cellStatsLines = null;
+ private static ExtraInfoDescriptor createWithCellStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.cellStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String connBiDirectLine = null;
+ private static ExtraInfoDescriptor createWithConnBiDirectLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.connBiDirectLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String exitStatsLines = null;
+ private static ExtraInfoDescriptor createWithExitStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.exitStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String bridgeStatsLines = null;
+ private static ExtraInfoDescriptor createWithBridgeStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.bridgeStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hidservStatsLines = null;
+ private static ExtraInfoDescriptor createWithHidservStatsLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hidservStatsLines = lines;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String unrecognizedLine = null;
+ private static ExtraInfoDescriptor createWithUnrecognizedLine(
+ String line, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.unrecognizedLine = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private byte[] nonAsciiLineBytes = null;
+ private static ExtraInfoDescriptor createWithNonAsciiLineBytes(
+ byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.nonAsciiLineBytes = lineBytes;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String routerSignatureLines = "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----";
+ private static ExtraInfoDescriptor createWithRouterSignatureLines(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerSignatureLines = line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String identityEd25519Lines = null,
+ masterKeyEd25519Line = null, routerSigEd25519Line = null;
+ private static ExtraInfoDescriptor createWithEd25519Lines(
+ String identityEd25519Lines, String masterKeyEd25519Line,
+ String routerSigEd25519Line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.identityEd25519Lines = identityEd25519Lines;
+ db.masterKeyEd25519Line = masterKeyEd25519Line;
+ db.routerSigEd25519Line = routerSigEd25519Line;
+ return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.extraInfoLine != null) {
+ sb.append(this.extraInfoLine).append("\n");
+ }
+ if (this.identityEd25519Lines != null) {
+ sb.append(this.identityEd25519Lines).append("\n");
+ }
+ if (this.masterKeyEd25519Line != null) {
+ sb.append(this.masterKeyEd25519Line).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.writeHistoryLine != null) {
+ sb.append(this.writeHistoryLine).append("\n");
+ }
+ if (this.readHistoryLine != null) {
+ sb.append(this.readHistoryLine).append("\n");
+ }
+ if (this.dirreqWriteHistoryLine != null) {
+ sb.append(this.dirreqWriteHistoryLine).append("\n");
+ }
+ if (this.dirreqReadHistoryLine != null) {
+ sb.append(this.dirreqReadHistoryLine).append("\n");
+ }
+ if (this.geoipDbDigestLine != null) {
+ sb.append(this.geoipDbDigestLine).append("\n");
+ }
+ if (this.geoip6DbDigestLine != null) {
+ sb.append(this.geoip6DbDigestLine).append("\n");
+ }
+ if (this.geoipStatsLines != null) {
+ sb.append(this.geoipStatsLines).append("\n");
+ }
+ if (this.dirreqStatsLines != null) {
+ sb.append(this.dirreqStatsLines).append("\n");
+ }
+ if (this.entryStatsLines != null) {
+ sb.append(this.entryStatsLines).append("\n");
+ }
+ if (this.cellStatsLines != null) {
+ sb.append(this.cellStatsLines).append("\n");
+ }
+ if (this.connBiDirectLine != null) {
+ sb.append(this.connBiDirectLine).append("\n");
+ }
+ if (this.exitStatsLines != null) {
+ sb.append(this.exitStatsLines).append("\n");
+ }
+ if (this.bridgeStatsLines != null) {
+ sb.append(this.bridgeStatsLines).append("\n");
+ }
+ if (this.hidservStatsLines != null) {
+ sb.append(this.hidservStatsLines).append("\n");
+ }
+ if (this.unrecognizedLine != null) {
+ sb.append(this.unrecognizedLine).append("\n");
+ }
+ if (this.nonAsciiLineBytes != null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(sb.toString().getBytes());
+ baos.write(this.nonAsciiLineBytes);
+ baos.write("\n".getBytes());
+ if (this.routerSignatureLines != null) {
+ baos.write(this.routerSignatureLines.getBytes());
+ }
+ return baos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ if (this.routerSigEd25519Line != null) {
+ sb.append(this.routerSigEd25519Line).append("\n");
+ }
+ if (this.routerSignatureLines != null) {
+ sb.append(this.routerSignatureLines).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ /* Helper class to build a set of geoip-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class GeoipStatsBuilder {
+ private String geoipStartTimeLine = "geoip-start-time 2012-02-10 "
+ + "18:32:51";
+ private static ExtraInfoDescriptor createWithGeoipStartTimeLine(
+ String line) throws DescriptorParseException {
+ GeoipStatsBuilder gsb = new GeoipStatsBuilder();
+ gsb.geoipStartTimeLine = line;
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ gsb.buildGeoipStatsLines());
+ }
+ private String geoipClientOriginsLine = "geoip-client-origins "
+ + "de=1152,cn=896,us=712,it=504,ru=352,fr=208,gb=208,ir=200";
+ private static ExtraInfoDescriptor createWithGeoipClientOriginsLine(
+ String line) throws DescriptorParseException {
+ GeoipStatsBuilder gsb = new GeoipStatsBuilder();
+ gsb.geoipClientOriginsLine = line;
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ gsb.buildGeoipStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithGeoipStatsLines(
+ new GeoipStatsBuilder().buildGeoipStatsLines());
+ }
+ private String buildGeoipStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.geoipStartTimeLine != null) {
+ sb.append(this.geoipStartTimeLine).append("\n");
+ }
+ if (this.geoipClientOriginsLine != null) {
+ sb.append(this.geoipClientOriginsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of dirreq-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class DirreqStatsBuilder {
+ private String dirreqStatsEndLine = "dirreq-stats-end 2012-02-11 "
+ + "00:59:53 (86400 s)";
+ private static ExtraInfoDescriptor createWithDirreqStatsEndLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqStatsEndLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3IpsLine = "dirreq-v3-ips us=1544,de=1056,"
+ + "it=1032,fr=784,es=640,ru=440,br=312,gb=272,kr=224,sy=192";
+ private static ExtraInfoDescriptor createWithDirreqV3IpsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3IpsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2IpsLine = "dirreq-v2-ips ";
+ private static ExtraInfoDescriptor createWithDirreqV2IpsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2IpsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3ReqsLine = "dirreq-v3-reqs us=1744,de=1224,"
+ + "it=1080,fr=832,es=664,ru=536,br=344,gb=296,kr=272,in=216";
+ private static ExtraInfoDescriptor createWithDirreqV3ReqsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3ReqsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2ReqsLine = "dirreq-v2-reqs ";
+ private static ExtraInfoDescriptor createWithDirreqV2ReqsLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2ReqsLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3RespLine = "dirreq-v3-resp ok=10848,"
+ + "not-enough-sigs=8,unavailable=0,not-found=0,not-modified=0,"
+ + "busy=80";
+ private static ExtraInfoDescriptor createWithDirreqV3RespLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3RespLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2RespLine = "dirreq-v2-resp ok=0,unavailable=0,"
+ + "not-found=1576,not-modified=0,busy=0";
+ private static ExtraInfoDescriptor createWithDirreqV2RespLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2RespLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2ShareLine = "dirreq-v2-share 0.37%";
+ private static ExtraInfoDescriptor createWithDirreqV2ShareLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2ShareLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3ShareLine = "dirreq-v3-share 0.37%";
+ private static ExtraInfoDescriptor createWithDirreqV3ShareLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3ShareLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3DirectDlLine = "dirreq-v3-direct-dl "
+ + "complete=36,timeout=4,running=0,min=7538,d1=20224,d2=28950,"
+ + "q1=40969,d3=55786,d4=145813,md=199164,d6=267230,d7=480900,"
+ + "q3=481049,d8=531276,d9=778086,max=15079428";
+ private static ExtraInfoDescriptor createWithDirreqV3DirectDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3DirectDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2DirectDlLine = "dirreq-v2-direct-dl "
+ + "complete=0,timeout=0,running=0";
+ private static ExtraInfoDescriptor createWithDirreqV2DirectDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2DirectDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV3TunneledDlLine = "dirreq-v3-tunneled-dl "
+ + "complete=10608,timeout=204,running=4,min=507,d1=20399,"
+ + "d2=27588,q1=29292,d3=30889,d4=40624,md=59967,d6=103333,"
+ + "d7=161170,q3=209415,d8=256711,d9=452503,max=23417777";
+ private static ExtraInfoDescriptor createWithDirreqV3TunneledDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV3TunneledDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private String dirreqV2TunneledDlLine = "dirreq-v2-tunneled-dl "
+ + "complete=0,timeout=0,running=0";
+ private static ExtraInfoDescriptor createWithDirreqV2TunneledDlLine(
+ String line) throws DescriptorParseException {
+ DirreqStatsBuilder dsb = new DirreqStatsBuilder();
+ dsb.dirreqV2TunneledDlLine = line;
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ dsb.buildDirreqStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithDirreqStatsLines(
+ new DirreqStatsBuilder().buildDirreqStatsLines());
+ }
+ private String buildDirreqStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.dirreqStatsEndLine != null) {
+ sb.append(this.dirreqStatsEndLine).append("\n");
+ }
+ if (this.dirreqV3IpsLine != null) {
+ sb.append(this.dirreqV3IpsLine).append("\n");
+ }
+ if (this.dirreqV2IpsLine != null) {
+ sb.append(this.dirreqV2IpsLine).append("\n");
+ }
+ if (this.dirreqV3ReqsLine != null) {
+ sb.append(this.dirreqV3ReqsLine).append("\n");
+ }
+ if (this.dirreqV2ReqsLine != null) {
+ sb.append(this.dirreqV2ReqsLine).append("\n");
+ }
+ if (this.dirreqV3RespLine != null) {
+ sb.append(this.dirreqV3RespLine).append("\n");
+ }
+ if (this.dirreqV2RespLine != null) {
+ sb.append(this.dirreqV2RespLine).append("\n");
+ }
+ if (this.dirreqV2ShareLine != null) {
+ sb.append(this.dirreqV2ShareLine).append("\n");
+ }
+ if (this.dirreqV3ShareLine != null) {
+ sb.append(this.dirreqV3ShareLine).append("\n");
+ }
+ if (this.dirreqV3DirectDlLine != null) {
+ sb.append(this.dirreqV3DirectDlLine).append("\n");
+ }
+ if (this.dirreqV2DirectDlLine != null) {
+ sb.append(this.dirreqV2DirectDlLine).append("\n");
+ }
+ if (this.dirreqV3TunneledDlLine != null) {
+ sb.append(this.dirreqV3TunneledDlLine).append("\n");
+ }
+ if (this.dirreqV2TunneledDlLine != null) {
+ sb.append(this.dirreqV2TunneledDlLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of entry-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class EntryStatsBuilder {
+ private String entryStatsEndLine = "entry-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithEntryStatsEndLine(
+ String line) throws DescriptorParseException {
+ EntryStatsBuilder esb = new EntryStatsBuilder();
+ esb.entryStatsEndLine = line;
+ return DescriptorBuilder.createWithEntryStatsLines(
+ esb.buildEntryStatsLines());
+ }
+ private String entryIpsLine = "entry-ips ir=25368,us=15744,it=14816,"
+ + "de=13256,es=8280,fr=8120,br=5176,sy=4760,ru=4504,sa=4216,"
+ + "gb=3152,pl=2928,nl=2208,kr=1856,ca=1792,ua=1272,in=1192";
+ private static ExtraInfoDescriptor createWithEntryIpsLine(
+ String line) throws DescriptorParseException {
+ EntryStatsBuilder esb = new EntryStatsBuilder();
+ esb.entryIpsLine = line;
+ return DescriptorBuilder.createWithEntryStatsLines(
+ esb.buildEntryStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithEntryStatsLines(
+ new EntryStatsBuilder().buildEntryStatsLines());
+ }
+ private String buildEntryStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.entryStatsEndLine != null) {
+ sb.append(this.entryStatsEndLine).append("\n");
+ }
+ if (this.entryIpsLine != null) {
+ sb.append(this.entryIpsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of cell-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class CellStatsBuilder {
+ private String cellStatsEndLine = "cell-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithCellStatsEndLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellStatsEndLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellProcessedCellsLine = "cell-processed-cells "
+ + "1441,11,6,4,2,1,1,1,1,1";
+ private static ExtraInfoDescriptor createWithCellProcessedCellsLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellProcessedCellsLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellQueuedCellsLine = "cell-queued-cells "
+ + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00";
+ private static ExtraInfoDescriptor createWithCellQueuedCellsLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellQueuedCellsLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellTimeInQueueLine = "cell-time-in-queue "
+ + "524,1,1,0,0,25,0,0,0,0";
+ private static ExtraInfoDescriptor createWithCellTimeInQueueLine(
+ String line) throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellTimeInQueueLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private String cellCircuitsPerDecileLine = "cell-circuits-per-decile "
+ + "866";
+ private static ExtraInfoDescriptor
+ createWithCellCircuitsPerDecileLine(String line)
+ throws DescriptorParseException {
+ CellStatsBuilder csb = new CellStatsBuilder();
+ csb.cellCircuitsPerDecileLine = line;
+ return DescriptorBuilder.createWithCellStatsLines(
+ csb.buildCellStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithCellStatsLines(
+ new CellStatsBuilder().buildCellStatsLines());
+ }
+ private String buildCellStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.cellStatsEndLine != null) {
+ sb.append(this.cellStatsEndLine).append("\n");
+ }
+ if (this.cellProcessedCellsLine != null) {
+ sb.append(this.cellProcessedCellsLine).append("\n");
+ }
+ if (this.cellQueuedCellsLine != null) {
+ sb.append(this.cellQueuedCellsLine).append("\n");
+ }
+ if (this.cellTimeInQueueLine != null) {
+ sb.append(this.cellTimeInQueueLine).append("\n");
+ }
+ if (this.cellCircuitsPerDecileLine != null) {
+ sb.append(this.cellCircuitsPerDecileLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of exit-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class ExitStatsBuilder {
+ private String exitStatsEndLine = "exit-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithExitStatsEndLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitStatsEndLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitKibibytesWrittenLine = "exit-kibibytes-written "
+ + "25=74647,80=31370,443=20577,49755=23,52563=12,52596=1111,"
+ + "57528=4,60912=11,61351=6,64811=3365,other=2592";
+ private static ExtraInfoDescriptor createWithExitKibibytesWrittenLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitKibibytesWrittenLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitKibibytesReadLine = "exit-kibibytes-read "
+ + "25=35562,80=1254256,443=110279,49755=9396,52563=1911,"
+ + "52596=648,57528=1188,60912=1427,61351=1824,64811=14,"
+ + "other=3054";
+ private static ExtraInfoDescriptor createWithExitKibibytesReadLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitKibibytesReadLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private String exitStreamsOpenedLine = "exit-streams-opened "
+ + "25=369748,80=64212,443=151660,49755=4,52563=4,52596=4,57528=4,"
+ + "60912=4,61351=4,64811=4,other=1212";
+ private static ExtraInfoDescriptor createWithExitStreamsOpenedLine(
+ String line) throws DescriptorParseException {
+ ExitStatsBuilder esb = new ExitStatsBuilder();
+ esb.exitStreamsOpenedLine = line;
+ return DescriptorBuilder.createWithExitStatsLines(
+ esb.buildExitStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithExitStatsLines(
+ new ExitStatsBuilder().buildExitStatsLines());
+ }
+ private String buildExitStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.exitStatsEndLine != null) {
+ sb.append(this.exitStatsEndLine).append("\n");
+ }
+ if (this.exitKibibytesWrittenLine != null) {
+ sb.append(this.exitKibibytesWrittenLine).append("\n");
+ }
+ if (this.exitKibibytesReadLine != null) {
+ sb.append(this.exitKibibytesReadLine).append("\n");
+ }
+ if (this.exitStreamsOpenedLine != null) {
+ sb.append(this.exitStreamsOpenedLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of bridge-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class BridgeStatsBuilder {
+ private String bridgeStatsEndLine = "bridge-stats-end 2012-02-11 "
+ + "01:59:39 (86400 s)";
+ private static ExtraInfoDescriptor createWithBridgeStatsEndLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeStatsEndLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpsLine = "bridge-ips ir=24,sy=16,??=8,cn=8,"
+ + "de=8,es=8,fr=8,gb=8,in=8,jp=8,kz=8,nl=8,ua=8,us=8,vn=8,za=8";
+ private static ExtraInfoDescriptor createWithBridgeIpsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpVersionsLine = "bridge-ip-versions v4=8,v6=16";
+ private static ExtraInfoDescriptor createWithBridgeIpVersionsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpVersionsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private String bridgeIpTransportsLine = "bridge-ip-transports "
+ + "<OR>=8,obfs2=792,obfs3=1728";
+ private static ExtraInfoDescriptor createWithBridgeIpTransportsLine(
+ String line) throws DescriptorParseException {
+ BridgeStatsBuilder bsb = new BridgeStatsBuilder();
+ bsb.bridgeIpTransportsLine = line;
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ bsb.buildBridgeStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithBridgeStatsLines(
+ new BridgeStatsBuilder().buildBridgeStatsLines());
+ }
+ private String buildBridgeStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.bridgeStatsEndLine != null) {
+ sb.append(this.bridgeStatsEndLine).append("\n");
+ }
+ if (this.bridgeIpsLine != null) {
+ sb.append(this.bridgeIpsLine).append("\n");
+ }
+ if (this.bridgeIpVersionsLine != null) {
+ sb.append(this.bridgeIpVersionsLine).append("\n");
+ }
+ if (this.bridgeIpTransportsLine != null) {
+ sb.append(this.bridgeIpTransportsLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ /* Helper class to build a set of hidserv-stats lines based on default
+ * data and modifications requested by test methods. */
+ private static class HidservStatsBuilder {
+ private String hidservStatsEndLine = "hidserv-stats-end 2015-12-03 "
+ + "14:26:56 (86400 s)";
+ private static ExtraInfoDescriptor createWithHidservStatsEndLine(
+ String line) throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservStatsEndLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private String hidservRendRelayedCellsLine =
+ "hidserv-rend-relayed-cells 36474281 delta_f=2048 epsilon=0.30 "
+ + "bin_size=1024";
+ private static ExtraInfoDescriptor
+ createWithHidservRendRelayedCellsLine(String line)
+ throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservRendRelayedCellsLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private String hidservDirOnionsSeenLine = "hidserv-dir-onions-seen "
+ + "-3 delta_f=8 epsilon=0.30 bin_size=8";
+ private static ExtraInfoDescriptor createWithHidservDirOnionsSeenLine(
+ String line) throws DescriptorParseException {
+ HidservStatsBuilder hsb = new HidservStatsBuilder();
+ hsb.hidservDirOnionsSeenLine = line;
+ return DescriptorBuilder.createWithHidservStatsLines(
+ hsb.buildHidservStatsLines());
+ }
+ private static ExtraInfoDescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ return DescriptorBuilder.createWithHidservStatsLines(
+ new HidservStatsBuilder().buildHidservStatsLines());
+ }
+ private String buildHidservStatsLines() {
+ StringBuilder sb = new StringBuilder();
+ if (this.hidservStatsEndLine != null) {
+ sb.append(this.hidservStatsEndLine).append("\n");
+ }
+ if (this.hidservRendRelayedCellsLine != null) {
+ sb.append(this.hidservRendRelayedCellsLine).append("\n");
+ }
+ if (this.hidservDirOnionsSeenLine != null) {
+ sb.append(this.hidservDirOnionsSeenLine).append("\n");
+ }
+ String lines = sb.toString();
+ if (lines.endsWith("\n")) {
+ lines = lines.substring(0, lines.length() - 1);
+ }
+ return lines;
+ }
+ }
+
+ @Test()
+ public void testSampleDescriptor() throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ ExtraInfoDescriptor descriptor =
+ new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ assertNotNull(descriptor.getWriteHistory());
+ assertEquals(1328951019000L, descriptor.getWriteHistory().
+ getHistoryEndMillis());
+ assertEquals(900L, descriptor.getWriteHistory().getIntervalLength());
+ assertEquals(4572675072L, (long) descriptor.getWriteHistory().
+ getBandwidthValues().get(1328951019000L));
+ assertNotNull(descriptor.getReadHistory());
+ assertNotNull(descriptor.getDirreqWriteHistory());
+ assertNotNull(descriptor.getDirreqReadHistory());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoLineMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine(null);
+ }
+
+ @Test()
+ public void testExtraInfoOpt() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ }
+
+ @Test()
+ public void testExtraInfoNicknameTwoSpaces()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ assertEquals("chaoscomputerclub5", descriptor.getNickname());
+ assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
+ descriptor.getFingerprint());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoLineNotFirst()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8\n"
+ + "extra-info chaoscomputerclub5 "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameInvalidChar() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub% A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5ReallyLongNickname "
+ + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintG() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 G9C039A5FD02FCA06303DCFAABE25C5912C63B26");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C6");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoLine("extra-info "
+ + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C63B26"
+ + "A9C0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine(null);
+ }
+
+ @Test()
+ public void testPublishedOpt() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-02-11 09:08:36");
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ }
+
+ @Test()
+ public void testPublishedMillis() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-02-11 09:08:36.123");
+ assertEquals(1328951316000L, descriptor.getPublishedMillis());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNegativeBytes()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-02-11 09:03:39 (900 s) "
+ + "-4713350144,-4723824640,-4710717440,-4572675072");
+ }
+
+ @Test()
+ public void testReadHistoryTabInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (900\ts) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test()
+ public void testReadHistoryTabIntervalBytes()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (900 s)\t"
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testReadHistoryNegativeInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (-900 s) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test()
+ public void testReadHistoryNonStandardInterval()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithReadHistoryLine("read-history "
+ + "2012-02-11 09:03:39 (1800 s) "
+ + "4707695616,4699666432,4650004480,4489718784");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqWriteHistoryMissingBytesBegin()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqWriteHistoryLine(
+ "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
+ + ",64996352,60625920,67922944");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqWriteHistoryMissingBytesMiddle()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqWriteHistoryLine(
+ "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
+ + "81281024,,60625920,67922944");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqReadHistoryMissingBytesEnd()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithDirreqReadHistoryLine(
+ "dirreq-read-history 2012-02-11 09:03:39 (900 s) "
+ + "17074176,16235520,16005120,");
+ }
+
+ @Test()
+ public void testGeoipDbDigestValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithGeoipDbDigestLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
+ descriptor.getGeoipDbDigest());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestTooShort()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301C");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestIllegalChars()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
+ + "&%6A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipDbDigestMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest");
+ }
+
+ @Test()
+ public void testGeoip6DbDigestValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithGeoip6DbDigestLine("geoip6-db-digest "
+ + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
+ assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
+ descriptor.getGeoip6DbDigest());
+ }
+
+ @Test()
+ public void testGeoipStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = GeoipStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328898771000L, descriptor.getGeoipStartTimeMillis());
+ SortedMap<String, Integer> ips = descriptor.getGeoipClientOrigins();
+ assertNotNull(ips);
+ assertEquals(1152, ips.get("de").intValue());
+ assertEquals(896, ips.get("cn").intValue());
+ assertFalse(ips.containsKey("pl"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipStartTimeDateOnly()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipStartTimeLine("geoip-start-time "
+ + "2012-02-10");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsDash()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de-1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsZero()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=zero,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsNone()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=none,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsOther()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,other=200");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsQuestionMarks()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,??=200");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsCapital()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins DE=1152,CN=896,US=712,IT=504,RU=352,FR=208,"
+ + "GB=208,IR=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingBegin()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins ,cn=896,us=712,it=504,ru=352,fr=208,gb=208,"
+ + "ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingMiddle()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,ir=200");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testGeoipClientOriginsMissingEnd()
+ throws DescriptorParseException {
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
+ + "gb=208,");
+ }
+
+ @Test()
+ public void testGeoipClientOriginsDuplicate()
+ throws DescriptorParseException {
+ /* dir-spec.txt doesn't say anything about duplicate country codes, so
+ * this line is valid, even though it leads to a somewhat undefined
+ * parse result. */
+ GeoipStatsBuilder.createWithGeoipClientOriginsLine(
+ "geoip-client-origins de=1152,de=952,cn=896,us=712,it=504,"
+ + "ru=352,fr=208,gb=208,ir=200");
+ }
+
+ @Test()
+ public void testDirreqStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DirreqStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328921993000L, descriptor.getDirreqStatsEndMillis());
+ assertEquals(86400L, descriptor.getDirreqStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getDirreqV3Ips();
+ assertNotNull(ips);
+ assertEquals(1544, ips.get("us").intValue());
+ assertFalse(ips.containsKey("no"));
+ assertTrue(descriptor.getDirreqV2Ips().isEmpty());
+ SortedMap<String, Integer> reqs = descriptor.getDirreqV3Reqs();
+ assertEquals(832, reqs.get("fr").intValue());
+ assertTrue(descriptor.getDirreqV2Reqs().isEmpty());
+ SortedMap<String, Integer> resp = descriptor.getDirreqV3Resp();
+ assertEquals(10848, resp.get("ok").intValue());
+ assertEquals(8, resp.get("not-enough-sigs").intValue());
+ resp = descriptor.getDirreqV2Resp();
+ assertEquals(1576, resp.get("not-found").intValue());
+ assertEquals(0.37, descriptor.getDirreqV2Share(), 0.0001);
+ assertEquals(0.37, descriptor.getDirreqV3Share(), 0.0001);
+ SortedMap<String, Integer> dl = descriptor.getDirreqV3DirectDl();
+ assertEquals(36, dl.get("complete").intValue());
+ dl = descriptor.getDirreqV2DirectDl();
+ assertEquals(0, dl.get("timeout").intValue());
+ dl = descriptor.getDirreqV3TunneledDl();
+ assertEquals(10608, dl.get("complete").intValue());
+ dl = descriptor.getDirreqV2TunneledDl();
+ assertEquals(0, dl.get("complete").intValue());
+ }
+
+ @Test()
+ public void testDirreqStatsIntervalTwoDays()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqStatsEndLine("dirreq-stats-end "
+ + "2012-02-11 00:59:53 (172800 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3IpsThreeLetterCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3IpsLine("dirreq-v3-ips "
+ + "usa=1544");
+ }
+
+ @Test()
+ public void testDirreqV2IpsDigitCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2IpsLine("dirreq-v2-ips 00=8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3ReqsOneLetterCountry()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3ReqsLine("dirreq-v3-reqs "
+ + "u=1744");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2ReqsNoNumber()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2ReqsLine("dirreq-v2-reqs us=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3RespTwoEqualSigns()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3RespLine("dirreq-v3-resp "
+ + "ok==10848");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2RespNull()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2RespLine("dirreq-v2-resp "
+ + "ok=null");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV2ShareComma()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2ShareLine("dirreq-v2-share "
+ + "0,37%");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3ShareNoPercent()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3ShareLine("dirreq-v3-share "
+ + "0.37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3DirectDlSpace()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3DirectDlLine(
+ "dirreq-v3-direct-dl complete 36");
+ }
+
+ @Test()
+ public void testDirreqV2DirectDlNegative()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2DirectDlLine(
+ "dirreq-v2-direct-dl complete=-8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3TunneledDlTooLarge()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV3TunneledDlLine(
+ "dirreq-v3-tunneled-dl complete=2147483648");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirreqV3TunneledDlDouble()
+ throws DescriptorParseException {
+ DirreqStatsBuilder.createWithDirreqV2TunneledDlLine(
+ "dirreq-v2-tunneled-dl complete=0.001");
+ }
+
+ @Test()
+ public void testEntryStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = EntryStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getEntryStatsEndMillis());
+ assertEquals(86400L, descriptor.getEntryStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getEntryIps();
+ assertNotNull(ips);
+ assertEquals(25368, ips.get("ir").intValue());
+ assertFalse(ips.containsKey("no"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEntryStatsEndNoDate() throws DescriptorParseException {
+ EntryStatsBuilder.createWithEntryStatsEndLine("entry-stats-end "
+ + "01:59:39 (86400 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEntryStatsIpsSemicolon()
+ throws DescriptorParseException {
+ EntryStatsBuilder.createWithEntryIpsLine("entry-ips "
+ + "ir=25368;us=15744");
+ }
+
+ @Test()
+ public void testCellStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = CellStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getCellStatsEndMillis());
+ assertEquals(86400L, descriptor.getCellStatsIntervalLength());
+ List<Integer> processedCells = descriptor.getCellProcessedCells();
+ assertEquals(10, processedCells.size());
+ assertEquals(1441, processedCells.get(0).intValue());
+ assertEquals(11, processedCells.get(1).intValue());
+ List<Double> queuedCells = descriptor.getCellQueuedCells();
+ assertEquals(10, queuedCells.size());
+ assertEquals(3.29, queuedCells.get(0), 0.001);
+ assertEquals(0.00, queuedCells.get(1), 0.001);
+ List<Integer> timeInQueue = descriptor.getCellTimeInQueue();
+ assertEquals(10, timeInQueue.size());
+ assertEquals(524, timeInQueue.get(0).intValue());
+ assertEquals(1, timeInQueue.get(1).intValue());
+ assertEquals(866, descriptor.getCellCircuitsPerDecile());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellStatsEndNoSeconds()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellStatsEndLine("cell-stats-end "
+ + "2012-02-11 01:59:39 (86400)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellProcessedCellsNineComma()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellProcessedCellsLine(
+ "cell-processed-cells 1441,11,6,4,2,1,1,1,1,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellProcessedCellsEleven()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellQueuedCellsLine("cell-queued-cells "
+ + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellTimeInQueueDouble()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellTimeInQueueLine("cell-time-in-queue "
+ + "524.0,1.0,1.0,0.0,0.0,25.0,0.0,0.0,0.0,0.0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCellCircuitsPerDecileNegative()
+ throws DescriptorParseException {
+ CellStatsBuilder.createWithCellCircuitsPerDecileLine(
+ "cell-circuits-per-decile -866");
+ }
+
+ @Test()
+ public void testConnBiDirectValid()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithConnBiDirectLine("conn-bi-direct 2012-02-11 01:59:39 "
+ + "(86400 s) 42173,1591,1310,1744");
+ assertEquals(1328925579000L,
+ descriptor.getConnBiDirectStatsEndMillis());
+ assertEquals(86400L, descriptor.getConnBiDirectStatsIntervalLength());
+ assertEquals(42173, descriptor.getConnBiDirectBelow());
+ assertEquals(1591, descriptor.getConnBiDirectRead());
+ assertEquals(1310, descriptor.getConnBiDirectWrite());
+ assertEquals(1744, descriptor.getConnBiDirectBoth());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConnBiDirectStatsFive()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithConnBiDirectLine("conn-bi-direct "
+ + "2012-02-11 01:59:39 (86400 s) 42173,1591,1310,1744,42");
+ }
+
+ @Test()
+ public void testExitStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = ExitStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getExitStatsEndMillis());
+ assertEquals(86400L, descriptor.getExitStatsIntervalLength());
+ String[] ports = new String[] { "25", "80", "443", "49755",
+ "52563", "52596", "57528", "60912", "61351", "64811", "other" };
+ int[] writtenValues = new int[] { 74647, 31370, 20577, 23, 12, 1111,
+ 4, 11, 6, 3365, 2592 };
+ int i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitKibibytesWritten().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(writtenValues[i++], e.getValue().intValue());
+ }
+ int[] readValues = new int[] { 35562, 1254256, 110279, 9396, 1911,
+ 648, 1188, 1427, 1824, 14, 3054 };
+ i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitKibibytesRead().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(readValues[i++], e.getValue().intValue());
+ }
+ int[] streamsValues = new int[] { 369748, 64212, 151660, 4, 4, 4, 4,
+ 4, 4, 4, 1212 };
+ i = 0;
+ for (Map.Entry<String, Long> e :
+ descriptor.getExitStreamsOpened().entrySet()) {
+ assertEquals(ports[i], e.getKey());
+ assertEquals(streamsValues[i++], e.getValue().intValue());
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsEndNoSeconds()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitStatsEndLine("exit-stats-end "
+ + "2012-02-11 01:59 (86400 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsWrittenNegativePort()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesWrittenLine(
+ "exit-kibibytes-written -25=74647");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsWrittenUnknown()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesWrittenLine(
+ "exit-kibibytes-written unknown=74647");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitStatsReadNegativeBytes()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesReadLine(
+ "exit-kibibytes-read 25=-35562");
+ }
+
+ @Test()
+ public void testExitStatsReadTooLarge()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitKibibytesReadLine(
+ "exit-kibibytes-read other=2282907805");
+ }
+
+ @Test()
+ public void testExitStatsStreamsTooLarge()
+ throws DescriptorParseException {
+ ExitStatsBuilder.createWithExitStreamsOpenedLine(
+ "exit-streams-opened 25=2147483648");
+ }
+
+ @Test()
+ public void testBridgeStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = BridgeStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1328925579000L, descriptor.getBridgeStatsEndMillis());
+ assertEquals(86400L, descriptor.getBridgeStatsIntervalLength());
+ SortedMap<String, Integer> ips = descriptor.getBridgeIps();
+ assertNotNull(ips);
+ assertEquals(24, ips.get("ir").intValue());
+ assertEquals(16, ips.get("sy").intValue());
+ assertFalse(ips.containsKey("no"));
+ SortedMap<String, Integer> ver = descriptor.getBridgeIpVersions();
+ assertNotNull(ver);
+ assertEquals(8, ver.get("v4").intValue());
+ assertEquals(16, ver.get("v6").intValue());
+ assertFalse(ver.containsKey("v8"));
+ SortedMap<String, Integer> trans = descriptor.getBridgeIpTransports();
+ assertNotNull(trans);
+ assertEquals(8, trans.get("<OR>").intValue());
+ assertEquals(792, trans.get("obfs2").intValue());
+ assertEquals(1728, trans.get("obfs3").intValue());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeStatsEndIntervalZero()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeStatsEndLine("bridge-stats-end "
+ + "2012-02-11 01:59:39 (0 s)");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpsLine("bridge-ips ir=24.5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpsNonAsciiKeyword()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
+ 0x14, (byte) 0xfe, 0x18, // non-ascii chars
+ 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2d, // "bridge-"
+ 0x69, 0x70, 0x73 }, false); // "ips" (no newline)
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpVersionsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpVersionsLine(
+ "bridge-ip-versions v4=24.5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBridgeIpTransportsDouble()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpTransportsLine(
+ "bridge-ip-transports obfs2=24.5");
+ }
+
+ @Test()
+ public void testBridgeIpTransportsUnderscore()
+ throws DescriptorParseException {
+ BridgeStatsBuilder.createWithBridgeIpTransportsLine(
+ "bridge-ip-transports meek=32,obfs3_websocket=8,websocket=64");
+ }
+
+ @Test()
+ public void testHidservStatsValid() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor = HidservStatsBuilder.
+ createWithDefaultLines();
+ assertEquals(1449152816000L, descriptor.getHidservStatsEndMillis());
+ assertEquals(86400L, descriptor.getHidservStatsIntervalLength());
+ assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
+ 0.0001);
+ Map<String, Double> params =
+ descriptor.getHidservRendRelayedCellsParameters();
+ assertTrue(params.containsKey("delta_f"));
+ assertEquals(2048.0, params.remove("delta_f"), 0.0001);
+ assertTrue(params.containsKey("epsilon"));
+ assertEquals(0.3, params.remove("epsilon"), 0.0001);
+ assertTrue(params.containsKey("bin_size"));
+ assertEquals(1024.0, params.remove("bin_size"), 0.0001);
+ assertTrue(params.isEmpty());
+ assertEquals(-3.0, descriptor.getHidservDirOnionsSeen(), 0.0001);
+ params = descriptor.getHidservDirOnionsSeenParameters();
+ assertTrue(params.containsKey("delta_f"));
+ assertEquals(8.0, params.remove("delta_f"), 0.0001);
+ assertTrue(params.containsKey("epsilon"));
+ assertEquals(0.3, params.remove("epsilon"), 0.0001);
+ assertTrue(params.containsKey("bin_size"));
+ assertEquals(8.0, params.remove("bin_size"), 0.0001);
+ assertTrue(params.isEmpty());
+ }
+
+ @Test()
+ public void testHidservStatsEndLineMissing()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ HidservStatsBuilder.createWithHidservStatsEndLine(null);
+ assertEquals(-1L, descriptor.getHidservStatsEndMillis());
+ assertEquals(-1L, descriptor.getHidservStatsIntervalLength());
+ }
+
+ @Test()
+ public void testHidservRendRelayedCellsNoParams()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ HidservStatsBuilder.createWithHidservRendRelayedCellsLine(
+ "hidserv-rend-relayed-cells 36474281");
+ assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
+ 0.0001);
+ assertTrue(
+ descriptor.getHidservRendRelayedCellsParameters().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHidservDirOnionsSeenCommaSeparatedParams()
+ throws DescriptorParseException {
+ HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
+ "hidserv-dir-onions-seen -3 delta_f=8,epsilon=0.30,bin_size=8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHidservDirOnionsSeenNoDoubleParams()
+ throws DescriptorParseException {
+ HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
+ "hidserv-dir-onions-seen -3 delta_f=A epsilon=B bin_size=C");
+ }
+
+ @Test()
+ public void testRouterSignatureOpt()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("opt "
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "crypto lines are ignored anyway\n"
+ + "-----END SIGNATURE-----");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterSignatureNotLastLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----\npublished 2012-02-11 09:08:36");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ExtraInfoDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ private static final String IDENTITY_ED25519_LINES =
+ "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
+ + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
+ + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
+ + "\n"
+ + "-----END ED25519 CERT-----";
+
+ private static final String MASTER_KEY_ED25519_LINE =
+ "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
+
+ private static final String ROUTER_SIG_ED25519_LINE =
+ "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
+ + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
+
+ @Test()
+ public void testEd25519() throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ assertEquals(IDENTITY_ED25519_LINES.substring(
+ IDENTITY_ED25519_LINES.indexOf("\n") + 1),
+ descriptor.getIdentityEd25519());
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ assertEquals(ROUTER_SIG_ED25519_LINE.substring(
+ ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getRouterSignatureEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityMasterKeyMismatch()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519IdentityMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(null,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
+ + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityEmptyCrypto()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519MasterKeyMissing()
+ throws DescriptorParseException {
+ ExtraInfoDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ null, ROUTER_SIG_ED25519_LINE);
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519MasterKeyDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519RouterSigMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519RouterSigDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
+ + ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testExtraInfoDigestSha256Relay()
+ throws DescriptorParseException {
+ byte[] descriptorBytes = ("extra-info Unnamed "
+ + "EA5B335055D2F03013FF030381F02B1C631EC723\n"
+ + "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiZRAenzZorGtx6xapoEeaqcLLOk3uWwJXTvOVLluSXXbRSZAQAgBADLN5"
+ + "wp\nCEOrRbshSbj1NDAUgc6cxU65M/Vx1x+b5+EXbkQZ5uiyB4pphVF5kPPT1P"
+ + "SleYqM\n8j+tlKh2i6+Xr0xScSPpmtG00/D0MoRlT7ZdaaaT5iw1DWDQCZ8BHG"
+ + "lAZwU=\n"
+ + "-----END ED25519 CERT-----\n"
+ + "published 2015-12-01 04:38:12\n"
+ + "write-history 2015-12-01 01:40:37 (14400 s) 88704000,60825600,"
+ + "61747200,76953600,61516800,59443200\n"
+ + "read-history 2015-12-01 01:40:37 (14400 s) 87321600,59443200,"
+ + "59904000,74880000,60364800,58060800\n"
+ + "router-sig-ed25519 c6eUeJs/SVjun3JhmjByEeWdRDyunSMAnGVhx71JiRj"
+ + "YzR8x5IcPebylG7m10wiolFxinvw78UhrrGo9Sq5ZBw\n"
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "oC2qFHCDOKSRoIPR86jdRxEYia390Z4d8fT0yr/1mg4RQ7lHmxlzFT6QxCswdX"
+ + "Ry\nvGNGR0wARySgyE+YKKWYn/Hp547JhhWd9Oc7BuFMY0XMvl/HOo+B9VjW+l"
+ + "nv6UBE\niqxx3C3Iw0ymohvOenyCUa/7TmsT7eVotDO57uIoGEc=\n"
+ + "-----END SIGNATURE-----\n"
+ + "").getBytes();
+ RelayExtraInfoDescriptor descriptor =
+ new RelayExtraInfoDescriptorImpl(descriptorBytes, true);
+ assertEquals("Pt1BtzfRwhYqGCDo8jjchS8nJP3ovrDyHGn+dqPbMgw",
+ descriptor.getExtraInfoDigestSha256());
+ }
+
+ @Test()
+ public void testExtraInfoDigestSha256Bridge()
+ throws DescriptorParseException {
+ byte[] descriptorBytes = ("extra-info idideditheconfig "
+ + "DC28749EC9E26E61DE492E46CD830379E9931B09\n"
+ + "master-key-ed25519 "
+ + "38FzmOIE6Mm85Ytx0MhFM6X9EuxWRUgb6HjyMGuO2AU\n"
+ + "published 2015-12-03 13:23:19\n"
+ + "write-history 2015-12-03 09:59:32 (14400 s) 53913600,52992000,"
+ + "53222400,53222400,53452800,53222400\n"
+ + "read-history 2015-12-03 09:59:32 (14400 s) 61056000,60364800,"
+ + "60364800,60134400,60595200,60364800\n"
+ + "geoip-db-digest 5BF366AD4A0572D82A1A0F6628AF8EF7725E3AB9\n"
+ + "geoip6-db-digest 212DE17D5A368DCAFA19B95F168BFFA101145A93\n"
+ + "router-digest-sha256 "
+ + "TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4\n"
+ + "router-digest 00B98F076B586272C3172B7F3DA29ADEE75F2ED8\n").getBytes();
+ BridgeExtraInfoDescriptor descriptor =
+ new BridgeExtraInfoDescriptorImpl(descriptorBytes, true);
+ assertEquals("TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4",
+ descriptor.getExtraInfoDigestSha256());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
new file mode 100644
index 0000000..abb51db
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
@@ -0,0 +1,82 @@
+package org.torproject.descriptor.impl;
+
+import org.junit.Test;
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.Microdescriptor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class MicrodescriptorImplTest {
+
+ /* Helper class to build a microdescriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String onionKeyLines = "onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALNZ4pNsHHkl7a+kFWbBmPHNAepjjvuhjTr1TaMB3UKuCRaXJmS2Qr"
+ + "CW\nkTmINqdQUccwb3ghb7EBZfDtCUvjcwMSEsRRTVIZqVQsYj6m3n1CegOc4o"
+ + "UutXaZ\nfkyty5XOgV4Qucx9wokzTMCHlO0V0x9y0FwFsK5Nb6ugqfQLLQ6XAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static Microdescriptor createWithDefaultLines()
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ return new MicrodescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyLine =
+ "ntor-onion-key PXLa7IGE+TzPDMsM5j9rFnDa37rd6kfZa5QuzqqJukw=";
+ private String idLine = "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8";
+ private static Microdescriptor createWithIdLine(String line)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.idLine = line;
+ return new MicrodescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.onionKeyLines != null) {
+ sb.append(this.onionKeyLines).append("\n");
+ }
+ if (this.ntorOnionKeyLine != null) {
+ sb.append(this.ntorOnionKeyLine).append("\n");
+ }
+ if (this.idLine != null) {
+ sb.append(this.idLine).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ @Test()
+ public void testDefaults() throws DescriptorParseException {
+ DescriptorBuilder.createWithDefaultLines();
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024TooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id rsa1024 AAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024TooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id ed25519 AAAAAAAAAAAAAAAAAAAAAA"
+ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa512() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine("id rsa512 "
+ + "bvegfGxp8k7T9QFpjPTrPaJTa/8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdEd25519Duplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithIdLine(
+ "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8\n"
+ + "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8");
+ }
+}
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
new file mode 100644
index 0000000..d864337
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
@@ -0,0 +1,1272 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+import org.junit.Test;
+import org.torproject.descriptor.NetworkStatusEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+
+/* TODO Add test cases for all lines starting with "opt ". */
+
+/* Test parsing of network status consensuses. The main focus is on
+ * making sure that the parser is as robust as possible and doesn't break,
+ * no matter what gets fed into it. A secondary focus is to ensure that
+ * a parsed consensus is fully compatible to dir-spec.txt. */
+public class RelayNetworkStatusConsensusImplTest {
+
+ /* Helper class to build a directory source based on default data and
+ * modifications requested by test methods. */
+ private static class DirSourceBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithDirSource(String dirSourceString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.dirSources.add(dirSourceString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String nickname = "gabelmoo";
+ private static RelayNetworkStatusConsensus
+ createWithNickname(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.nickname = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
+ private static RelayNetworkStatusConsensus
+ createWithIdentity(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.identity = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String hostName = "212.112.245.170";
+ private static RelayNetworkStatusConsensus
+ createWithHostName(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.hostName = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String address = "212.112.245.170";
+ private static RelayNetworkStatusConsensus
+ createWithAddress(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.address = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String dirPort = "80";
+ private static RelayNetworkStatusConsensus
+ createWithDirPort(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.dirPort = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String orPort = "443";
+ private static RelayNetworkStatusConsensus
+ createWithOrPort(String string)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.orPort = string;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String contactLine = "contact 4096R/C5AA446D Sebastian Hahn "
+ + "<tor(a)sebastianhahn.net>";
+ private static RelayNetworkStatusConsensus
+ createWithContactLine(String line)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.contactLine = line;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String voteDigestLine =
+ "vote-digest 0F398A5834D2C139E1D92310B09F814F243354D1";
+ private static RelayNetworkStatusConsensus
+ createWithVoteDigestLine(String line)
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.voteDigestLine = line;
+ return createWithDirSource(dsb.buildDirSource());
+ }
+ private String buildDirSource() {
+ StringBuilder sb = new StringBuilder();
+ String dirSourceLine = "dir-source " + this.nickname + " "
+ + this.identity + " " + this.hostName + " " + this.address + " "
+ + this.dirPort + " " + this.orPort;
+ sb.append(dirSourceLine).append("\n");
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.voteDigestLine != null) {
+ sb.append(this.voteDigestLine).append("\n");
+ }
+ String dirSourceWithTrailingNewLine = sb.toString();
+ String dirSource = dirSourceWithTrailingNewLine.substring(0,
+ dirSourceWithTrailingNewLine.length() - 1);
+ return dirSource;
+ }
+ }
+
+ /* Helper class to build a status entry based on default data and
+ * modifications requested by test methods. */
+ private static class StatusEntryBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithStatusEntry(String statusEntryString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(statusEntryString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String nickname = "right2privassy3";
+ private static RelayNetworkStatusConsensus
+ createWithNickname(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.nickname = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String fingerprintBase64 = "ADQ6gCT3DiFHKPDFr3rODBUI8HM";
+ private static RelayNetworkStatusConsensus
+ createWithFingerprintBase64(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.fingerprintBase64 = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String descriptorBase64 = "Yiti+nayuT2Efe2X1+M4nslwVuU";
+ private static RelayNetworkStatusConsensus
+ createWithDescriptorBase64(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.descriptorBase64 = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String publishedString = "2011-11-29 21:34:27";
+ private static RelayNetworkStatusConsensus
+ createWithPublishedString(String string)
+ throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.publishedString = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String address = "50.63.8.215";
+ private static RelayNetworkStatusConsensus
+ createWithAddress(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.address = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String orPort = "9023";
+ private static RelayNetworkStatusConsensus
+ createWithOrPort(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.orPort = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String dirPort = "0";
+ private static RelayNetworkStatusConsensus
+ createWithDirPort(String string) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.dirPort = string;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String sLine = "s Exit Fast Named Running Stable Valid";
+ private static RelayNetworkStatusConsensus
+ createWithSLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.sLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String vLine = "v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)";
+ private static RelayNetworkStatusConsensus
+ createWithVLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.vLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String wLine = "w Bandwidth=1";
+ private static RelayNetworkStatusConsensus
+ createWithWLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.wLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String pLine = "p accept 80,1194,1220,1293";
+ private static RelayNetworkStatusConsensus
+ createWithPLine(String line) throws DescriptorParseException {
+ StatusEntryBuilder seb = new StatusEntryBuilder();
+ seb.pLine = line;
+ return createWithStatusEntry(seb.buildStatusEntry());
+ }
+ private String buildStatusEntry() {
+ StringBuilder sb = new StringBuilder();
+ String rLine = "r " + nickname + " " + fingerprintBase64 + " "
+ + descriptorBase64 + " " + publishedString + " " + address + " "
+ + orPort + " " + dirPort;
+ sb.append(rLine).append("\n");
+ if (this.sLine != null) {
+ sb.append(this.sLine).append("\n");
+ }
+ if (this.vLine != null) {
+ sb.append(this.vLine).append("\n");
+ }
+ if (this.wLine != null) {
+ sb.append(this.wLine).append("\n");
+ }
+ if (this.pLine != null) {
+ sb.append(this.pLine).append("\n");
+ }
+ String statusEntryWithTrailingNewLine = sb.toString();
+ String statusEntry = statusEntryWithTrailingNewLine.substring(0,
+ statusEntryWithTrailingNewLine.length() - 1);
+ return statusEntry;
+ }
+ }
+
+ /* Helper class to build a directory signature based on default data and
+ * modifications requested by test methods. */
+ private static class DirectorySignatureBuilder {
+ private static RelayNetworkStatusConsensus
+ createWithDirectorySignature(String directorySignatureString)
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.addDirectorySignature(directorySignatureString);
+ return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
+ true);
+ }
+ private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
+ private static RelayNetworkStatusConsensus
+ createWithIdentity(String string)
+ throws DescriptorParseException {
+ DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
+ dsb.identity = string;
+ return createWithDirectorySignature(dsb.buildDirectorySignature());
+ }
+ private String signingKey =
+ "845CF1D0B370CA443A8579D18E7987E7E532F639";
+ private static RelayNetworkStatusConsensus
+ createWithSigningKey(String string)
+ throws DescriptorParseException {
+ DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
+ dsb.signingKey = string;
+ return createWithDirectorySignature(dsb.buildDirectorySignature());
+ }
+ private String buildDirectorySignature() {
+ String directorySignature = "directory-signature " + identity + " "
+ + signingKey + "\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "gE64+/4BH43v1+7jS9FK1tu2+94at8xhVSPn4O/PpOx7b0Yb+S1hac1QHAiS"
+ + "Ll+k\n"
+ + "6OiANKzhj54WHSrUswBPrOzjmKj0OhGXSAe5nHZUFX9a1MDQLDCoZBj536X9"
+ + "P3JG\n"
+ + "z89A+wrsN17I5490y66AEvws54BYZMbgRfp8HXn/0Ss=\n"
+ + "-----END SIGNATURE-----";
+ return directorySignature;
+ }
+ }
+
+ @Test()
+ public void testSampleConsensus() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertEquals(3, consensus.getNetworkStatusVersion());
+ assertEquals(11, consensus.getConsensusMethod());
+ assertEquals(1322643600000L, consensus.getValidAfterMillis());
+ assertEquals(1322647200000L, consensus.getFreshUntilMillis());
+ assertEquals(1322654400000L, consensus.getValidUntilMillis());
+ assertEquals(300L, consensus.getVoteSeconds());
+ assertEquals(300L, consensus.getDistSeconds());
+ assertTrue(consensus.getRecommendedClientVersions().contains(
+ "0.2.3.8-alpha"));
+ assertTrue(consensus.getRecommendedServerVersions().contains(
+ "0.2.3.8-alpha"));
+ assertTrue(consensus.getKnownFlags().contains("Running"));
+ assertEquals(30000, (int) consensus.getConsensusParams().get(
+ "CircuitPriorityHalflifeMsec"));
+ assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
+ "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getHostname());
+ assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
+ "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getIp());
+ assertTrue(consensus.containsStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894"));
+ assertEquals("188.177.149.216", consensus.getStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894").getAddress());
+ for (DirectorySignature signature : consensus.getSignatures()) {
+ if ("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4".equals(
+ signature.getIdentity())) {
+ assertEquals("3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86",
+ signature.getSigningKeyDigest());
+ }
+ }
+ assertEquals(285, (int) consensus.getBandwidthWeights().get("Wbd"));
+ assertTrue(consensus.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLineSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n ");
+ }
+
+ @Test()
+ public void testNetworkStatusVersionPrefixLineAtChar()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "@consensus\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "directory-footer\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLinePoundChar()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "#consensus\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersion42()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 42");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionFourtyTwo()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version FourtyTwo");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionSpaceBefore()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithNetworkStatusVersionLine(
+ " network-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusSpaceBefore() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(" vote-status consensus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status ");
+ }
+
+ @Test()
+ public void testVoteStatusConsensusOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status consensus ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusVote() throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine("vote-status vote");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusTheMagicVoteStatus()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithVoteStatusLine(
+ "vote-status TheMagicVoteStatus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodOneSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodEleven()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(
+ "consensus-method eleven");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodMinusOne()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNinePeriod()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine("consensus-method "
+ + "999999999999999999999999999999999999999999999999999999999999");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodTwoLines()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithConsensusMethodLine(
+ "consensus-method 1\nconsensus-method 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterLongAgo() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine("valid-after long ago");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterFeb30() throws DescriptorParseException {
+ ConsensusBuilder.createWithValidAfterLine(
+ "valid-after 2011-02-30 09:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithFreshUntilLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilAroundTen() throws DescriptorParseException {
+ ConsensusBuilder.createWithFreshUntilLine(
+ "fresh-until 2011-11-30 around ten");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidUntilTomorrowMorning()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithValidUntilLine(
+ "valid-until tomorrow morning");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayTriple() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine(
+ "voting-delay 300 300 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelaySingle() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneTwo() throws DescriptorParseException {
+ ConsensusBuilder.createWithVotingDelayLine("voting-delay one two");
+ }
+
+ @Test()
+ public void testClientServerVersionsNoLine()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.clientVersionsLine = null;
+ cb.serverVersionsLine = null;
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertNull(consensus.getRecommendedClientVersions());
+ assertNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testServerVersionsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithServerVersionsLine(null);
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testClientVersionsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine(null);
+ assertNull(consensus.getRecommendedClientVersions());
+ assertNotNull(consensus.getRecommendedServerVersions());
+ }
+
+ @Test()
+ public void testClientVersionsNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine("client-versions");
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertTrue(consensus.getRecommendedClientVersions().isEmpty());
+ }
+
+ @Test()
+ public void testClientVersionsOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithClientVersionsLine("client-versions ");
+ assertNotNull(consensus.getRecommendedClientVersions());
+ assertTrue(consensus.getRecommendedClientVersions().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsComma() throws DescriptorParseException {
+ ConsensusBuilder.createWithClientVersionsLine("client-versions ,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsCommaVersion()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithClientVersionsLine(
+ "client-versions ,0.2.2.34");
+ }
+
+ @Test()
+ public void testPackageNone() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(null);
+ assertNull(consensus.getPackageLines());
+ }
+
+ @Test()
+ public void testPackageOne() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http digest=digest";
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ assertEquals(packageLine.substring("package ".length()),
+ consensus.getPackageLines().get(0));
+ }
+
+ @Test()
+ public void testPackageTwo() throws DescriptorParseException {
+ List<String> packageLines = Arrays.asList(
+ "package shouldbesecond 0 http digest=digest",
+ "package outoforder 0 http digest=digest");
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithPackageLines(packageLines.get(0)
+ + "\n" + packageLines.get(1));
+ for (int i = 0; i < packageLines.size(); i++) {
+ assertEquals(packageLines.get(i).substring("package ".length()),
+ consensus.getPackageLines().get(i));
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPackageIncomplete() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http";
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoLine() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine("known-flags");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsOneSpace() throws DescriptorParseException {
+ ConsensusBuilder.createWithKnownFlagsLine("known-flags ");
+ }
+
+ @Test()
+ public void testParamsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine(null);
+ assertNull(consensus.getConsensusParams());
+ }
+
+ @Test()
+ public void testParamsNoSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test()
+ public void testParamsOneSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params ");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test()
+ public void testParamsThreeSpaces() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params ");
+ assertNotNull(consensus.getConsensusParams());
+ assertTrue(consensus.getConsensusParams().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsNoEqualSign() throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params key-value");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsOneTooLargeNegative()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params min=-2147483649");
+ }
+
+ @Test()
+ public void testParamsLargestNegative()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params min=-2147483648");
+ assertEquals(1, consensus.getConsensusParams().size());
+ assertEquals(-2147483648,
+ (int) consensus.getConsensusParams().get("min"));
+ }
+
+ @Test()
+ public void testParamsLargestPositive()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithParamsLine("params max=2147483647");
+ assertEquals(1, consensus.getConsensusParams().size());
+ assertEquals(2147483647,
+ (int) consensus.getConsensusParams().get("max"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParamsOneTooLargePositive()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithParamsLine("params max=2147483648");
+ }
+
+ @Test()
+ public void testDirSourceLegacyNickname()
+ throws DescriptorParseException {
+ DirSourceBuilder dsb = new DirSourceBuilder();
+ dsb.nickname = "gabelmoo-legacy";
+ dsb.identity = "81349FC1F2DBA2C2C11B45CB9706637D480AB913";
+ dsb.contactLine = null;
+ dsb.voteDigestLine = null;
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithDirSource(dsb.buildDirSource());
+ assertEquals(3, consensus.getDirSourceEntries().size());
+ assertTrue(consensus.getDirSourceEntries().get(
+ "81349FC1F2DBA2C2C11B45CB9706637D480AB913").isLegacy());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceNicknameTooLong()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithNickname("gabelmooisfinebutthisistoolong");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceIdentityTooShort()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceIdentityTooLong()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111"
+ + "4BB25CEF515B226ED03BB616EB2F60BEC8");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceHostnameMissing()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithHostName("");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceAddress24() throws DescriptorParseException {
+ DirSourceBuilder.createWithAddress("212.112.245");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceAddress40() throws DescriptorParseException {
+ DirSourceBuilder.createWithAddress("212.112.245.170.123");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPortMinusOne()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("-1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPort66666()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceDirPortOnions()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithDirPort("onions");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceOrPortOnions()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithOrPort("onions");
+ }
+
+ @Test()
+ public void testDirSourceContactNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine(null);
+ assertNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test()
+ public void testDirSourceContactLineNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine("contact");
+ assertNotNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test()
+ public void testDirSourceContactLineOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ DirSourceBuilder.createWithContactLine("contact ");
+ assertNotNull(consensus.getDirSourceEntries().get(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestNoLine()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestLineNoSpace()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine("vote-digest");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceVoteDigestLineOneSpace()
+ throws DescriptorParseException {
+ DirSourceBuilder.createWithVoteDigestLine("vote-digest ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameNotAllowedChars()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithNickname("notAll()wed");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithNickname("1234567890123456789tooLong");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64("TooShort");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintEndsWithEqualSign()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithFingerprintBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8HMAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorTooShort() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64("TooShort");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorEndsWithEqualSign()
+ throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64(
+ "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDescriptorTooLong() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDescriptorBase64(
+ "Yiti+nayuT2Efe2X1+M4nslwVuUAAAA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished1960() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPublishedString("1960-11-29 21:34:27");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished9999() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPublishedString("9999-11-29 21:34:27");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress256() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("256.63.8.215");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress24() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("50.63.8/24");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressV6() throws DescriptorParseException {
+ StatusEntryBuilder.createWithAddress("::1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPort66666() throws DescriptorParseException {
+ StatusEntryBuilder.createWithOrPort("66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortEighty() throws DescriptorParseException {
+ StatusEntryBuilder.createWithOrPort("eighty");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMinusOne() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDirPort("-1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortZero() throws DescriptorParseException {
+ StatusEntryBuilder.createWithDirPort("zero");
+ }
+
+ @Test()
+ public void testSLineNoSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithSLine("s");
+ assertTrue(consensus.getStatusEntry(
+ "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
+ }
+
+ @Test()
+ public void testSLineOneSpace() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithSLine("s ");
+ assertTrue(consensus.getStatusEntry(
+ "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoSLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.sLine = sb.sLine + "\n" + sb.sLine;
+ 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");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWLineOneSpace() throws DescriptorParseException {
+ StatusEntryBuilder.createWithWLine("w ");
+ }
+
+ @Test()
+ public void testWLineWarpSeven() throws DescriptorParseException {
+ StatusEntryBuilder.createWithWLine("w Warp=7");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoWLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.wLine = sb.wLine + "\n" + sb.wLine;
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+
+ @Test()
+ public void testWLineUnmeasured() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.wLine = "w Bandwidth=42424242 Unmeasured=1";
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
+ if (s.getBandwidth() == 42424242L) {
+ assertTrue(s.getUnmeasured());
+ }
+ }
+ }
+
+ @Test()
+ public void testWLineNotUnmeasured() throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ StatusEntryBuilder.createWithWLine("w Bandwidth=20");
+ for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
+ assertFalse(s.getUnmeasured());
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPolicy() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPorts() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p accept");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineNoPolicyNoPorts() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPLineProject() throws DescriptorParseException {
+ StatusEntryBuilder.createWithPLine("p project 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoPLines() throws DescriptorParseException {
+ StatusEntryBuilder sb = new StatusEntryBuilder();
+ sb.pLine = sb.pLine + "\n" + sb.pLine;
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.add(sb.buildStatusEntry());
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ }
+
+ @Test()
+ public void testNoStatusEntries() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.statusEntries.clear();
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertFalse(consensus.containsStatusEntry(
+ "00795A6E8D91C270FC23B30F388A495553E01894"));
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectoryFooterNoLine()
+ throws DescriptorParseException {
+ /* This breaks, because a bandwidth-weights line without a preceding
+ * directory-footer line is not allowed. */
+ ConsensusBuilder.createWithDirectoryFooterLine(null);
+ }
+
+ @Test()
+ public void testDirectoryFooterMissing()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.setDirectoryFooterLine(null);
+ cb.setBandwidthWeightsLine(null);
+ /* This does not break, because directory footers were optional before
+ * consensus method 9. */
+ RelayNetworkStatusConsensus consensus =
+ new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
+ assertNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testDirectoryFooterLineSpace()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithDirectoryFooterLine("directory-footer ");
+ }
+
+ @Test()
+ public void testBandwidthWeightsNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus =
+ ConsensusBuilder.createWithBandwidthWeightsLine(null);
+ assertNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testBandwidthWeightsLineNoSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithBandwidthWeightsLine("bandwidth-weights");
+ assertNotNull(consensus.getBandwidthWeights());
+ }
+
+ @Test()
+ public void testBandwidthWeightsLineOneSpace()
+ throws DescriptorParseException {
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithBandwidthWeightsLine("bandwidth-weights ");
+ assertNotNull(consensus.getBandwidthWeights());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthWeightsLineNoEqualSign()
+ throws DescriptorParseException {
+ ConsensusBuilder.createWithBandwidthWeightsLine(
+ "bandwidth-weights Wbd-285");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignatureIdentityTooShort()
+ throws DescriptorParseException {
+ DirectorySignatureBuilder.createWithIdentity("ED03BB616EB2F60");
+ }
+
+ @Test()
+ public void testDirectorySignatureIdentityTooLong()
+ throws DescriptorParseException {
+ /* This hex string has an unusual length of 58 hex characters, but
+ * dir-spec.txt only requires a hex string, and we can't know all hex
+ * string lengths for all future digest algorithms, so let's just
+ * accept this. */
+ DirectorySignatureBuilder.createWithIdentity(
+ "ED03BB616EB2F60BEC80151114BB25CEF515B226ED03BB616EB2F60BEC");
+ }
+
+ @Test()
+ public void testDirectorySignatureSigningKeyTooShort()
+ throws DescriptorParseException {
+ /* See above, we accept this hex string even though it's unusually
+ * short. */
+ DirectorySignatureBuilder.createWithSigningKey("845CF1D0B370CA");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignatureSigningKeyTooShortOddNumber()
+ throws DescriptorParseException {
+ /* We don't accept this hex string, because it contains an odd number
+ * of hex characters. */
+ DirectorySignatureBuilder.createWithSigningKey("845");
+ }
+
+ @Test()
+ public void testDirectorySignatureSigningKeyTooLong()
+ throws DescriptorParseException {
+ /* See above, we accept this hex string even though it's unusually
+ * long. */
+ DirectorySignatureBuilder.createWithSigningKey(
+ "845CF1D0B370CA443A8579D18E7987E7E532F639845CF1D0B370CA443A");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNonAsciiByte20() throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ byte[] consensusBytes = cb.buildConsensus();
+ consensusBytes[20] = (byte) 200;
+ new RelayNetworkStatusConsensusImpl(consensusBytes, true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNonAsciiByteMinusOne()
+ throws DescriptorParseException {
+ ConsensusBuilder cb = new ConsensusBuilder();
+ cb.networkStatusVersionLine = "Xnetwork-status-version 3";
+ byte[] consensusBytes = cb.buildConsensus();
+ consensusBytes[0] = (byte) 200;
+ new RelayNetworkStatusConsensusImpl(consensusBytes, true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedHeaderLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedHeaderLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedHeaderLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirSourceLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirSourceLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedStatusEntryLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedStatusEntryLine(
+ unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedStatusEntryLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedStatusEntryLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirectoryFooterLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedFooterLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirectoryFooterLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedFooterLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirectorySignatureLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ConsensusBuilder.createWithUnrecognizedDirectorySignatureLine(
+ unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirectorySignatureLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusConsensus consensus = ConsensusBuilder.
+ createWithUnrecognizedDirectorySignatureLine(unrecognizedLine,
+ false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
new file mode 100644
index 0000000..1c840f5
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
@@ -0,0 +1,1373 @@
+/* Copyright 2011--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import org.torproject.descriptor.DirectorySignature;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.RelayNetworkStatusVote;
+
+/* TODO Add test cases for all lines starting with "opt ". */
+
+/* Test parsing of network status votes. Some of the vote-parsing code is
+ * already tested in the consensus-parsing tests. The tests in this class
+ * focus on the differences between votes and consensuses that are mostly
+ * in the directory header. */
+public class RelayNetworkStatusVoteImplTest {
+
+ /* Helper class to build a vote based on default data and modifications
+ * requested by test methods. */
+ private static class VoteBuilder {
+ private String networkStatusVersionLine = "network-status-version 3";
+ private static RelayNetworkStatusVote
+ createWithNetworkStatusVersionLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.networkStatusVersionLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String voteStatusLine = "vote-status vote";
+ private static RelayNetworkStatusVote
+ createWithVoteStatusLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.voteStatusLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String consensusMethodsLine =
+ "consensus-methods 1 2 3 4 5 6 7 8 9 10 11";
+ private static RelayNetworkStatusVote
+ createWithConsensusMethodsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.consensusMethodsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String publishedLine = "published 2011-11-30 08:50:01";
+ private static RelayNetworkStatusVote
+ createWithPublishedLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.publishedLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String validAfterLine = "valid-after 2011-11-30 09:00:00";
+ private static RelayNetworkStatusVote
+ createWithValidAfterLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.validAfterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
+ private static RelayNetworkStatusVote
+ createWithFreshUntilLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.freshUntilLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String validUntilLine = "valid-until 2011-11-30 12:00:00";
+ private static RelayNetworkStatusVote
+ createWithValidUntilLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.validUntilLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String votingDelayLine = "voting-delay 300 300";
+ private static RelayNetworkStatusVote
+ createWithVotingDelayLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.votingDelayLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String clientVersionsLine = "client-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ private static RelayNetworkStatusVote
+ createWithClientVersionsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.clientVersionsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String serverVersionsLine = "server-versions 0.2.1.31,"
+ + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
+ private static RelayNetworkStatusVote
+ createWithServerVersionsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.serverVersionsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String packageLines = null;
+ protected static RelayNetworkStatusVote
+ createWithPackageLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.packageLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String knownFlagsLine = "known-flags Authority BadExit Exit "
+ + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
+ private static RelayNetworkStatusVote
+ createWithKnownFlagsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.knownFlagsLine = 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 "
+ + "guard-bw-exc-exits=184320 enough-mtbf=1";
+ private static RelayNetworkStatusVote
+ createWithFlagThresholdsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.flagThresholdsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String paramsLine = "params "
+ + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
+ + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
+ + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
+ + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
+ private static RelayNetworkStatusVote
+ createWithParamsLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.paramsLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirSourceLine = "dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80";
+ private static RelayNetworkStatusVote
+ createWithDirSourceLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirSourceLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String contactLine = "contact 4096R/E012B42D Jacob Appelbaum "
+ + "<jacob(a)appelbaum.net>";
+ private static RelayNetworkStatusVote
+ createWithContactLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.contactLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String legacyDirKeyLine = null;
+ private static RelayNetworkStatusVote
+ createWithLegacyDirKeyLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.legacyDirKeyLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCertificateVersionLine =
+ "dir-key-certificate-version 3";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCertificateVersionLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCertificateVersionLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String fingerprintLine = "fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C";
+ private static RelayNetworkStatusVote
+ createWithFingerprintLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.fingerprintLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyPublishedLine = "dir-key-published 2011-04-27 "
+ + "05:34:37";
+ private static RelayNetworkStatusVote
+ createWithDirKeyPublishedLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyPublishedLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyExpiresLine = "dir-key-expires 2012-04-27 "
+ + "05:34:37";
+ private static RelayNetworkStatusVote
+ createWithDirKeyExpiresLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyExpiresLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirIdentityKeyLines = "dir-identity-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIIBigKCAYEAtKpuLgVK25sfScjsxfVU1ljofrDygt9GP7bNJl/rghX42KUT97"
+ + "5W\nrGp/fbhF7p+FcKCzNOhJFINQbRf/5E3lN8mzoamIU43QqQ9RRVf94688Us"
+ + "azVsAN\nNVT0v9J0cr387WePjenRuIE1MmiP0nmw/XdvbPTayqax7VYlcUMXGH"
+ + "l8DnWix1EN\nRwmeig+JBte0JS12oo2HG9zcSfjLJVjY6ZmvRrVycXiRxGc/Jg"
+ + "NlSrV4cxUNykaB\nJ6pO6J499OZfQu7m1vAPTENrVJ4yEfRGRwFIY+d/s8BkKc"
+ + "aiWtXAfTe31uBI6GEH\nmS3HNu1JVSuoaUiQIvVYDLMfBvMcNyAx97UT1l6E0T"
+ + "n6a7pgChrquGwXai1xGzk8\n58aXwdSFoFBSTCkyemopq5H20p/nkPAO0pHL1k"
+ + "TvcaKz9CEj4XcKm+kOmzejYmIa\nkbWNcRpXPiUZ+xmwGtsq30xrzqiONmERkx"
+ + "qlmf7bVQPFvh3Kz6hGcmTBhTbHSe9h\nzDgmdaTNn3EHAgMBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static RelayNetworkStatusVote
+ createWithDirIdentityKeyLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirIdentityKeyLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirSigningKeyLines = "dir-signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAN05qyHFQlTqykMP8yLuD4G2UuYulD4Xs8iSX5uqF+WGsUA1E4zZh4"
+ + "8h\nDFj8+drFiCu3EqhMEmVG4ACtJK2uz6D1XohUsbPWTR6LSnWJ8q6/zfTSLu"
+ + "mBGsN7\nPUXyMNjwRKL6UvrcbYk1d2mRBLO7SAP/sFW5fHhIBVeLIWrzQ19rAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static RelayNetworkStatusVote
+ createWithDirSigningKeyLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirSigningKeyLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCrosscertLines = "dir-key-crosscert\n"
+ + "-----BEGIN ID SIGNATURE-----\n"
+ + "rPBFn6IJ6TvAHj4pSwlg+RTn1fP89JGSVa08wuyJr5dAvZsdakQXvRjamT9oJU"
+ + "aZ\nnY5Rl/tRlGuSQ0BglTPPKoXdKERK0FUr9f0EKrQy7NDUgE2j9losiRuyKz"
+ + "hA3neZ\nK4yF8bhqAwM51u7fzAhIjNeRif9c04rhFJJCseco84w=\n"
+ + "-----END ID SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCrosscertLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCrosscertLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String dirKeyCertificationLines = "dir-key-certification\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "hPSh6FuohNF5ccjiMbkvr8cZJwGFuL11cNtwN9k0X3pUdFZVATIEkqBe7z+rE2"
+ + "PX\nPw+BGyC6wYAieoTVIhLpwKqd7DXLYjuhPZ28+7MQaDL01AqYeRp5PT01Px"
+ + "rFY0Um\nlVf95uqUitgvDT76Ne4ExWk6UvGlYB9OBgBySZz8VWe9znoMqb0uHn"
+ + "/p8IzqTApT\nAxRWXBHClntMeRqtGxaj8DcdJFn8yMxQiZG7MfDg2sq2ySPJyG"
+ + "lN+neoVDVhZiDI\n9LTNmw60gWlUp2erFeam8Mo1ZBC4DPNjQEm6QeHZFZMkhD"
+ + "uO6SwS/FL712A42+Co\nYtMaVot/p5FG2ZSBXbgl2XP5/z8ELnpmXqMbPAoWRo"
+ + "3BPNSJkIQQNog8Q5ZrK+av\nZDw5eGPltGKsXOkvuzIMM8nBeAnDPDgYvzrIFO"
+ + "bEGbvY/P8mzVAZxp3Yz+sRtNel\nC1SWz/Fx+Saex5oI7DJ3xtSD4XqKb/wYwZ"
+ + "FT8IxDYq1t2tFXdHxd4QPRVcvc0zYC\n"
+ + "-----END SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirKeyCertificationLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.dirKeyCertificationLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private List<String> statusEntries = null;
+ private static RelayNetworkStatusVote createWithStatusEntries(
+ List<String> statusEntries) throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.statusEntries = statusEntries;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String directoryFooterLine = "directory-footer";
+ private static RelayNetworkStatusVote
+ createWithDirectoryFooterLine(String line)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.directoryFooterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String directorySignatureLines = "directory-signature "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C "
+ + "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
+ + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
+ + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----";
+ private static RelayNetworkStatusVote
+ createWithDirectorySignatureLines(String lines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.directorySignatureLines = lines;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+ private String unrecognizedHeaderLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedHeaderLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedHeaderLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirSourceLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedDirSourceLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedDirSourceLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedStatusEntryLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedStatusEntryLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedStatusEntryLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedFooterLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedFooterLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedFooterLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String unrecognizedDirectorySignatureLine = null;
+ protected static RelayNetworkStatusVote
+ createWithUnrecognizedDirectorySignatureLine(String line,
+ boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.unrecognizedDirectorySignatureLine = line;
+ return new RelayNetworkStatusVoteImpl(vb.buildVote(),
+ failUnrecognizedDescriptorLines);
+ }
+
+ private VoteBuilder() {
+ if (this.statusEntries != null) {
+ return;
+ }
+ this.statusEntries = new ArrayList<>();
+ this.statusEntries.add("r right2privassy3 "
+ + "ADQ6gCT3DiFHKPDFr3rODBUI8HM lJY5Vf7kXec+VdkGW2flEsfkFC8 "
+ + "2011-11-12 00:03:40 50.63.8.215 9023 0\n"
+ + "s Exit Fast Guard Running Stable Valid\n"
+ + "opt v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)\n"
+ + "w Bandwidth=297 Measured=73\n"
+ + "p accept 80,1194,1220,1293,1500,1533,1677,1723,1863,"
+ + "2082-2083,2086-2087,2095-2096,2102-2104,3128,3389,3690,4321,"
+ + "4643,5050,5190,5222-5223,5228,5900,6660-6669,6679,6697,8000,"
+ + "8008,8074,8080,8087-8088,8443,8888,9418,9999-10000,19294,"
+ + "19638\n"
+ + "m 8,9,10,11 "
+ + "sha256=9ciEx9t0McXk9A06I7qwN7pxuNOdpCP64RV/6cx2Zkc");
+ }
+ private byte[] buildVote() {
+ StringBuilder sb = new StringBuilder();
+ this.appendHeader(sb);
+ this.appendDirSource(sb);
+ this.appendStatusEntries(sb);
+ this.appendFooter(sb);
+ this.appendDirectorySignature(sb);
+ return sb.toString().getBytes();
+ }
+ private void appendHeader(StringBuilder sb) {
+ if (this.networkStatusVersionLine != null) {
+ sb.append(this.networkStatusVersionLine).append("\n");
+ }
+ if (this.voteStatusLine != null) {
+ sb.append(this.voteStatusLine).append("\n");
+ }
+ if (this.consensusMethodsLine != null) {
+ sb.append(this.consensusMethodsLine).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.validAfterLine != null) {
+ sb.append(this.validAfterLine).append("\n");
+ }
+ if (this.freshUntilLine != null) {
+ sb.append(this.freshUntilLine).append("\n");
+ }
+ if (this.validUntilLine != null) {
+ sb.append(this.validUntilLine).append("\n");
+ }
+ if (this.votingDelayLine != null) {
+ sb.append(this.votingDelayLine).append("\n");
+ }
+ if (this.clientVersionsLine != null) {
+ sb.append(this.clientVersionsLine).append("\n");
+ }
+ if (this.serverVersionsLine != null) {
+ sb.append(this.serverVersionsLine).append("\n");
+ }
+ if (this.packageLines != null) {
+ sb.append(this.packageLines).append("\n");
+ }
+ if (this.knownFlagsLine != null) {
+ sb.append(this.knownFlagsLine).append("\n");
+ }
+ if (this.flagThresholdsLine != null) {
+ sb.append(this.flagThresholdsLine).append("\n");
+ }
+ if (this.paramsLine != null) {
+ sb.append(this.paramsLine).append("\n");
+ }
+ if (this.unrecognizedHeaderLine != null) {
+ sb.append(this.unrecognizedHeaderLine).append("\n");
+ }
+ }
+ private void appendDirSource(StringBuilder sb) {
+ if (this.dirSourceLine != null) {
+ sb.append(this.dirSourceLine).append("\n");
+ }
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.legacyDirKeyLine != null) {
+ sb.append(this.legacyDirKeyLine).append("\n");
+ }
+ if (this.dirKeyCertificateVersionLine != null) {
+ sb.append(this.dirKeyCertificateVersionLine).append("\n");
+ }
+ if (this.fingerprintLine != null) {
+ sb.append(this.fingerprintLine).append("\n");
+ }
+ if (this.dirKeyPublishedLine != null) {
+ sb.append(this.dirKeyPublishedLine).append("\n");
+ }
+ if (this.dirKeyExpiresLine != null) {
+ sb.append(this.dirKeyExpiresLine).append("\n");
+ }
+ if (this.dirIdentityKeyLines != null) {
+ sb.append(this.dirIdentityKeyLines).append("\n");
+ }
+ if (this.dirSigningKeyLines != null) {
+ sb.append(this.dirSigningKeyLines).append("\n");
+ }
+ if (this.dirKeyCrosscertLines != null) {
+ sb.append(this.dirKeyCrosscertLines).append("\n");
+ }
+ if (this.dirKeyCertificationLines != null) {
+ sb.append(this.dirKeyCertificationLines).append("\n");
+ }
+ if (this.unrecognizedDirSourceLine != null) {
+ sb.append(this.unrecognizedDirSourceLine).append("\n");
+ }
+ }
+ private void appendStatusEntries(StringBuilder sb) {
+ for (String statusEntry : this.statusEntries) {
+ sb.append(statusEntry).append("\n");
+ }
+ if (this.unrecognizedStatusEntryLine != null) {
+ sb.append(this.unrecognizedStatusEntryLine).append("\n");
+ }
+ }
+ private void appendFooter(StringBuilder sb) {
+ if (this.directoryFooterLine != null) {
+ sb.append(this.directoryFooterLine).append("\n");
+ }
+ if (this.unrecognizedFooterLine != null) {
+ sb.append(this.unrecognizedFooterLine).append("\n");
+ }
+ }
+ private void appendDirectorySignature(StringBuilder sb) {
+ if (this.directorySignatureLines != null) {
+ sb.append(directorySignatureLines).append("\n");
+ }
+ if (this.unrecognizedDirectorySignatureLine != null) {
+ sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
+ }
+ }
+ }
+
+ @Test()
+ public void testSampleVote() throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ RelayNetworkStatusVote vote =
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ assertEquals(3, vote.getNetworkStatusVersion());
+ List<Integer> consensusMethods = Arrays.asList(
+ new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ assertEquals(vote.getConsensusMethods(), consensusMethods);
+ assertEquals(1322643001000L, vote.getPublishedMillis());
+ assertEquals(1322643600000L, vote.getValidAfterMillis());
+ assertEquals(1322647200000L, vote.getFreshUntilMillis());
+ assertEquals(1322654400000L, vote.getValidUntilMillis());
+ assertEquals(300L, vote.getVoteSeconds());
+ assertEquals(300L, vote.getDistSeconds());
+ assertTrue(vote.getKnownFlags().contains("Running"));
+ assertEquals(30000, (int) vote.getConsensusParams().get(
+ "CircuitPriorityHalflifeMsec"));
+ assertEquals("Tor 0.2.1.29 (r8e9b25e6c7a2e70c)",
+ vote.getStatusEntry("00343A8024F70E214728F0C5AF7ACE0C1508F073").
+ getVersion());
+ assertEquals(3, vote.getDirKeyCertificateVersion());
+ assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
+ vote.getIdentity());
+ assertEquals(1303882477000L, /* 2011-04-27 05:34:37 */
+ vote.getDirKeyPublishedMillis());
+ assertEquals(1335504877000L, /* 2012-04-27 05:34:37 */
+ vote.getDirKeyExpiresMillis());
+ assertEquals("-----BEGIN RSA PUBLIC KEY-----",
+ vote.getDirIdentityKey().split("\n")[0]);
+ assertEquals("-----BEGIN RSA PUBLIC KEY-----",
+ vote.getDirSigningKey().split("\n")[0]);
+ assertEquals("-----BEGIN ID SIGNATURE-----",
+ vote.getDirKeyCrosscert().split("\n")[0]);
+ assertEquals("-----BEGIN SIGNATURE-----",
+ vote.getDirKeyCertification().split("\n")[0]);
+ assertEquals(1, vote.getSignatures().size());
+ DirectorySignature signature = vote.getSignatures().get(0);
+ assertEquals("sha1", signature.getAlgorithm());
+ assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
+ signature.getIdentity());
+ assertEquals("EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19",
+ signature.getSigningKeyDigest());
+ assertEquals("-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
+ + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
+ + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----\n", signature.getSignature());
+ assertTrue(vote.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNewLineSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 3\n ");
+ }
+
+ @Test()
+ public void testNetworkStatusVersionPrefixLineAtChar()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "@vote\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLine()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "directory-footer\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionPrefixLinePoundChar()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "#vote\nnetwork-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersion42()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version 42");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionFourtyTwo()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ "network-status-version FourtyTwo");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNetworkStatusVersionSpaceBefore()
+ throws DescriptorParseException {
+ VoteBuilder.createWithNetworkStatusVersionLine(
+ " network-status-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusSpaceBefore() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(" vote-status vote");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status ");
+ }
+
+ @Test()
+ public void testVoteStatusVoteOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status vote ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusConsensus() throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine("vote-status consensus");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVoteStatusTheMagicVoteStatus()
+ throws DescriptorParseException {
+ VoteBuilder.createWithVoteStatusLine(
+ "vote-status TheMagicVoteStatus");
+ }
+
+ @Test()
+ public void testConsensusMethodNoLine()
+ throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithConsensusMethodsLine(null);
+ assertNull(vote.getConsensusMethods());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodEleven()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine(
+ "consensus-methods eleven");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodMinusOne()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodNinePeriod()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine("consensus-methods "
+ + "999999999999999999999999999999999999999999999999999999999999");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testConsensusMethodTwoLines()
+ throws DescriptorParseException {
+ VoteBuilder.createWithConsensusMethodsLine(
+ "consensus-method 1\nconsensus-method 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithPublishedLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterLongAgo() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine("valid-after long ago");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidAfterFeb30() throws DescriptorParseException {
+ VoteBuilder.createWithValidAfterLine(
+ "valid-after 2011-02-30 09:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithFreshUntilLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFreshUntilAroundTen() throws DescriptorParseException {
+ VoteBuilder.createWithFreshUntilLine(
+ "fresh-until 2011-11-30 around ten");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testValidUntilTomorrowMorning()
+ throws DescriptorParseException {
+ VoteBuilder.createWithValidUntilLine(
+ "valid-until tomorrow morning");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayTriple() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine(
+ "voting-delay 300 300 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelaySingle() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay 300");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testVotingDelayOneTwo() throws DescriptorParseException {
+ VoteBuilder.createWithVotingDelayLine("voting-delay one two");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsComma() throws DescriptorParseException {
+ VoteBuilder.createWithClientVersionsLine("client-versions ,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testClientVersionsCommaVersion()
+ throws DescriptorParseException {
+ VoteBuilder.createWithClientVersionsLine(
+ "client-versions ,0.2.2.34");
+ }
+
+ @Test()
+ public void testPackageNone() throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(null);
+ assertNull(vote.getPackageLines());
+ }
+
+ @Test()
+ public void testPackageOne() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http digest=digest";
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(packageLine);
+ assertEquals(packageLine.substring("package ".length()),
+ vote.getPackageLines().get(0));
+ }
+
+ @Test()
+ public void testPackageTwo() throws DescriptorParseException {
+ List<String> packageLines = Arrays.asList(
+ "package shouldbesecond 0 http digest=digest",
+ "package outoforder 0 http digest=digest");
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithPackageLines(packageLines.get(0)
+ + "\n" + packageLines.get(1));
+ for (int i = 0; i < packageLines.size(); i++) {
+ assertEquals(packageLines.get(i).substring("package ".length()),
+ vote.getPackageLines().get(i));
+ }
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPackageIncomplete() throws DescriptorParseException {
+ String packageLine = "package shouldbesecond 0 http";
+ ConsensusBuilder.createWithPackageLines(packageLine);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoLine() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsNoSpace() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine("known-flags");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testKnownFlagsOneSpace() throws DescriptorParseException {
+ VoteBuilder.createWithKnownFlagsLine("known-flags ");
+ }
+
+ @Test()
+ public void testFlagThresholdsLine() throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ RelayNetworkStatusVote vote =
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ assertEquals(693369L, vote.getStableUptime());
+ assertEquals(153249L, vote.getStableMtbf());
+ assertEquals(40960L, vote.getFastBandwidth());
+ assertEquals(94.669, vote.getGuardWfu(), 0.001);
+ assertEquals(691200L, vote.getGuardTk());
+ assertEquals(174080L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(184320L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(1, vote.getEnoughMtbfInfo());
+ }
+
+ @Test()
+ public void testFlagThresholdsNoLine() throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithFlagThresholdsLine(null);
+ assertEquals(-1L, vote.getStableUptime());
+ assertEquals(-1L, vote.getStableMtbf());
+ assertEquals(-1L, vote.getFastBandwidth());
+ assertEquals(-1.0, vote.getGuardWfu(), 0.001);
+ assertEquals(-1L, vote.getGuardTk());
+ assertEquals(-1L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(-1L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(-1, vote.getEnoughMtbfInfo());
+ }
+
+ @Test()
+ public void testFlagThresholdsAllZeroes()
+ throws DescriptorParseException {
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds "
+ + "stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.0% "
+ + "guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 "
+ + "enough-mtbf=0");
+ assertEquals(0L, vote.getStableUptime());
+ assertEquals(0L, vote.getStableMtbf());
+ assertEquals(0L, vote.getFastBandwidth());
+ assertEquals(0.0, vote.getGuardWfu(), 0.001);
+ assertEquals(0L, vote.getGuardTk());
+ assertEquals(0L, vote.getGuardBandwidthIncludingExits());
+ assertEquals(0L, vote.getGuardBandwidthExcludingExits());
+ assertEquals(0, vote.getEnoughMtbfInfo());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdsNoSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdsOneSpace()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFlagThresholdsLine("flag-thresholds ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFlagThresholdDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder vb = new VoteBuilder();
+ vb.flagThresholdsLine = vb.flagThresholdsLine + "\n"
+ + vb.flagThresholdsLine;
+ new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source "
+ + "urrassssssssssssssssssssssssssssssssssssssssssssssss "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameIllegalCharacters()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urra$ "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testFingerprintLowerCase() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987e1d626e3eba5e5e75a458de0626d088c 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintIllegalCharacters()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "ABCDEFGHIJKLM6E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + " 208.83.223.34 208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testHostname256()
+ throws DescriptorParseException {
+ /* This test doesn't fail, because we're not parsing the hostname. */
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 256.256.256.256 "
+ + "208.83.223.34 443 80");
+ assertEquals("256.256.256.256", vote.getHostname());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHostnameMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
+ + "80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress256()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "256.256.256.256 443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
+ + "80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMinus443()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 -443 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortFourFourThree()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 four-four-three 80");
+ }
+
+ @Test()
+ public void testDirPort0() throws DescriptorParseException {
+ /* This test doesn't fail, because we're accepting DirPort 0, even
+ * though it doesn't make sense from Tor's view. */
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 0 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortMissing() throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 ");
+ }
+
+ @Test()
+ public void testDirPortOrPortIdentical()
+ throws DescriptorParseException {
+ /* This test doesn't fail, even though identical OR and Dir port don't
+ * make much sense from Tor's view. */
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 80 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSourceLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSourceLine("dir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80\ndir-source urras "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
+ + "208.83.223.34 443 80");
+ }
+
+ @Test()
+ public void testContactLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithContactLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testContactLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithContactLine("contact 4096R/E012B42D Jacob "
+ + "Appelbaum <jacob(a)appelbaum.net>\ncontact 4096R/E012B42D Jacob "
+ + "Appelbaum <jacob(a)appelbaum.net>");
+ }
+
+ @Test()
+ public void testLegacyDirKeyLine() throws DescriptorParseException {
+ RelayNetworkStatusVote vote = VoteBuilder.createWithLegacyDirKeyLine(
+ "legacy-dir-key 81349FC1F2DBA2C2C11B45CB9706637D480AB913");
+ assertEquals("81349FC1F2DBA2C2C11B45CB9706637D480AB913",
+ vote.getLegacyDirKey());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testLegacyDirKeyLineNoId() throws DescriptorParseException {
+ VoteBuilder.createWithLegacyDirKeyLine("legacy-dir-key ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificateVersionLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificateVersionLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificateVersionLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificateVersionLine(
+ "dir-key-certificate-version 3\ndir-key-certificate-version 3");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C\nfingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineTooLong()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintLineTooShort()
+ throws DescriptorParseException {
+ VoteBuilder.createWithFingerprintLine("fingerprint "
+ + "80550987E1D626E3EBA5E5E75A458DE0626D");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublished3011()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "3011-04-27 05:34:37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublishedRecentlyAtNoon()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "recently 12:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyPublishedRecentlyNoTime()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
+ + "recently");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresSoonAtNoon()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires "
+ + "soon 12:00:00");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyExpiresLineDuplicate()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires 2012-04-27 "
+ + "05:34:37\ndir-key-expires 2012-04-27 05:34:37");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirIdentityKeyLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirIdentityKeyLines(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirSigningKeyLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirSigningKeyLines(null);
+ }
+
+ @Test()
+ public void testDirKeyCrosscertLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCrosscertLines(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirKeyCertificationLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirKeyCertificationLines(null);
+ }
+
+ @Test()
+ public void testDirectoryFooterLineMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirectoryFooterLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirectorySignaturesLinesMissing()
+ throws DescriptorParseException {
+ VoteBuilder.createWithDirectorySignatureLines(null);
+ }
+
+ @Test()
+ public void testDirectorySignaturesLinesTwoAlgorithms()
+ throws DescriptorParseException {
+ String identitySha256 = "32519E5CB7254AB5A94CC9925EC7676E53D5D52EEAB7"
+ + "914BD3ED751E537CAFCC";
+ String signingKeyDigestSha256 = "5A59D99C17831B9254422B6C5AA10CC59381"
+ + "6CAA5241E22ECAE8BBB4E8E9D1FC";
+ String signatureSha256 = "-----BEGIN SIGNATURE-----\n"
+ + "x57Alc424/zHS73SHokghGtNBVrBjtUz+gSL5w9AHGKUQcMyfw4Z9aDlKpTbFc"
+ + "5W\nnyIvFmM9C2OAH0S1+a647HHIxhE0zKf4+yKSwzqSyL6sbKQygVlJsRHNRr"
+ + "cFg8lp\nqBxEwvxQoA4xEDqnerR92pbK9l42nNLiKOcoReUqbbQ=\n"
+ + "-----END SIGNATURE-----";
+ String identitySha1 = "80550987E1D626E3EBA5E5E75A458DE0626D088C";
+ String signingKeyDigestSha1 =
+ "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19";
+ String signatureSha1 = "-----BEGIN SIGNATURE-----\n"
+ + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxnF3"
+ + "Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40OikfOI"
+ + "wEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
+ + "-----END SIGNATURE-----";
+ String signaturesLines = String.format(
+ "directory-signature sha256 %s %s\n%s\n"
+ + "directory-signature %s %s\n%s", identitySha256,
+ signingKeyDigestSha256, signatureSha256, identitySha1,
+ signingKeyDigestSha1, signatureSha1);
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
+ assertEquals(2, vote.getSignatures().size());
+ DirectorySignature firstSignature = vote.getSignatures().get(0);
+ assertEquals("sha256", firstSignature.getAlgorithm());
+ assertEquals(identitySha256, firstSignature.getIdentity());
+ assertEquals(signingKeyDigestSha256,
+ firstSignature.getSigningKeyDigest());
+ assertEquals(signatureSha256 + "\n", firstSignature.getSignature());
+ DirectorySignature secondSignature = vote.getSignatures().get(1);
+ assertEquals("sha1", secondSignature.getAlgorithm());
+ assertEquals(identitySha1, secondSignature.getIdentity());
+ assertEquals(signingKeyDigestSha1,
+ secondSignature.getSigningKeyDigest());
+ assertEquals(signatureSha1 + "\n", secondSignature.getSignature());
+ assertEquals(signingKeyDigestSha1, vote.getSigningKeyDigest());
+ }
+
+ @Test()
+ public void testDirectorySignaturesLinesTwoAlgorithmsSameDigests()
+ throws DescriptorParseException {
+ String signaturesLines = "directory-signature 00 00\n"
+ + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----\n"
+ + "directory-signature sha256 00 00\n"
+ + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----";
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
+ assertEquals(2, vote.getSignatures().size());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedHeaderLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedHeaderLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedHeaderLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedDirSourceLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
+ true);
+ }
+
+ @Test()
+ public void testUnrecognizedDirSourceLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedFooterLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ VoteBuilder.createWithUnrecognizedFooterLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedFooterLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ RelayNetworkStatusVote vote = VoteBuilder.
+ createWithUnrecognizedFooterLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testIdEd25519MasterKey()
+ throws DescriptorParseException {
+ String masterKey25519 = "8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8";
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r PDrelay1 AAFJ5u9xAqrKlpDW6N0pMhJLlKs "
+ + "bgJiI/la3e9u0K7cQ5pMSXhigHI 2015-12-01 04:54:30 95.215.44.189 "
+ + "8080 0\n"
+ + "id ed25519 " + masterKey25519);
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ String fingerprint = vote.getStatusEntries().firstKey();
+ assertEquals(masterKey25519,
+ vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
+ }
+
+ @Test()
+ public void testIdEd25519None()
+ throws DescriptorParseException {
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
+ + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
+ + "443 9030\n"
+ + "id ed25519 none");
+ RelayNetworkStatusVote vote =
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ String fingerprint = vote.getStatusEntries().firstKey();
+ assertEquals("none",
+ vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIdRsa1024None()
+ throws DescriptorParseException {
+ List<String> statusEntries = new ArrayList<>();
+ statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
+ + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
+ + "443 9030\n"
+ + "id rsa1024 none");
+ VoteBuilder.createWithStatusEntries(statusEntries);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
new file mode 100644
index 0000000..cd3f1a5
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -0,0 +1,1605 @@
+/* Copyright 2012--2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.SortedMap;
+
+import org.junit.Test;
+import org.torproject.descriptor.BandwidthHistory;
+import org.torproject.descriptor.ServerDescriptor;
+
+/* Test parsing of relay server descriptors. */
+public class ServerDescriptorImplTest {
+
+ /* Helper class to build a descriptor based on default data and
+ * modifications requested by test methods. */
+ private static class DescriptorBuilder {
+ private String routerLine = "router saberrider2008 94.134.192.243 "
+ + "9001 0 0";
+ private static ServerDescriptor createWithRouterLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String bandwidthLine = "bandwidth 51200 51200 53470";
+ private static ServerDescriptor createWithBandwidthLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.bandwidthLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String platformLine = "platform Tor 0.2.2.35 "
+ + "(git-b04388f9e7546a9f) on Linux i686";
+ private static ServerDescriptor createWithPlatformLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.platformLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String publishedLine = "published 2012-01-01 04:03:19";
+ private static ServerDescriptor createWithPublishedLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.publishedLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String fingerprintLine = "opt fingerprint D873 3048 FC8E "
+ + "C910 2466 AD8F 3098 622B F1BF 71FD";
+ private static ServerDescriptor createWithFingerprintLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.fingerprintLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hibernatingLine = null;
+ private static ServerDescriptor createWithHibernatingLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hibernatingLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String uptimeLine = "uptime 48";
+ private static ServerDescriptor createWithUptimeLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.uptimeLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String onionKeyLines = "onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
+ + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
+ + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static ServerDescriptor createWithOnionKeyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.onionKeyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String signingKeyLines = "signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
+ + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
+ + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----";
+ private static ServerDescriptor createWithSigningKeyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.signingKeyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String onionKeyCrosscertLines = null;
+ private static ServerDescriptor createWithOnionKeyCrosscertLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.onionKeyCrosscertLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyCrosscertLines = null;
+ private static ServerDescriptor createWithNtorOnionKeyCrosscertLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ntorOnionKeyCrosscertLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String exitPolicyLines = "reject *:*";
+ private static ServerDescriptor createWithExitPolicyLines(
+ String lines) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.exitPolicyLines = lines;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String contactLine = "contact Random Person <nobody AT "
+ + "example dot com>";
+ private static ServerDescriptor createWithContactLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.contactLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String familyLine = null;
+ private static ServerDescriptor createWithFamilyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.familyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String readHistoryLine = null;
+ private static ServerDescriptor createWithReadHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.readHistoryLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String writeHistoryLine = null;
+ private static ServerDescriptor createWithWriteHistoryLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.writeHistoryLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String eventdnsLine = null;
+ private static ServerDescriptor createWithEventdnsLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.eventdnsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String cachesExtraInfoLine = null;
+ private static ServerDescriptor createWithCachesExtraInfoLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.cachesExtraInfoLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String extraInfoDigestLine = "opt extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74";
+ private static ServerDescriptor createWithExtraInfoDigestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.extraInfoDigestLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String hiddenServiceDirLine = "opt hidden-service-dir";
+ private static ServerDescriptor createWithHiddenServiceDirLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.hiddenServiceDirLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String protocolsLine = "opt protocols Link 1 2 Circuit 1";
+ private static ServerDescriptor createWithProtocolsLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.protocolsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String allowSingleHopExitsLine = null;
+ private static ServerDescriptor
+ createWithAllowSingleHopExitsLine(String line)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.allowSingleHopExitsLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ipv6PolicyLine = null;
+ private static ServerDescriptor createWithIpv6PolicyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ipv6PolicyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String ntorOnionKeyLine = null;
+ private static ServerDescriptor createWithNtorOnionKeyLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.ntorOnionKeyLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String tunnelledDirServerLine = null;
+ private static ServerDescriptor createWithTunnelledDirServerLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.tunnelledDirServerLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String routerSignatureLines = "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----";
+ private static ServerDescriptor createWithRouterSignatureLines(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.routerSignatureLines = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private String unrecognizedLine = null;
+ private static ServerDescriptor createWithUnrecognizedLine(
+ String line, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.unrecognizedLine = line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private byte[] nonAsciiLineBytes = null;
+ private static ServerDescriptor createWithNonAsciiLineBytes(
+ byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
+ throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.nonAsciiLineBytes = lineBytes;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(),
+ failUnrecognizedDescriptorLines);
+ }
+ private String identityEd25519Lines = null,
+ masterKeyEd25519Line = null, routerSigEd25519Line = null;
+ private static ServerDescriptor createWithEd25519Lines(
+ String identityEd25519Lines, String masterKeyEd25519Line,
+ String routerSigEd25519Line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.identityEd25519Lines = identityEd25519Lines;
+ db.masterKeyEd25519Line = masterKeyEd25519Line;
+ db.routerSigEd25519Line = routerSigEd25519Line;
+ return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ }
+ private byte[] buildDescriptor() {
+ StringBuilder sb = new StringBuilder();
+ if (this.routerLine != null) {
+ sb.append(this.routerLine).append("\n");
+ }
+ if (this.identityEd25519Lines != null) {
+ sb.append(this.identityEd25519Lines).append("\n");
+ }
+ if (this.masterKeyEd25519Line != null) {
+ sb.append(this.masterKeyEd25519Line).append("\n");
+ }
+ if (this.bandwidthLine != null) {
+ sb.append(this.bandwidthLine).append("\n");
+ }
+ if (this.platformLine != null) {
+ sb.append(this.platformLine).append("\n");
+ }
+ if (this.publishedLine != null) {
+ sb.append(this.publishedLine).append("\n");
+ }
+ if (this.fingerprintLine != null) {
+ sb.append(this.fingerprintLine).append("\n");
+ }
+ if (this.hibernatingLine != null) {
+ sb.append(this.hibernatingLine).append("\n");
+ }
+ if (this.uptimeLine != null) {
+ sb.append(this.uptimeLine).append("\n");
+ }
+ if (this.onionKeyLines != null) {
+ sb.append(this.onionKeyLines).append("\n");
+ }
+ if (this.signingKeyLines != null) {
+ sb.append(this.signingKeyLines).append("\n");
+ }
+ if (this.onionKeyCrosscertLines != null) {
+ sb.append(this.onionKeyCrosscertLines).append("\n");
+ }
+ if (this.ntorOnionKeyCrosscertLines != null) {
+ sb.append(this.ntorOnionKeyCrosscertLines).append("\n");
+ }
+ if (this.exitPolicyLines != null) {
+ sb.append(this.exitPolicyLines).append("\n");
+ }
+ if (this.contactLine != null) {
+ sb.append(this.contactLine).append("\n");
+ }
+ if (this.familyLine != null) {
+ sb.append(this.familyLine).append("\n");
+ }
+ if (this.readHistoryLine != null) {
+ sb.append(this.readHistoryLine).append("\n");
+ }
+ if (this.writeHistoryLine != null) {
+ sb.append(this.writeHistoryLine).append("\n");
+ }
+ if (this.eventdnsLine != null) {
+ sb.append(this.eventdnsLine).append("\n");
+ }
+ if (this.cachesExtraInfoLine != null) {
+ sb.append(this.cachesExtraInfoLine).append("\n");
+ }
+ if (this.extraInfoDigestLine != null) {
+ sb.append(this.extraInfoDigestLine).append("\n");
+ }
+ if (this.hiddenServiceDirLine != null) {
+ sb.append(this.hiddenServiceDirLine).append("\n");
+ }
+ if (this.protocolsLine != null) {
+ sb.append(this.protocolsLine).append("\n");
+ }
+ if (this.allowSingleHopExitsLine != null) {
+ sb.append(this.allowSingleHopExitsLine).append("\n");
+ }
+ if (this.ipv6PolicyLine != null) {
+ sb.append(this.ipv6PolicyLine).append("\n");
+ }
+ if (this.ntorOnionKeyLine != null) {
+ sb.append(this.ntorOnionKeyLine).append("\n");
+ }
+ if (this.tunnelledDirServerLine != null) {
+ sb.append(this.tunnelledDirServerLine).append("\n");
+ }
+ if (this.unrecognizedLine != null) {
+ sb.append(this.unrecognizedLine).append("\n");
+ }
+ if (this.nonAsciiLineBytes != null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(sb.toString().getBytes());
+ baos.write(this.nonAsciiLineBytes);
+ baos.write("\n".getBytes());
+ if (this.routerSignatureLines != null) {
+ baos.write(this.routerSignatureLines.getBytes());
+ }
+ return baos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ if (this.routerSigEd25519Line != null) {
+ sb.append(this.routerSigEd25519Line).append("\n");
+ }
+ if (this.routerSignatureLines != null) {
+ sb.append(this.routerSignatureLines).append("\n");
+ }
+ return sb.toString().getBytes();
+ }
+ }
+
+ @Test()
+ public void testSampleDescriptor() throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ ServerDescriptor descriptor =
+ new RelayServerDescriptorImpl(db.buildDescriptor(), true);
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ assertEquals(9001, (int) descriptor.getOrPort());
+ assertEquals(0, (int) descriptor.getSocksPort());
+ 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(1325390599000L, descriptor.getPublishedMillis());
+ assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
+ descriptor.getFingerprint());
+ assertEquals(48, descriptor.getUptime().longValue());
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(53470, (int) descriptor.getBandwidthObserved());
+ assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
+ descriptor.getExtraInfoDigest());
+ assertEquals(Arrays.asList(new Integer[] {2}),
+ descriptor.getHiddenServiceDirVersions());
+ assertEquals("Random Person <nobody AT example dot com>",
+ descriptor.getContact());
+ assertEquals(Arrays.asList(new String[] {"reject *:*"}),
+ descriptor.getExitPolicyLines());
+ assertFalse(descriptor.isHibernating());
+ assertNull(descriptor.getFamilyEntries());
+ assertNull(descriptor.getReadHistory());
+ assertNull(descriptor.getWriteHistory());
+ assertFalse(descriptor.getUsesEnhancedDnsLogic());
+ assertFalse(descriptor.getCachesExtraInfo());
+ assertFalse(descriptor.getAllowSingleHopExits());
+ assertTrue(descriptor.getUnrecognizedLines().isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterLineMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine(null);
+ }
+
+ @Test()
+ public void testRouterOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithRouterLine("opt router saberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ assertEquals(9001, (int) descriptor.getOrPort());
+ assertEquals(0, (int) descriptor.getSocksPort());
+ assertEquals(0, (int) descriptor.getDirPort());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterLinePrecedingHibernatingLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("hibernating 1\nrouter "
+ + "saberrider2008 94.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router 94.134.192.243 9001 "
+ + "0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameInvalidChar() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router $aberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNicknameTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router "
+ + "saberrider2008ReallyLongNickname 94.134.192.243 9001 0 0");
+ }
+
+ @Test()
+ public void testNicknameTwoSpaces() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 9001 0 0");
+ assertEquals("saberrider2008", descriptor.getNickname());
+ assertEquals("94.134.192.243", descriptor.getAddress());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress24() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192/24 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddress294() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "294.134.192.243 9001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAddressMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 9001 "
+ + "0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPort99001() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 99001 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortOne() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 one 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOrPortNewline() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 0\n 0 0");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testDirPortMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterLine("router saberrider2008 "
+ + "94.134.192.243 9001 0 ");
+ }
+
+ @Test()
+ public void testPlatformMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine(null);
+ assertNull(descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("opt platform Tor 0.2.2.35 "
+ + "(git-b04388f9e7546a9f) on Linux i686");
+ assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
+ descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformNoSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("platform");
+ assertEquals("", descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testPlatformSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPlatformLine("platform ");
+ assertEquals("", descriptor.getPlatform());
+ }
+
+ @Test()
+ public void testProtocolsNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithProtocolsLine("protocols Link 1 2 Circuit 1");
+ assertEquals(Arrays.asList(new Integer[] {1, 2}),
+ descriptor.getLinkProtocolVersions());
+ assertEquals(Arrays.asList(new Integer[] {1}),
+ descriptor.getCircuitProtocolVersions());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testProtocolsAB() throws DescriptorParseException {
+ DescriptorBuilder.createWithProtocolsLine("opt protocols Link A B "
+ + "Circuit 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testProtocolsNoCircuitVersions()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithProtocolsLine("opt protocols Link 1 2");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine(null);
+ }
+
+ @Test()
+ public void testPublishedOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-01-01 04:03:19");
+ assertEquals(1325390599000L, descriptor.getPublishedMillis());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished2039() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2039-01-01 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublished1912() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 1912-01-01 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedFeb31() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2012-02-31 "
+ + "04:03:19");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testPublishedNoTime() throws DescriptorParseException {
+ DescriptorBuilder.createWithPublishedLine("published 2012-01-01");
+ }
+
+ @Test()
+ public void testPublishedMillis() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithPublishedLine("opt published 2012-01-01 04:03:19.123");
+ assertEquals(1325390599000L, descriptor.getPublishedMillis());
+ }
+
+ @Test()
+ public void testFingerprintNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFingerprintLine("fingerprint D873 3048 FC8E C910 2466 "
+ + "AD8F 3098 622B F1BF 71FD");
+ assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
+ descriptor.getFingerprint());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintG() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint G873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooShort() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintTooLong() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
+ + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD D873");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFingerprintNoSpaces() throws DescriptorParseException {
+ DescriptorBuilder.createWithFingerprintLine("opt fingerprint "
+ + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ }
+
+ @Test()
+ public void testUptimeMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUptimeLine(null);
+ assertNull(descriptor.getUptime());
+ }
+
+ @Test()
+ public void testUptimeOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUptimeLine("opt uptime 48");
+ assertEquals(48, descriptor.getUptime().longValue());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeFourtyEight() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime fourty-eight");
+ }
+
+ @Test()
+ public void testUptimeMinusOne() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime -1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeNoSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUptimeFourEight() throws DescriptorParseException {
+ DescriptorBuilder.createWithUptimeLine("uptime 4 8");
+ }
+
+ @Test()
+ public void testBandwidthOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithBandwidthLine("opt bandwidth 51200 51200 53470");
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(53470, (int) descriptor.getBandwidthObserved());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine(null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthOneValue() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth 51200");
+ }
+
+ @Test()
+ public void testBandwidthTwoValues() throws DescriptorParseException {
+ /* This is allowed, because Tor versions 0.0.8 and older only wrote
+ * bandwidth lines with rate and burst values, but no observed
+ * value. */
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithBandwidthLine("bandwidth 51200 51200");
+ assertEquals(51200, (int) descriptor.getBandwidthRate());
+ assertEquals(51200, (int) descriptor.getBandwidthBurst());
+ assertEquals(-1, (int) descriptor.getBandwidthObserved());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthFourValues() throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth 51200 51200 "
+ + "53470 53470");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testBandwidthMinusOneTwoThree()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithBandwidthLine("bandwidth -1 -2 -3");
+ }
+
+ @Test()
+ public void testExtraInfoDigestNoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine("extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74");
+ assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
+ descriptor.getExtraInfoDigest());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestNoSpace()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestTooShort()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest 1469D1550738A25B1E7B47CDDBCD7B2899F5");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExtraInfoDigestTooLong()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExtraInfoDigestLine("opt "
+ + "extra-info-digest "
+ + "1469D1550738A25B1E7B47CDDBCD7B2899F51B741469");
+ }
+
+ @Test()
+ public void testExtraInfoDigestMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine(null);
+ assertNull(descriptor.getExtraInfoDigest());
+ }
+
+ @Test()
+ public void testExtraInfoDigestAdditionalDigest()
+ throws DescriptorParseException {
+ String extraInfoDigest = "0879DB7B765218D7B3AE7557669D20307BB21CAA";
+ String additionalExtraInfoDigest =
+ "V609l+N6ActBveebfNbH5lQ6wHDNstDkFgyqEhBHwtA";
+ String extraInfoDigestLine = String.format("extra-info-digest %s %s",
+ extraInfoDigest, additionalExtraInfoDigest);
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExtraInfoDigestLine(extraInfoDigestLine);
+ assertEquals(extraInfoDigest, descriptor.getExtraInfoDigest());
+ }
+
+ @Test()
+ public void testOnionKeyOpt() throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyLines("opt onion-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
+ + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
+ + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----");
+ }
+
+ @Test()
+ public void testSigningKeyOpt() throws DescriptorParseException {
+ DescriptorBuilder.createWithSigningKeyLines("opt signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
+ + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
+ + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
+ + "MBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----");
+ }
+
+ @Test()
+ public void testHiddenServiceDirMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine(null);
+ assertNull(descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testHiddenServiceDirNoOpt()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine("hidden-service-dir");
+ assertEquals(Arrays.asList(new Integer[] {2}),
+ descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testHiddenServiceDirVersions2And3()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHiddenServiceDirLine("hidden-service-dir 2 3");
+ assertEquals(Arrays.asList(new Integer[] {2, 3}),
+ descriptor.getHiddenServiceDirVersions());
+ }
+
+ @Test()
+ public void testContactMissing() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine(null);
+ assertNull(descriptor.getContact());
+ }
+
+ @Test()
+ public void testContactOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("opt contact Random Person");
+ assertEquals("Random Person", descriptor.getContact());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testContactDuplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithContactLine("contact Random "
+ + "Person\ncontact Random Person");
+ }
+
+ @Test()
+ public void testContactNoSpace() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("contact");
+ assertEquals("", descriptor.getContact());
+ }
+
+ @Test()
+ public void testContactCarriageReturn()
+ throws DescriptorParseException {
+ String contactString = "Random "
+ + "Person -----BEGIN PGP PUBLIC KEY BLOCK-----\r"
+ + "Version: GnuPG v1 dot 4 dot 7 (Darwin)\r\r"
+ + "mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd"
+ + "07\rmTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESm"
+ + "oGEdAX\roid3XuIYRpRnqoafbFg9sg+OofX/mGrO+5ACfagQ9rlfx2oxCWijYw"
+ + "pYFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithContactLine("contact " + contactString);
+ assertEquals(contactString, descriptor.getContact());
+ }
+
+ @Test()
+ public void testExitPolicyRejectAllAcceptAll()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("reject *:*\naccept *:*");
+ assertEquals(Arrays.asList(new String[] {"reject *:*", "accept *:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test()
+ public void testExitPolicyOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("opt reject *:*");
+ assertEquals(Arrays.asList(new String[] {"reject *:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyNoPort() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject *");
+ }
+
+ @Test()
+ public void testExitPolicyAccept80RejectAll()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("accept *:80\nreject *:*");
+ assertEquals(Arrays.asList(new String[] {"accept *:80",
+ "reject *:*"}), descriptor.getExitPolicyLines());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyReject321() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject "
+ + "123.123.123.321:80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyRejectPort66666()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("reject *:66666");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyProjectAll() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines("project *:*");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testExitPolicyMissing() throws DescriptorParseException {
+ DescriptorBuilder.createWithExitPolicyLines(null);
+ }
+
+ @Test()
+ public void testExitPolicyMaskTypes() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithExitPolicyLines("reject 192.168.0.0/16:*\n"
+ + "reject 94.134.192.243/255.255.255.0:*");
+ assertEquals(Arrays.asList(new String[] { "reject 192.168.0.0/16:*",
+ "reject 94.134.192.243/255.255.255.0:*"}),
+ descriptor.getExitPolicyLines());
+ }
+
+ @Test()
+ public void testRouterSignatureOpt()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("opt "
+ + "router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "crypto lines are ignored anyway\n"
+ + "-----END SIGNATURE-----");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testRouterSignatureNotLastLine()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
+ + "-----BEGIN SIGNATURE-----\n"
+ + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
+ + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
+ + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
+ + "-----END SIGNATURE-----\ncontact me");
+ }
+
+ @Test()
+ public void testHibernatingOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("opt hibernating 1");
+ assertTrue(descriptor.isHibernating());
+ }
+
+ @Test()
+ public void testHibernatingFalse() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("hibernating 0");
+ assertFalse(descriptor.isHibernating());
+ }
+
+ @Test()
+ public void testHibernatingTrue() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithHibernatingLine("hibernating 1");
+ assertTrue(descriptor.isHibernating());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHibernatingYep() throws DescriptorParseException {
+ DescriptorBuilder.createWithHibernatingLine("hibernating yep");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testHibernatingNoSpace() throws DescriptorParseException {
+ DescriptorBuilder.createWithHibernatingLine("hibernating");
+ }
+
+ @Test()
+ public void testFamilyOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("opt family saberrider2008");
+ assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyFingerprint() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ assertEquals(Arrays.asList(new String[] {
+ "$D8733048FC8EC9102466AD8F3098622BF1BF71FD"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyNickname() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family saberrider2008");
+ assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyDuplicate() throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family "
+ + "saberrider2008\nfamily saberrider2008");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyNicknamePrefix() throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family $saberrider2008");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testFamilyFingerprintNoPrefix()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithFamilyLine("family "
+ + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
+ }
+
+ @Test()
+ public void testFamilyFingerprintNicknameNamed()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008");
+ assertEquals(Arrays.asList(new String[]
+ { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008" }),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testFamilyFingerprintNicknameUnnamed()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithFamilyLine("family "
+ + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008");
+ assertEquals(Arrays.asList(new String[]
+ { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008" }),
+ descriptor.getFamilyEntries());
+ }
+
+ @Test()
+ public void testWriteHistory() throws DescriptorParseException {
+ String writeHistoryLine = "write-history 2012-01-01 03:51:44 (900 s) "
+ + "4345856,261120,7591936,1748992";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine(writeHistoryLine);
+ assertNotNull(descriptor.getWriteHistory());
+ BandwidthHistory parsedWriteHistory = descriptor.getWriteHistory();
+ assertEquals(writeHistoryLine, parsedWriteHistory.getLine());
+ assertEquals(1325389904000L, (long) parsedWriteHistory.
+ getHistoryEndMillis());
+ assertEquals(900L, (long) parsedWriteHistory.getIntervalLength());
+ SortedMap<Long, Long> bandwidthValues = parsedWriteHistory.
+ getBandwidthValues();
+ assertEquals(4345856L, (long) bandwidthValues.remove(1325387204000L));
+ assertEquals(261120L, (long) bandwidthValues.remove(1325388104000L));
+ assertEquals(7591936L, (long) bandwidthValues.remove(1325389004000L));
+ assertEquals(1748992L, (long) bandwidthValues.remove(1325389904000L));
+ assertTrue(bandwidthValues.isEmpty());
+ }
+
+ @Test()
+ public void testWriteHistoryOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("opt write-history 2012-01-01 "
+ + "03:51:44 (900 s) 4345856,261120,7591936,1748992");
+ assertNotNull(descriptor.getWriteHistory());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistory3012() throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "3012-01-01 03:51:44 (900 s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoSeconds()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51 (900 s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoParathenses()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 900 s 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoSpaceSeconds()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900s) 4345856,261120,7591936,1748992");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryTrailingComma()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) 4345856,261120,7591936,");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryOneTwoThree()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) one,two,three");
+ }
+
+ @Test()
+ public void testWriteHistoryNoValuesSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(900 s) ");
+ assertEquals(900, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ assertTrue(descriptor.getWriteHistory().getBandwidthValues().
+ isEmpty());
+ }
+
+ @Test()
+ public void testWriteHistoryNoValuesNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(900 s)");
+ assertEquals(900, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ assertTrue(descriptor.getWriteHistory().getBandwidthValues().
+ isEmpty());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryNoS() throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine(
+ "write-history 2012-01-01 03:51:44 (900 ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testWriteHistoryTrailingNumber()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithWriteHistoryLine("write-history "
+ + "2012-01-01 03:51:44 (900 s) 4345856 1");
+ }
+
+ @Test()
+ public void testWriteHistory1800Seconds()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
+ + "(1800 s) 4345856");
+ assertEquals(1800L, (long) descriptor.getWriteHistory().
+ getIntervalLength());
+ }
+
+ @Test()
+ public void testReadHistory() throws DescriptorParseException {
+ String readHistoryLine = "read-history 2012-01-01 03:51:44 (900 s) "
+ + "4268032,139264,7797760,1415168";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithReadHistoryLine(readHistoryLine);
+ assertNotNull(descriptor.getReadHistory());
+ BandwidthHistory parsedReadHistory = descriptor.getReadHistory();
+ assertEquals(readHistoryLine, parsedReadHistory.getLine());
+ assertEquals(1325389904000L, (long) parsedReadHistory.
+ getHistoryEndMillis());
+ assertEquals(900L, (long) parsedReadHistory.getIntervalLength());
+ SortedMap<Long, Long> bandwidthValues = parsedReadHistory.
+ getBandwidthValues();
+ assertEquals(4268032L, (long) bandwidthValues.remove(1325387204000L));
+ assertEquals(139264L, (long) bandwidthValues.remove(1325388104000L));
+ assertEquals(7797760L, (long) bandwidthValues.remove(1325389004000L));
+ assertEquals(1415168L, (long) bandwidthValues.remove(1325389904000L));
+ assertTrue(bandwidthValues.isEmpty());
+ }
+
+ @Test()
+ public void testReadHistoryTwoSpaces() throws DescriptorParseException {
+ /* There are some server descriptors from older Tor versions that
+ * contain "opt read-history " lines. */
+ String readHistoryLine = "opt read-history 2012-01-01 03:51:44 "
+ + "(900 s) 4268032,139264,7797760,1415168";
+ DescriptorBuilder.createWithReadHistoryLine(readHistoryLine);
+ }
+
+ @Test()
+ public void testEventdnsOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("opt eventdns 1");
+ assertTrue(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test()
+ public void testEventdns1() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("eventdns 1");
+ assertTrue(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test()
+ public void testEventdns0() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithEventdnsLine("eventdns 0");
+ assertFalse(descriptor.getUsesEnhancedDnsLogic());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEventdnsTrue() throws DescriptorParseException {
+ DescriptorBuilder.createWithEventdnsLine("eventdns true");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEventdnsNo() throws DescriptorParseException {
+ DescriptorBuilder.createWithEventdnsLine("eventdns no");
+ }
+
+ @Test()
+ public void testCachesExtraInfoOpt() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithCachesExtraInfoLine("opt caches-extra-info");
+ assertTrue(descriptor.getCachesExtraInfo());
+ }
+
+ @Test()
+ public void testCachesExtraInfoNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithCachesExtraInfoLine("caches-extra-info");
+ assertTrue(descriptor.getCachesExtraInfo());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testCachesExtraInfoTrue() throws DescriptorParseException {
+ DescriptorBuilder.createWithCachesExtraInfoLine("caches-extra-info "
+ + "true");
+ }
+
+ @Test()
+ public void testAllowSingleHopExitsOpt()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithAllowSingleHopExitsLine("opt allow-single-hop-exits");
+ assertTrue(descriptor.getAllowSingleHopExits());
+ }
+
+ @Test()
+ public void testAllowSingleHopExitsNoSpace()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithAllowSingleHopExitsLine("allow-single-hop-exits");
+ assertTrue(descriptor.getAllowSingleHopExits());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAllowSingleHopExitsTrue()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithAllowSingleHopExitsLine(
+ "allow-single-hop-exits true");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testAllowSingleHopExitsNonAsciiKeyword()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
+ 0x14, (byte) 0xfe, 0x18, // non-ascii chars
+ 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2d, // "allow-"
+ 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2d, // "single-"
+ 0x68, 0x6f, 0x70, 0x2d, // "hop-"
+ 0x65, 0x78, 0x69, 0x74, 0x73 }, // "exits" (no newline)
+ false);
+ }
+
+ @Test()
+ public void testIpv6PolicyLine() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithIpv6PolicyLine("ipv6-policy accept 80,1194,1220,1293");
+ assertEquals("accept", descriptor.getIpv6DefaultPolicy());
+ assertEquals("80,1194,1220,1293", descriptor.getIpv6PortList());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPolicy()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPorts()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy accept");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineNoPolicyNoPorts()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testIpv6PolicyLineProject()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy project 80");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoIpv6PolicyLines() throws DescriptorParseException {
+ DescriptorBuilder.createWithIpv6PolicyLine(
+ "ipv6-policy accept 80,1194,1220,1293\n"
+ + "ipv6-policy accept 80,1194,1220,1293");
+ }
+
+ @Test()
+ public void testNtorOnionKeyLine() throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY=");
+ assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
+ descriptor.getNtorOnionKey());
+ }
+
+ @Test()
+ public void testNtorOnionKeyLineNoPadding()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
+ assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
+ descriptor.getNtorOnionKey());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyLineNoKey()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key ");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyLineTwoKeys()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTwoNtorOnionKeyLines() throws DescriptorParseException {
+ DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\nntor-onion-key "
+ + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\n");
+ }
+
+ @Test()
+ public void testTunnelledDirServerTrue()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder
+ .createWithTunnelledDirServerLine("tunnelled-dir-server");
+ assertTrue(descriptor.getTunnelledDirServer());
+ }
+
+ @Test()
+ public void testTunnelledDirServerFalse()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor = DescriptorBuilder
+ .createWithTunnelledDirServerLine(null);
+ assertFalse(descriptor.getTunnelledDirServer());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerTypo()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunneled-dir-server");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerTwice()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunnelled-dir-server\ntunnelled-dir-server");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testTunnelledDirServerArgs()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithTunnelledDirServerLine(
+ "tunnelled-dir-server 1");
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testUnrecognizedLineFail()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
+ }
+
+ @Test()
+ public void testUnrecognizedLineIgnore()
+ throws DescriptorParseException {
+ String unrecognizedLine = "unrecognized-line 1";
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(unrecognizedLine, false);
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add(unrecognizedLine);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testSomeOtherKey() throws DescriptorParseException {
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add("some-other-key");
+ unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
+ unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
+ + "1U4V9SeiKooSo5BpPL");
+ unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
+ + "IynCI4QryfCEuC3cTF");
+ unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
+ + "facOkpAgMBAAE=");
+ unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
+ StringBuilder sb = new StringBuilder();
+ for (String line : unrecognizedLines) {
+ sb.append("\n").append(line);
+ }
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(sb.toString().substring(1), false);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ @Test()
+ public void testUnrecognizedCryptoBlockNoKeyword()
+ throws DescriptorParseException {
+ List<String> unrecognizedLines = new ArrayList<>();
+ unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
+ unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
+ + "1U4V9SeiKooSo5BpPL");
+ unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
+ + "IynCI4QryfCEuC3cTF");
+ unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
+ + "facOkpAgMBAAE=");
+ unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
+ StringBuilder sb = new StringBuilder();
+ for (String line : unrecognizedLines) {
+ sb.append("\n").append(line);
+ }
+ ServerDescriptor descriptor = DescriptorBuilder.
+ createWithUnrecognizedLine(sb.toString().substring(1), false);
+ assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
+ }
+
+ private static final String IDENTITY_ED25519_LINES =
+ "identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
+ + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
+ + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
+ + "\n"
+ + "-----END ED25519 CERT-----";
+
+ private static final String MASTER_KEY_ED25519_LINE =
+ "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
+
+ private static final String ROUTER_SIG_ED25519_LINE =
+ "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
+ + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
+
+ @Test()
+ public void testEd25519() throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ assertEquals(IDENTITY_ED25519_LINES.substring(
+ IDENTITY_ED25519_LINES.indexOf("\n") + 1),
+ descriptor.getIdentityEd25519());
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ assertEquals(ROUTER_SIG_ED25519_LINE.substring(
+ ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getRouterSignatureEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityMasterKeyMismatch()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519IdentityMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(null,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
+ + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519IdentityEmptyCrypto()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
+ + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519MasterKeyMissing()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ null, ROUTER_SIG_ED25519_LINE);
+ assertEquals(MASTER_KEY_ED25519_LINE.substring(
+ MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
+ descriptor.getMasterKeyEd25519());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519MasterKeyDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
+ ROUTER_SIG_ED25519_LINE);
+ }
+
+ @Test()
+ public void testEd25519RouterSigMissing()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, null);
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testEd25519RouterSigDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
+ MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
+ + ROUTER_SIG_ED25519_LINE);
+ }
+
+ private static final String ONION_KEY_CROSSCERT_LINES =
+ "onion-key-crosscert\n"
+ + "-----BEGIN CROSSCERT-----\n"
+ + "gVWpiNgG2FekW1uonr4KKoqykjr4bqUBKGZfu6s9rvsV1TThnquZNP6ZhX2IPdQA"
+ + "\nlfKtzFggGu/4BiJ5oTSDj2sK2DMjY3rjrMQZ3I/wJ25yhc9gxjqYqUYO9MmJwA"
+ + "Lp\nfYkqp/t4WchJpyva/4hK8vITsI6eT2BfY/DWMy/suIE=\n"
+ + "-----END CROSSCERT-----";
+
+ private static final String NTOR_ONION_KEY_CROSSCERT_LINES =
+ "ntor-onion-key-crosscert 1\n"
+ + "-----BEGIN ED25519 CERT-----\n"
+ + "AQoABiUeAdauu1MxYGMmGLTCPaoes0RvW7udeLc1t8LZ4P3CDo5bAN4nrRfbCfOt"
+ + "\nz2Nwqn8tER1a+Ry6Vs+ilMZA55Rag4+f6Zdb1fmHWknCxbQlLHpqHACMtemPda"
+ + "Ka\nErPtMuiEqAc=\n"
+ + "-----END ED25519 CERT-----";
+
+ @Test()
+ public void testOnionKeyCrosscert() throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ ONION_KEY_CROSSCERT_LINES);
+ assertEquals(ONION_KEY_CROSSCERT_LINES.substring(
+ ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
+ descriptor.getOnionKeyCrosscert());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testOnionKeyCrosscertDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ ONION_KEY_CROSSCERT_LINES + "\n" + ONION_KEY_CROSSCERT_LINES);
+ }
+
+ @Test()
+ public void testNtorOnionKeyCrosscert()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithNtorOnionKeyCrosscertLines(
+ NTOR_ONION_KEY_CROSSCERT_LINES);
+ assertEquals(NTOR_ONION_KEY_CROSSCERT_LINES.substring(
+ NTOR_ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
+ descriptor.getNtorOnionKeyCrosscert());
+ assertEquals(1, descriptor.getNtorOnionKeyCrosscertSign());
+ }
+
+ @Test(expected = DescriptorParseException.class)
+ public void testNtorOnionKeyCrosscertDuplicate()
+ throws DescriptorParseException {
+ DescriptorBuilder.createWithOnionKeyCrosscertLines(
+ NTOR_ONION_KEY_CROSSCERT_LINES + "\n"
+ + NTOR_ONION_KEY_CROSSCERT_LINES);
+ }
+}
+
diff --git a/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
new file mode 100644
index 0000000..b5cde0a
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
@@ -0,0 +1,97 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.Descriptor;
+
+public class TorperfResultImplTest {
+
+ @Test()
+ public void testAnnotatedInput() throws Exception{
+ TorperfResultImpl result = (TorperfResultImpl)
+ (TorperfResultImpl.parseTorperfResults((torperfAnnotation + input)
+ .getBytes("US-ASCII"), false).get(0));
+ assertEquals("Expected one annotation.", 1,
+ result.getAnnotations().size());
+ assertEquals(torperfAnnotation.substring(0, 17),
+ result.getAnnotations().get(0));
+ int count = 0;
+ for (Long l: result.getDataPercentiles().values()) {
+ assertNotNull(l);
+ assertEquals(l.longValue(), deciles[count++]);
+ }
+ }
+
+ @Test()
+ public void testPartiallyAnnotatedInput() throws Exception{
+ byte[] asciiBytes = (torperfAnnotation
+ + input + input + input).getBytes("US-ASCII");
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ asciiBytes, false);
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
+ assertEquals(3, result.size());
+ assertEquals("Expected zero annotations.", 0,
+ ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
+ assertEquals("Expected zero annotations.", 0,
+ ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
+ }
+
+ @Test()
+ public void testAllAnnotatedInput() throws Exception {
+ byte[] asciiBytes = (torperfAnnotation + input
+ + torperfAnnotation + input
+ + torperfAnnotation + input).getBytes("US-ASCII");
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ asciiBytes, false);
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
+ assertEquals(3, result.size());
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
+ assertEquals("Expected one annotation.", 1,
+ ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
+ }
+
+ private static long[] deciles = new long[] {
+ 1441065602980L, 1441065603030L, 1441065603090L, 1441065603120L,
+ 1441065603230L, 1441065603250L, 1441065603310L, 1441065603370L,
+ 1441065603370L };
+
+ private static final String torperfAnnotation = "@type torperf 1.0\n";
+
+ private static final String input =
+ "BUILDTIMES=0.872834920883,1.09103679657,1.49180984497 "
+ + "CIRC_ID=1228 CONNECT=1441065601.86 DATACOMPLETE=1441065603.39 "
+ + "DATAPERC10=1441065602.98 DATAPERC20=1441065603.03 "
+ + "DATAPERC30=1441065603.09 DATAPERC40=1441065603.12 "
+ + "DATAPERC50=1441065603.23 DATAPERC60=1441065603.25 "
+ + "DATAPERC70=1441065603.31 DATAPERC80=1441065603.37 "
+ + "DATAPERC90=1441065603.37 DATAREQUEST=1441065602.38 "
+ + "DATARESPONSE=1441065602.84 DIDTIMEOUT=0 FILESIZE=51200 "
+ + "LAUNCH=1441065361.30 NEGOTIATE=1441065601.86 "
+ + "PATH=$C4C9C332D25B3546BEF4E1250CF410E97EF996E6,"
+ + "$C43FA6474A9F071E9120DF63ED6EB8FDBA105234,"
+ + "$7C0AA4E3B73E407E9F5FEB1912F8BE26D8AA124D QUANTILE=0.800000 "
+ + "READBYTES=51416 REQUEST=1441065601.86 RESPONSE=1441065602.38 "
+ + "SOCKET=1441065601.86 SOURCE=moria START=1441065601.86 "
+ + "TIMEOUT=1500 USED_AT=1441065603.40 USED_BY=2475 WRITEBYTES=75\n";
+
+ @Test()
+ public void testDatapercNonNumeric() throws Exception {
+ List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
+ ("DATAPERMILLE=2.0 " + input).getBytes(), false);
+ assertEquals(1, result.size());
+ TorperfResultImpl torperfResult = (TorperfResultImpl) result.get(0);
+ assertEquals(1, torperfResult.getUnrecognizedKeys().size());
+ assertEquals("DATAPERMILLE",
+ torperfResult.getUnrecognizedKeys().firstKey());
+ }
+}
+
diff --git a/test/org/torproject/descriptor/benchmark/MeasurePerformance.java b/test/org/torproject/descriptor/benchmark/MeasurePerformance.java
deleted file mode 100644
index a52020a..0000000
--- a/test/org/torproject/descriptor/benchmark/MeasurePerformance.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/* Copyright 2016 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.benchmark;
-
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorReader;
-import org.torproject.descriptor.DescriptorSourceFactory;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.Microdescriptor;
-import org.torproject.descriptor.NetworkStatusEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-import org.torproject.descriptor.ServerDescriptor;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedMap;
-
-public class MeasurePerformance {
-
- /* Check if all necessary files are available and then measure
- * performance of some more or less common use cases. */
- public static void main(String[] args) {
- if (!filesAvailable()) {
- return;
- }
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[0]));
- pause();
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[1]));
- pause();
- measureAverageAdvertisedBandwidth(new File(resDir, resPaths[2]));
- pause();
- measureCountriesV3Requests(new File(resDir, resPaths[3]));
- pause();
- measureCountriesV3Requests(new File(resDir, resPaths[4]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[5]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[6]));
- pause();
- measureAverageRelaysExit(new File(resDir, resPaths[7]));
- measureFractionRelaysExit80Microdescriptors(
- new File(resDir, resPaths[8]));
- measureFractionRelaysExit80Microdescriptors(
- new File(resDir, resPaths[9]));
- }
-
- private static File resDir = new File("res");
- private static String[] resPaths = new String[] {
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11.tar.xz",
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11.tar",
- "archive/relay-descriptors/server-descriptors/"
- + "server-descriptors-2015-11",
- "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar.xz",
- "archive/relay-descriptors/extra-infos/extra-infos-2015-11.tar",
- "archive/relay-descriptors/consensuses/consensuses-2015-11.tar.xz",
- "archive/relay-descriptors/consensuses/consensuses-2015-11.tar",
- "archive/relay-descriptors/consensuses/consensuses-2015-11",
- "archive/relay-descriptors/microdescs/microdescs-2015-11.tar.xz",
- "archive/relay-descriptors/microdescs/microdescs-2015-11.tar"
- };
-
- private static boolean filesAvailable() {
- if (!resDir.exists() || !resDir.isDirectory()) {
- return false;
- }
- for (String resPath : resPaths) {
- if (!(new File(resDir, resPath).exists())) {
- System.err.println("Missing resource: " + resDir + "/" + resPath);
- return false;
- }
- }
- return true;
- }
-
- private static void pause() {
- try {
- Thread.sleep(15L * 1000L);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private static void measureAverageAdvertisedBandwidth(
- File tarballFileOrDirectory) {
- System.out.println("Starting measureAverageAdvertisedBandwidth");
- long startedMillis = System.currentTimeMillis();
- long sumAdvertisedBandwidth = 0, countedServerDescriptors = 0;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFileOrDirectory);
- descriptorReader.addDirectory(tarballFileOrDirectory);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof ServerDescriptor)) {
- continue;
- }
- ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
- sumAdvertisedBandwidth += (long) Math.min(Math.min(
- serverDescriptor.getBandwidthRate(),
- serverDescriptor.getBandwidthBurst()),
- serverDescriptor.getBandwidthObserved());
- countedServerDescriptors++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureAverageAdvertisedBandwidth");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed server descriptors: %d%n",
- countedServerDescriptors);
- System.out.printf("Average advertised bandwidth: %d%n",
- sumAdvertisedBandwidth / countedServerDescriptors);
- System.out.printf("Time per server descriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedServerDescriptors));
- }
-
- private static void measureCountriesV3Requests(File tarballFile) {
- System.out.println("Starting measureCountriesV3Requests");
- long startedMillis = System.currentTimeMillis();
- Set<String> countries = new HashSet<>();
- long countedExtraInfoDescriptors = 0;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFile);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof ExtraInfoDescriptor)) {
- continue;
- }
- ExtraInfoDescriptor extraInfoDescriptor =
- (ExtraInfoDescriptor) descriptor;
- SortedMap<String, Integer> dirreqV3Reqs =
- extraInfoDescriptor.getDirreqV3Reqs();
- if (dirreqV3Reqs != null) {
- countries.addAll(dirreqV3Reqs.keySet());
- }
- countedExtraInfoDescriptors++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureCountriesV3Requests");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed extra-info descriptors: %d%n",
- countedExtraInfoDescriptors);
- System.out.printf("Number of countries: %d%n",
- countries.size());
- System.out.printf("Time per extra-info descriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedExtraInfoDescriptors));
- }
-
- private static void measureAverageRelaysExit(
- File tarballFileOrDirectory) {
- System.out.println("Starting measureAverageRelaysExit");
- long startedMillis = System.currentTimeMillis();
- long totalRelaysWithExitFlag = 0L, totalRelays = 0L,
- countedConsensuses = 0L;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFileOrDirectory);
- descriptorReader.addDirectory(tarballFileOrDirectory);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
- continue;
- }
- RelayNetworkStatusConsensus consensus =
- (RelayNetworkStatusConsensus) descriptor;
- for (NetworkStatusEntry entry :
- consensus.getStatusEntries().values()) {
- if (entry.getFlags().contains("Exit")) {
- totalRelaysWithExitFlag++;
- }
- totalRelays++;
- }
- countedConsensuses++;
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending measureAverageRelaysExit");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed consensuses: %d%n", countedConsensuses);
- System.out.printf("Total number of status entries: %d%n",
- totalRelays);
- System.out.printf("Total number of status entries with Exit flag: "
- + "%d%n", totalRelaysWithExitFlag);
- System.out.printf("Average number of relays with Exit Flag: %.2f%n",
- (double) totalRelaysWithExitFlag / (double) totalRelays);
- System.out.printf("Time per consensus: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedConsensuses));
- }
-
- private static void measureFractionRelaysExit80Microdescriptors(
- File tarballFile) {
- System.out.println("Starting "
- + "measureFractionRelaysExit80Microdescriptors");
- long startedMillis = System.currentTimeMillis();
- long totalRelaysWithExitFlag = 0L, countedMicrodescriptors = 0L;
- DescriptorReader descriptorReader =
- DescriptorSourceFactory.createDescriptorReader();
- descriptorReader.addTarball(tarballFile);
- Iterator<DescriptorFile> descriptorFiles =
- descriptorReader.readDescriptors();
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof Microdescriptor)) {
- continue;
- }
- countedMicrodescriptors++;
- Microdescriptor microdescriptor =
- (Microdescriptor) descriptor;
- String defaultPolicy = microdescriptor.getDefaultPolicy();
- if (defaultPolicy == null) {
- continue;
- }
- boolean accept = "accept".equals(
- microdescriptor.getDefaultPolicy());
- for (String ports : microdescriptor.getPortList().split(",")) {
- if (ports.contains("-")) {
- String[] parts = ports.split("-");
- int from = Integer.parseInt(parts[0]);
- int to = Integer.parseInt(parts[1]);
- if (from <= 80 && to >= 80) {
- if (accept) {
- totalRelaysWithExitFlag++;
- }
- } else if (to > 80) {
- if (!accept) {
- totalRelaysWithExitFlag++;
- }
- break;
- }
- } else if ("80".equals(ports)) {
- if (accept) {
- totalRelaysWithExitFlag++;
- }
- break;
- }
- }
- }
- }
- long endedMillis = System.currentTimeMillis();
- System.out.println("Ending "
- + "measureFractionRelaysExit80Microdescriptors");
- System.out.printf("Total time: %d millis%n",
- endedMillis - startedMillis);
- System.out.printf("Processed microdescriptors: %d%n",
- countedMicrodescriptors);
- System.out.printf("Total number of microdescriptors that exit to 80: "
- + "%d%n", totalRelaysWithExitFlag);
- System.out.printf("Average number of relays that exit to 80: %.2f%n",
- (double) totalRelaysWithExitFlag
- / (double) countedMicrodescriptors);
- System.out.printf("Time per microdescriptor: %.6f millis%n",
- ((double) (endedMillis - startedMillis))
- / ((double) countedMicrodescriptors));
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
deleted file mode 100644
index 0847e13..0000000
--- a/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.BridgeNetworkStatus;
-import org.torproject.descriptor.DescriptorParseException;
-
-/* Test parsing of bridge network statuses. Some of the parsing code is
- * already tested in the consensus/vote-parsing tests. */
-public class BridgeNetworkStatusTest {
-
- /* Helper class to build a bridge network status based on default data
- * and modifications requested by test methods. */
- private static class StatusBuilder {
- private String fileName = "20151121-173936-"
- + "4A0CCD2DDC7995083D73F5D667100C8A5831F16D";
- private static BridgeNetworkStatus
- createWithFileName(String fileName)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.fileName = fileName;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private String publishedLine = "published 2015-11-21 17:39:36";
- private static BridgeNetworkStatus
- createWithPublishedLine(String line)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.publishedLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private String flagThresholdsLine = "flag-thresholds "
- + "stable-uptime=3105080 stable-mtbf=2450615 fast-speed=55000 "
- + "guard-wfu=98.000% guard-tk=691200 guard-bw-inc-exits=337000 "
- + "guard-bw-exc-exits=339000 enough-mtbf=1 "
- + "ignoring-advertised-bws=0";
- private static BridgeNetworkStatus
- createWithFlagThresholdsLine(String line)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.flagThresholdsLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- true);
- }
- private List<String> statusEntries = new ArrayList<>();
- private String unrecognizedHeaderLine = null;
- protected static BridgeNetworkStatus
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.unrecognizedHeaderLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static BridgeNetworkStatus
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- sb.unrecognizedStatusEntryLine = line;
- return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
- failUnrecognizedDescriptorLines);
- }
-
- private StatusBuilder() {
- this.statusEntries.add("r Unnamed ABk0wg4j6BLCdZKleVtmNrfzJGI "
- + "bh7gVU1Cz6+JG+7j4qGsF4prDi8 2015-11-21 15:46:25 "
- + "10.153.163.200 443 0\ns Fast Running Stable Valid\n"
- + "w Bandwidth=264\np reject 1-65535");
- }
- private byte[] buildStatus() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendStatusEntries(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.flagThresholdsLine != null) {
- sb.append(this.flagThresholdsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- }
-
- @Test()
- public void testSampleStatus() throws DescriptorParseException {
- StatusBuilder sb = new StatusBuilder();
- BridgeNetworkStatus status =
- new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName, true);
- assertEquals(1448127576000L, status.getPublishedMillis());
- assertEquals(3105080L, status.getStableUptime());
- assertEquals(2450615L, status.getStableMtbf());
- assertEquals(55000L, status.getFastBandwidth());
- assertEquals(98.0, status.getGuardWfu(), 0.001);
- assertEquals(691200L, status.getGuardTk());
- assertEquals(337000L, status.getGuardBandwidthIncludingExits());
- assertEquals(339000L, status.getGuardBandwidthExcludingExits());
- assertEquals(1, status.getEnoughMtbfInfo());
- assertEquals(0, status.getIgnoringAdvertisedBws());
- assertEquals(264, status.getStatusEntries().get(
- "001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
- assertTrue(status.getUnrecognizedLines().isEmpty());
- }
-
- @Test()
- public void testPublishedNoLine() throws DescriptorParseException {
- BridgeNetworkStatus status =
- StatusBuilder.createWithPublishedLine(null);
- assertEquals(1448127576000L, status.getPublishedMillis());
- }
-
- @Test()
- public void testFlagThresholdsNoLine() throws DescriptorParseException {
- BridgeNetworkStatus status =
- StatusBuilder.createWithFlagThresholdsLine(null);
- assertEquals(-1L, status.getStableUptime());
- assertEquals(-1L, status.getStableMtbf());
- assertEquals(-1L, status.getFastBandwidth());
- assertEquals(-1.0, status.getGuardWfu(), 0.001);
- assertEquals(-1L, status.getGuardTk());
- assertEquals(-1L, status.getGuardBandwidthIncludingExits());
- assertEquals(-1L, status.getGuardBandwidthExcludingExits());
- assertEquals(-1, status.getEnoughMtbfInfo());
- assertEquals(-1, status.getIgnoringAdvertisedBws());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ConsensusBuilder.java b/test/org/torproject/descriptor/impl/ConsensusBuilder.java
deleted file mode 100644
index 29a2d47..0000000
--- a/test/org/torproject/descriptor/impl/ConsensusBuilder.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* Helper class to build a consensus based on default data and
- * modifications requested by test methods. */
-public class ConsensusBuilder {
- String networkStatusVersionLine = "network-status-version 3";
- protected static RelayNetworkStatusConsensus
- createWithNetworkStatusVersionLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.networkStatusVersionLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String voteStatusLine = "vote-status consensus";
- protected static RelayNetworkStatusConsensus
- createWithVoteStatusLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.voteStatusLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String consensusMethodLine = "consensus-method 11";
- protected static RelayNetworkStatusConsensus
- createWithConsensusMethodLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.consensusMethodLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String validAfterLine = "valid-after 2011-11-30 09:00:00";
- protected static RelayNetworkStatusConsensus
- createWithValidAfterLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.validAfterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
- protected static RelayNetworkStatusConsensus
- createWithFreshUntilLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.freshUntilLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String validUntilLine = "valid-until 2011-11-30 12:00:00";
- protected static RelayNetworkStatusConsensus
- createWithValidUntilLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.validUntilLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String votingDelayLine = "voting-delay 300 300";
- protected static RelayNetworkStatusConsensus
- createWithVotingDelayLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.votingDelayLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- String clientVersionsLine = "client-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- protected static RelayNetworkStatusConsensus
- createWithClientVersionsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.clientVersionsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- String serverVersionsLine = "server-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- protected static RelayNetworkStatusConsensus
- createWithServerVersionsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.serverVersionsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String packageLines = null;
- protected static RelayNetworkStatusConsensus
- createWithPackageLines(String lines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.packageLines = lines;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String knownFlagsLine = "known-flags Authority BadExit Exit "
- + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
- protected static RelayNetworkStatusConsensus
- createWithKnownFlagsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.knownFlagsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String paramsLine = "params "
- + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
- + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
- + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
- + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
- protected static RelayNetworkStatusConsensus
- createWithParamsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.paramsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- List<String> dirSources = new ArrayList<>();
- List<String> statusEntries = new ArrayList<>();
- private String directoryFooterLine = "directory-footer";
- protected void setDirectoryFooterLine(String line) {
- this.directoryFooterLine = line;
- }
- protected static RelayNetworkStatusConsensus
- createWithDirectoryFooterLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.directoryFooterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private String bandwidthWeightsLine = "bandwidth-weights Wbd=285 "
- + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=1021 Wee=10000 "
- + "Weg=1021 Wem=10000 Wgb=10000 Wgd=8694 Wgg=10000 Wgm=10000 "
- + "Wmb=10000 Wmd=285 Wme=0 Wmg=0 Wmm=10000";
- protected void setBandwidthWeightsLine(String line) {
- this.bandwidthWeightsLine = line;
- }
- protected static RelayNetworkStatusConsensus
- createWithBandwidthWeightsLine(String line)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.bandwidthWeightsLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
- private List<String> directorySignatures = new ArrayList<>();
- protected void addDirectorySignature(String directorySignatureString) {
- this.directorySignatures.add(directorySignatureString);
- }
- private String unrecognizedHeaderLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedHeaderLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirSourceLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedDirSourceLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedDirSourceLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedStatusEntryLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedFooterLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedFooterLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedFooterLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirectorySignatureLine = null;
- protected static RelayNetworkStatusConsensus
- createWithUnrecognizedDirectorySignatureLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.unrecognizedDirectorySignatureLine = line;
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- failUnrecognizedDescriptorLines);
- }
-
- protected ConsensusBuilder() {
- this.dirSources.add("dir-source tor26 "
- + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 86.59.21.38 "
- + "86.59.21.38 80 443\ncontact Peter Palfrader\nvote-digest "
- + "0333880AA67ED7E07C11108656D0C8D6DD1C7E5D");
- this.dirSources.add("dir-source ides "
- + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 216.224.124.114 "
- + "216.224.124.114 9030 9090\ncontact Mike Perry "
- + "<mikeperryTAfsckedTODorg>\nvote-digest "
- + "1A8827ECD53184F7A771EFA9B3D30DC473FE8670");
- this.statusEntries.add("r ANONIONROUTER "
- + "AHhuQ8zFQJdT8l42Axxc6m6kNwI yEMZ5B/JQixNZgC1+2rLe0pR9rU "
- + "2011-11-30 02:52:58 93.128.66.111 24051 24052\ns Exit Fast "
- + "Named Running V2Dir Valid\nv Tor 0.2.2.34\nw "
- + "Bandwidth=1100\np reject 25,119,135-139,6881-6999");
- this.statusEntries.add("r Magellan AHlabo2RwnD8I7MPOIpJVVPgGJQ "
- + "rB/7uzI4mU38bZ9cSXEy+Z/4Cuk 2011-11-30 05:37:35 "
- + "188.177.149.216 9001 9030\ns Fast Named Running V2Dir "
- + "Valid\nv Tor 0.2.2.34\nw Bandwidth=367\np reject 1-65535");
- this.directorySignatures.add("directory-signature "
- + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
- + "3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "NYRcTWAMRiYYiGW0hIbzeZKU6sefg98AwwXrQUCudO8wfA1cfgttTDoscB9I"
- + "TbOY\nr+c30jV/qQCMamTAEDGgJTw8KghI32vytupKallI1EjCOF8UvL1UnA"
- + "LgpaR7sZ3W\n7WQZVVrWDtnYaULOEKfwnGnRC7WwE+YRSysbzwwCVs0=\n"
- + "-----END SIGNATURE-----");
- this.directorySignatures.add("directory-signature "
- + "27B6B5996C426270A5C95488AA5BCEB6BCC86956 "
- + "D5C30C15BB3F1DA27669C2D88439939E8F418FCF\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "DzFPj3vyYrCv0W3r8qDPJPlmeLnadY+drjWkdOqO66Ih/hAWBb9KcBJAX1sX"
- + "aDA7\n/iSaDhduBXuJdcu8lbmMP8d6uYBdRjHXqWDXySUZAkSfPB4JJPNGvf"
- + "oQA/qeby7E\n5374pPPL6WwCLJHkKtk21S9oHDmFBdlZq7JWQelWlVM=\n"
- + "-----END SIGNATURE-----");
- }
- protected byte[] buildConsensus() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendDirSources(sb);
- this.appendStatusEntries(sb);
- this.appendFooter(sb);
- this.appendDirectorySignatures(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.networkStatusVersionLine != null) {
- sb.append(this.networkStatusVersionLine).append("\n");
- }
- if (this.voteStatusLine != null) {
- sb.append(this.voteStatusLine).append("\n");
- }
- if (this.consensusMethodLine != null) {
- sb.append(this.consensusMethodLine).append("\n");
- }
- if (this.validAfterLine != null) {
- sb.append(this.validAfterLine).append("\n");
- }
- if (this.freshUntilLine != null) {
- sb.append(this.freshUntilLine).append("\n");
- }
- if (this.validUntilLine != null) {
- sb.append(this.validUntilLine).append("\n");
- }
- if (this.votingDelayLine != null) {
- sb.append(this.votingDelayLine).append("\n");
- }
- if (this.clientVersionsLine != null) {
- sb.append(this.clientVersionsLine).append("\n");
- }
- if (this.serverVersionsLine != null) {
- sb.append(this.serverVersionsLine).append("\n");
- }
- if (this.packageLines != null) {
- sb.append(this.packageLines).append("\n");
- }
- if (this.knownFlagsLine != null) {
- sb.append(this.knownFlagsLine).append("\n");
- }
- if (this.paramsLine != null) {
- sb.append(this.paramsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendDirSources(StringBuilder sb) {
- for (String dirSource : this.dirSources) {
- sb.append(dirSource).append("\n");
- }
- if (this.unrecognizedDirSourceLine != null) {
- sb.append(this.unrecognizedDirSourceLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- private void appendFooter(StringBuilder sb) {
- if (this.directoryFooterLine != null) {
- sb.append(this.directoryFooterLine).append("\n");
- }
- if (this.bandwidthWeightsLine != null) {
- sb.append(this.bandwidthWeightsLine).append("\n");
- }
- if (this.unrecognizedFooterLine != null) {
- sb.append(this.unrecognizedFooterLine).append("\n");
- }
- }
- private void appendDirectorySignatures(StringBuilder sb) {
- for (String directorySignature : this.directorySignatures) {
- sb.append(directorySignature).append("\n");
- }
- if (this.unrecognizedDirectorySignatureLine != null) {
- sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
- }
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java b/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
deleted file mode 100644
index fde8e57..0000000
--- a/test/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.util.SortedMap;
-
-import org.junit.Test;
-
-public class DescriptorCollectorImplTest {
-
- private static final String REMOTE_DIRECTORY_CONSENSUSES =
- "/recent/relay-descriptors/consensuses/";
-
- @Test()
- public void testOneFile() {
- String remoteFilename = "2015-05-24-12-00-00-consensus";
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"" + remoteFilename + "\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">24-May-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertSame(1, remoteFiles.size());
- assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
- remoteFiles.firstKey());
- assertEquals((Long) 1432469280000L,
- remoteFiles.get(remoteFiles.firstKey()));
- }
-
- @Test()
- public void testSameFileTwoTimestampsLastWins() {
- String remoteFilename = "2015-05-24-12-00-00-consensus";
- String firstTimestamp = "24-May-2015 12:04";
- String secondTimestamp = "24-May-2015 12:08";
- String lineFormat = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"%s\">2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">%s </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>\n";
- String directoryListing = String.format(lineFormat + lineFormat,
- remoteFilename, firstTimestamp, remoteFilename, secondTimestamp);
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertSame(1, remoteFiles.size());
- assertEquals(REMOTE_DIRECTORY_CONSENSUSES + remoteFilename,
- remoteFiles.firstKey());
- assertEquals((Long) 1432469280000L,
- remoteFiles.get(remoteFiles.firstKey()));
- }
-
- @Test()
- public void testSubDirectoryOnly() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/folder.gif\" alt=\"[DIR]\"></td><td>"
- + "<a href=\"subdir/\">subdir/</a></td>"
- + "<td align=\"right\">27-May-2015 14:07 </td>"
- + "<td align=\"right\"> - </td><td> </td></tr>";
- DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
- SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testParentDirectoryOnly() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/back.gif\" alt=\"[DIR]\"></td><td>"
- + "<a href=\"/recent/relay-descriptors/\">Parent Directory</a>"
- + "</td><td> </td><td align=\"right\"> - </td>"
- + "<td> </td></tr>";
- DescriptorCollectorImpl collector = new DescriptorCollectorImpl();
- SortedMap<String, Long> remoteFiles = collector.parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testUnexpectedDateFormat() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">2015-05-24 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNotNull(remoteFiles);
- assertTrue(remoteFiles.isEmpty());
- }
-
- @Test()
- public void testInvalidDate() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">34-May-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNull(remoteFiles);
- }
-
- @Test()
- public void testInvalidLocaleDe() {
- String directoryListing = "<tr><td valign=\"top\">"
- + "<img src=\"/icons/unknown.gif\" alt=\"[ ]\"></td><td>"
- + "<a href=\"2015-05-24-12-00-00-consensus\">"
- + "2015-05-24-12-00-00-consensus</a></td>"
- + "<td align=\"right\">24-Mai-2015 12:08 </td>"
- + "<td align=\"right\">1.5M</td><td> </td></tr>";
- SortedMap<String, Long> remoteFiles =
- new DescriptorCollectorImpl().parseDirectoryListing(
- REMOTE_DIRECTORY_CONSENSUSES, directoryListing);
- assertNull(remoteFiles);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ExitListImplTest.java b/test/org/torproject/descriptor/impl/ExitListImplTest.java
deleted file mode 100644
index a563857..0000000
--- a/test/org/torproject/descriptor/impl/ExitListImplTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Test;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExitListEntry;
-
-public class ExitListImplTest {
-
- @Test()
- public void testAnnotatedInput() throws Exception {
- ExitListImpl result = new ExitListImpl((tordnselAnnotation + input)
- .getBytes("US-ASCII"), fileName, false);
- assertEquals("Expected one annotation.", 1,
- result.getAnnotations().size());
- assertEquals(tordnselAnnotation.substring(0, 18),
- result.getAnnotations().get(0));
- assertEquals(1441065722000L, result.getDownloadedMillis());
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- assertEquals("Found: " + result.getExitListEntries(), 7,
- result.getExitListEntries().size());
- assertEquals("Found: " + result.getEntries(), 5,
- result.getEntries().size());
- }
-
- @Test()
- public void testMultipleOldExitAddresses() throws Exception {
- ExitListImpl result = new ExitListImpl(
- (tordnselAnnotation + multiExitAddressInput)
- .getBytes("US-ASCII"), fileName, false);
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- assertEquals("Found: " + result.getExitListEntries(),
- 3, result.getExitListEntries().size());
- Map<String, Long> testMap = new HashMap();
- testMap.put("81.7.17.171", 1441044592000L);
- testMap.put("81.7.17.172", 1441044652000L);
- testMap.put("81.7.17.173", 1441044712000L);
- for (ExitListEntry ele : result.getExitListEntries()) {
- Map<String, Long> map = ele.getExitAddresses();
- assertEquals("Found: " + map, 1, map.size());
- Map.Entry<String, Long> ea = map.entrySet().iterator().next();
- assertTrue("Map: " + testMap,
- testMap.keySet().contains(ea.getKey()));
- assertTrue("Map: " + testMap + " exitaddress: " + ea,
- testMap.values().contains(ea.getValue()));
- testMap.remove(ea.getKey());
- }
- assertTrue("Map: " + testMap, testMap.isEmpty());
- }
-
- @Test()
- public void testMultipleExitAddresses() throws Exception {
- ExitListImpl result = new ExitListImpl(
- (tordnselAnnotation + multiExitAddressInput)
- .getBytes("US-ASCII"), fileName, false);
- assertTrue("Unrecognized lines: " + result.getUnrecognizedLines(),
- result.getUnrecognizedLines().isEmpty());
- Map<String, Long> map = result.getEntries()
- .iterator().next().getExitAddresses();
- assertEquals("Found: " + map, 3, map.size());
- assertTrue("Map: " + map, map.containsKey("81.7.17.171"));
- assertTrue("Map: " + map, map.containsKey("81.7.17.172"));
- assertTrue("Map: " + map, map.containsKey("81.7.17.173"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testInsufficientInput0() throws Exception {
- new ExitListImpl((tordnselAnnotation + insufficientInput[0])
- .getBytes("US-ASCII"), fileName, false);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testInsufficientInput1() throws Exception {
- new ExitListImpl((tordnselAnnotation + insufficientInput[1])
- .getBytes("US-ASCII"), fileName, false);
- }
-
- private static final String tordnselAnnotation = "@type tordnsel 1.0\n";
- private static final String fileName = "2015-09-01-00-02-02";
- private static final String[] insufficientInput = new String[] {
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n",
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n" };
-
- private static final String multiExitAddressInput =
- "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
- + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n";
- private static final String input = "Downloaded 2015-09-01 00:02:02\n"
- + "ExitNode 0011BD2485AD45D984EC4159C88FC066E5E3300E\n"
- + "Published 2015-08-31 16:17:30\n"
- + "LastStatus 2015-08-31 17:03:18\n"
- + "ExitAddress 162.247.72.201 2015-08-31 17:09:23\n"
- + "ExitNode 0098C475875ABC4AA864738B1D1079F711C38287\n"
- + "Published 2015-08-31 13:59:24\n"
- + "LastStatus 2015-08-31 15:03:20\n"
- + "ExitAddress 162.248.160.151 2015-08-31 15:07:27\n"
- + "ExitNode 00C4B4731658D3B4987132A3F77100CFCB190D97\n"
- + "Published 2015-08-31 17:47:52\n"
- + "LastStatus 2015-08-31 18:03:17\n"
- + "ExitAddress 81.7.17.171 2015-08-31 18:09:52\n"
- + "ExitAddress 81.7.17.172 2015-08-31 18:10:52\n"
- + "ExitAddress 81.7.17.173 2015-08-31 18:11:52\n"
- + "ExitNode 00F2D93EBAF2F51D6EE4DCB0F37D91D72F824B16\n"
- + "Published 2015-08-31 14:39:05\n"
- + "LastStatus 2015-08-31 16:02:18\n"
- + "ExitAddress 23.239.18.57 2015-08-31 16:06:07\n"
- + "ExitNode 011B1D1E876B2C835D01FB9D407F2E00B28077F6\n"
- + "Published 2015-08-31 05:14:35\n"
- + "LastStatus 2015-08-31 06:03:29\n"
- + "ExitAddress 104.131.51.150 2015-08-31 06:04:07\n";
-}
-
diff --git a/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
deleted file mode 100644
index 6843196..0000000
--- a/test/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
+++ /dev/null
@@ -1,1737 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-
-import org.junit.Test;
-import org.torproject.descriptor.BridgeExtraInfoDescriptor;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.ExtraInfoDescriptor;
-import org.torproject.descriptor.RelayExtraInfoDescriptor;
-
-/* Test parsing of extra-info descriptors. */
-public class ExtraInfoDescriptorImplTest {
-
- /* Helper class to build a descriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String extraInfoLine = "extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26";
- private static ExtraInfoDescriptor createWithExtraInfoLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.extraInfoLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String publishedLine = "published 2012-02-11 09:08:36";
- private static ExtraInfoDescriptor createWithPublishedLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.publishedLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String writeHistoryLine = "write-history 2012-02-11 09:03:39 "
- + "(900 s) 4713350144,4723824640,4710717440,4572675072";
- private static ExtraInfoDescriptor createWithWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.writeHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String readHistoryLine = "read-history 2012-02-11 09:03:39 "
- + "(900 s) 4707695616,4699666432,4650004480,4489718784";
- private static ExtraInfoDescriptor createWithReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.readHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqWriteHistoryLine = "dirreq-write-history "
- + "2012-02-11 09:03:39 (900 s) 81281024,64996352,60625920,"
- + "67922944";
- private static ExtraInfoDescriptor createWithDirreqWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqWriteHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqReadHistoryLine = "dirreq-read-history "
- + "2012-02-11 09:03:39 (900 s) 17074176,16235520,16005120,"
- + "16209920";
- private static ExtraInfoDescriptor createWithDirreqReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqReadHistoryLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoipDbDigestLine = null;
- private static ExtraInfoDescriptor createWithGeoipDbDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoipDbDigestLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoip6DbDigestLine = null;
- private static ExtraInfoDescriptor createWithGeoip6DbDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoip6DbDigestLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String geoipStatsLines = null;
- private static ExtraInfoDescriptor createWithGeoipStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.geoipStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String dirreqStatsLines = null;
- private static ExtraInfoDescriptor createWithDirreqStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.dirreqStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String entryStatsLines = null;
- private static ExtraInfoDescriptor createWithEntryStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.entryStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String cellStatsLines = null;
- private static ExtraInfoDescriptor createWithCellStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.cellStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String connBiDirectLine = null;
- private static ExtraInfoDescriptor createWithConnBiDirectLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.connBiDirectLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String exitStatsLines = null;
- private static ExtraInfoDescriptor createWithExitStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.exitStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String bridgeStatsLines = null;
- private static ExtraInfoDescriptor createWithBridgeStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.bridgeStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hidservStatsLines = null;
- private static ExtraInfoDescriptor createWithHidservStatsLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hidservStatsLines = lines;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String unrecognizedLine = null;
- private static ExtraInfoDescriptor createWithUnrecognizedLine(
- String line, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.unrecognizedLine = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private byte[] nonAsciiLineBytes = null;
- private static ExtraInfoDescriptor createWithNonAsciiLineBytes(
- byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.nonAsciiLineBytes = lineBytes;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private String routerSignatureLines = "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----";
- private static ExtraInfoDescriptor createWithRouterSignatureLines(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerSignatureLines = line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private String identityEd25519Lines = null,
- masterKeyEd25519Line = null, routerSigEd25519Line = null;
- private static ExtraInfoDescriptor createWithEd25519Lines(
- String identityEd25519Lines, String masterKeyEd25519Line,
- String routerSigEd25519Line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.identityEd25519Lines = identityEd25519Lines;
- db.masterKeyEd25519Line = masterKeyEd25519Line;
- db.routerSigEd25519Line = routerSigEd25519Line;
- return new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.extraInfoLine != null) {
- sb.append(this.extraInfoLine).append("\n");
- }
- if (this.identityEd25519Lines != null) {
- sb.append(this.identityEd25519Lines).append("\n");
- }
- if (this.masterKeyEd25519Line != null) {
- sb.append(this.masterKeyEd25519Line).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.writeHistoryLine != null) {
- sb.append(this.writeHistoryLine).append("\n");
- }
- if (this.readHistoryLine != null) {
- sb.append(this.readHistoryLine).append("\n");
- }
- if (this.dirreqWriteHistoryLine != null) {
- sb.append(this.dirreqWriteHistoryLine).append("\n");
- }
- if (this.dirreqReadHistoryLine != null) {
- sb.append(this.dirreqReadHistoryLine).append("\n");
- }
- if (this.geoipDbDigestLine != null) {
- sb.append(this.geoipDbDigestLine).append("\n");
- }
- if (this.geoip6DbDigestLine != null) {
- sb.append(this.geoip6DbDigestLine).append("\n");
- }
- if (this.geoipStatsLines != null) {
- sb.append(this.geoipStatsLines).append("\n");
- }
- if (this.dirreqStatsLines != null) {
- sb.append(this.dirreqStatsLines).append("\n");
- }
- if (this.entryStatsLines != null) {
- sb.append(this.entryStatsLines).append("\n");
- }
- if (this.cellStatsLines != null) {
- sb.append(this.cellStatsLines).append("\n");
- }
- if (this.connBiDirectLine != null) {
- sb.append(this.connBiDirectLine).append("\n");
- }
- if (this.exitStatsLines != null) {
- sb.append(this.exitStatsLines).append("\n");
- }
- if (this.bridgeStatsLines != null) {
- sb.append(this.bridgeStatsLines).append("\n");
- }
- if (this.hidservStatsLines != null) {
- sb.append(this.hidservStatsLines).append("\n");
- }
- if (this.unrecognizedLine != null) {
- sb.append(this.unrecognizedLine).append("\n");
- }
- if (this.nonAsciiLineBytes != null) {
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(sb.toString().getBytes());
- baos.write(this.nonAsciiLineBytes);
- baos.write("\n".getBytes());
- if (this.routerSignatureLines != null) {
- baos.write(this.routerSignatureLines.getBytes());
- }
- return baos.toByteArray();
- } catch (IOException e) {
- return null;
- }
- }
- if (this.routerSigEd25519Line != null) {
- sb.append(this.routerSigEd25519Line).append("\n");
- }
- if (this.routerSignatureLines != null) {
- sb.append(this.routerSignatureLines).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- /* Helper class to build a set of geoip-stats lines based on default
- * data and modifications requested by test methods. */
- private static class GeoipStatsBuilder {
- private String geoipStartTimeLine = "geoip-start-time 2012-02-10 "
- + "18:32:51";
- private static ExtraInfoDescriptor createWithGeoipStartTimeLine(
- String line) throws DescriptorParseException {
- GeoipStatsBuilder gsb = new GeoipStatsBuilder();
- gsb.geoipStartTimeLine = line;
- return DescriptorBuilder.createWithGeoipStatsLines(
- gsb.buildGeoipStatsLines());
- }
- private String geoipClientOriginsLine = "geoip-client-origins "
- + "de=1152,cn=896,us=712,it=504,ru=352,fr=208,gb=208,ir=200";
- private static ExtraInfoDescriptor createWithGeoipClientOriginsLine(
- String line) throws DescriptorParseException {
- GeoipStatsBuilder gsb = new GeoipStatsBuilder();
- gsb.geoipClientOriginsLine = line;
- return DescriptorBuilder.createWithGeoipStatsLines(
- gsb.buildGeoipStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithGeoipStatsLines(
- new GeoipStatsBuilder().buildGeoipStatsLines());
- }
- private String buildGeoipStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.geoipStartTimeLine != null) {
- sb.append(this.geoipStartTimeLine).append("\n");
- }
- if (this.geoipClientOriginsLine != null) {
- sb.append(this.geoipClientOriginsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of dirreq-stats lines based on default
- * data and modifications requested by test methods. */
- private static class DirreqStatsBuilder {
- private String dirreqStatsEndLine = "dirreq-stats-end 2012-02-11 "
- + "00:59:53 (86400 s)";
- private static ExtraInfoDescriptor createWithDirreqStatsEndLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqStatsEndLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3IpsLine = "dirreq-v3-ips us=1544,de=1056,"
- + "it=1032,fr=784,es=640,ru=440,br=312,gb=272,kr=224,sy=192";
- private static ExtraInfoDescriptor createWithDirreqV3IpsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3IpsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2IpsLine = "dirreq-v2-ips ";
- private static ExtraInfoDescriptor createWithDirreqV2IpsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2IpsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3ReqsLine = "dirreq-v3-reqs us=1744,de=1224,"
- + "it=1080,fr=832,es=664,ru=536,br=344,gb=296,kr=272,in=216";
- private static ExtraInfoDescriptor createWithDirreqV3ReqsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3ReqsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2ReqsLine = "dirreq-v2-reqs ";
- private static ExtraInfoDescriptor createWithDirreqV2ReqsLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2ReqsLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3RespLine = "dirreq-v3-resp ok=10848,"
- + "not-enough-sigs=8,unavailable=0,not-found=0,not-modified=0,"
- + "busy=80";
- private static ExtraInfoDescriptor createWithDirreqV3RespLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3RespLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2RespLine = "dirreq-v2-resp ok=0,unavailable=0,"
- + "not-found=1576,not-modified=0,busy=0";
- private static ExtraInfoDescriptor createWithDirreqV2RespLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2RespLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2ShareLine = "dirreq-v2-share 0.37%";
- private static ExtraInfoDescriptor createWithDirreqV2ShareLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2ShareLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3ShareLine = "dirreq-v3-share 0.37%";
- private static ExtraInfoDescriptor createWithDirreqV3ShareLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3ShareLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3DirectDlLine = "dirreq-v3-direct-dl "
- + "complete=36,timeout=4,running=0,min=7538,d1=20224,d2=28950,"
- + "q1=40969,d3=55786,d4=145813,md=199164,d6=267230,d7=480900,"
- + "q3=481049,d8=531276,d9=778086,max=15079428";
- private static ExtraInfoDescriptor createWithDirreqV3DirectDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3DirectDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2DirectDlLine = "dirreq-v2-direct-dl "
- + "complete=0,timeout=0,running=0";
- private static ExtraInfoDescriptor createWithDirreqV2DirectDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2DirectDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV3TunneledDlLine = "dirreq-v3-tunneled-dl "
- + "complete=10608,timeout=204,running=4,min=507,d1=20399,"
- + "d2=27588,q1=29292,d3=30889,d4=40624,md=59967,d6=103333,"
- + "d7=161170,q3=209415,d8=256711,d9=452503,max=23417777";
- private static ExtraInfoDescriptor createWithDirreqV3TunneledDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV3TunneledDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private String dirreqV2TunneledDlLine = "dirreq-v2-tunneled-dl "
- + "complete=0,timeout=0,running=0";
- private static ExtraInfoDescriptor createWithDirreqV2TunneledDlLine(
- String line) throws DescriptorParseException {
- DirreqStatsBuilder dsb = new DirreqStatsBuilder();
- dsb.dirreqV2TunneledDlLine = line;
- return DescriptorBuilder.createWithDirreqStatsLines(
- dsb.buildDirreqStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithDirreqStatsLines(
- new DirreqStatsBuilder().buildDirreqStatsLines());
- }
- private String buildDirreqStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.dirreqStatsEndLine != null) {
- sb.append(this.dirreqStatsEndLine).append("\n");
- }
- if (this.dirreqV3IpsLine != null) {
- sb.append(this.dirreqV3IpsLine).append("\n");
- }
- if (this.dirreqV2IpsLine != null) {
- sb.append(this.dirreqV2IpsLine).append("\n");
- }
- if (this.dirreqV3ReqsLine != null) {
- sb.append(this.dirreqV3ReqsLine).append("\n");
- }
- if (this.dirreqV2ReqsLine != null) {
- sb.append(this.dirreqV2ReqsLine).append("\n");
- }
- if (this.dirreqV3RespLine != null) {
- sb.append(this.dirreqV3RespLine).append("\n");
- }
- if (this.dirreqV2RespLine != null) {
- sb.append(this.dirreqV2RespLine).append("\n");
- }
- if (this.dirreqV2ShareLine != null) {
- sb.append(this.dirreqV2ShareLine).append("\n");
- }
- if (this.dirreqV3ShareLine != null) {
- sb.append(this.dirreqV3ShareLine).append("\n");
- }
- if (this.dirreqV3DirectDlLine != null) {
- sb.append(this.dirreqV3DirectDlLine).append("\n");
- }
- if (this.dirreqV2DirectDlLine != null) {
- sb.append(this.dirreqV2DirectDlLine).append("\n");
- }
- if (this.dirreqV3TunneledDlLine != null) {
- sb.append(this.dirreqV3TunneledDlLine).append("\n");
- }
- if (this.dirreqV2TunneledDlLine != null) {
- sb.append(this.dirreqV2TunneledDlLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of entry-stats lines based on default
- * data and modifications requested by test methods. */
- private static class EntryStatsBuilder {
- private String entryStatsEndLine = "entry-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithEntryStatsEndLine(
- String line) throws DescriptorParseException {
- EntryStatsBuilder esb = new EntryStatsBuilder();
- esb.entryStatsEndLine = line;
- return DescriptorBuilder.createWithEntryStatsLines(
- esb.buildEntryStatsLines());
- }
- private String entryIpsLine = "entry-ips ir=25368,us=15744,it=14816,"
- + "de=13256,es=8280,fr=8120,br=5176,sy=4760,ru=4504,sa=4216,"
- + "gb=3152,pl=2928,nl=2208,kr=1856,ca=1792,ua=1272,in=1192";
- private static ExtraInfoDescriptor createWithEntryIpsLine(
- String line) throws DescriptorParseException {
- EntryStatsBuilder esb = new EntryStatsBuilder();
- esb.entryIpsLine = line;
- return DescriptorBuilder.createWithEntryStatsLines(
- esb.buildEntryStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithEntryStatsLines(
- new EntryStatsBuilder().buildEntryStatsLines());
- }
- private String buildEntryStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.entryStatsEndLine != null) {
- sb.append(this.entryStatsEndLine).append("\n");
- }
- if (this.entryIpsLine != null) {
- sb.append(this.entryIpsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of cell-stats lines based on default
- * data and modifications requested by test methods. */
- private static class CellStatsBuilder {
- private String cellStatsEndLine = "cell-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithCellStatsEndLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellStatsEndLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellProcessedCellsLine = "cell-processed-cells "
- + "1441,11,6,4,2,1,1,1,1,1";
- private static ExtraInfoDescriptor createWithCellProcessedCellsLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellProcessedCellsLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellQueuedCellsLine = "cell-queued-cells "
- + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00";
- private static ExtraInfoDescriptor createWithCellQueuedCellsLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellQueuedCellsLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellTimeInQueueLine = "cell-time-in-queue "
- + "524,1,1,0,0,25,0,0,0,0";
- private static ExtraInfoDescriptor createWithCellTimeInQueueLine(
- String line) throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellTimeInQueueLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private String cellCircuitsPerDecileLine = "cell-circuits-per-decile "
- + "866";
- private static ExtraInfoDescriptor
- createWithCellCircuitsPerDecileLine(String line)
- throws DescriptorParseException {
- CellStatsBuilder csb = new CellStatsBuilder();
- csb.cellCircuitsPerDecileLine = line;
- return DescriptorBuilder.createWithCellStatsLines(
- csb.buildCellStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithCellStatsLines(
- new CellStatsBuilder().buildCellStatsLines());
- }
- private String buildCellStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.cellStatsEndLine != null) {
- sb.append(this.cellStatsEndLine).append("\n");
- }
- if (this.cellProcessedCellsLine != null) {
- sb.append(this.cellProcessedCellsLine).append("\n");
- }
- if (this.cellQueuedCellsLine != null) {
- sb.append(this.cellQueuedCellsLine).append("\n");
- }
- if (this.cellTimeInQueueLine != null) {
- sb.append(this.cellTimeInQueueLine).append("\n");
- }
- if (this.cellCircuitsPerDecileLine != null) {
- sb.append(this.cellCircuitsPerDecileLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of exit-stats lines based on default
- * data and modifications requested by test methods. */
- private static class ExitStatsBuilder {
- private String exitStatsEndLine = "exit-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithExitStatsEndLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitStatsEndLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitKibibytesWrittenLine = "exit-kibibytes-written "
- + "25=74647,80=31370,443=20577,49755=23,52563=12,52596=1111,"
- + "57528=4,60912=11,61351=6,64811=3365,other=2592";
- private static ExtraInfoDescriptor createWithExitKibibytesWrittenLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitKibibytesWrittenLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitKibibytesReadLine = "exit-kibibytes-read "
- + "25=35562,80=1254256,443=110279,49755=9396,52563=1911,"
- + "52596=648,57528=1188,60912=1427,61351=1824,64811=14,"
- + "other=3054";
- private static ExtraInfoDescriptor createWithExitKibibytesReadLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitKibibytesReadLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private String exitStreamsOpenedLine = "exit-streams-opened "
- + "25=369748,80=64212,443=151660,49755=4,52563=4,52596=4,57528=4,"
- + "60912=4,61351=4,64811=4,other=1212";
- private static ExtraInfoDescriptor createWithExitStreamsOpenedLine(
- String line) throws DescriptorParseException {
- ExitStatsBuilder esb = new ExitStatsBuilder();
- esb.exitStreamsOpenedLine = line;
- return DescriptorBuilder.createWithExitStatsLines(
- esb.buildExitStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithExitStatsLines(
- new ExitStatsBuilder().buildExitStatsLines());
- }
- private String buildExitStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.exitStatsEndLine != null) {
- sb.append(this.exitStatsEndLine).append("\n");
- }
- if (this.exitKibibytesWrittenLine != null) {
- sb.append(this.exitKibibytesWrittenLine).append("\n");
- }
- if (this.exitKibibytesReadLine != null) {
- sb.append(this.exitKibibytesReadLine).append("\n");
- }
- if (this.exitStreamsOpenedLine != null) {
- sb.append(this.exitStreamsOpenedLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of bridge-stats lines based on default
- * data and modifications requested by test methods. */
- private static class BridgeStatsBuilder {
- private String bridgeStatsEndLine = "bridge-stats-end 2012-02-11 "
- + "01:59:39 (86400 s)";
- private static ExtraInfoDescriptor createWithBridgeStatsEndLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeStatsEndLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpsLine = "bridge-ips ir=24,sy=16,??=8,cn=8,"
- + "de=8,es=8,fr=8,gb=8,in=8,jp=8,kz=8,nl=8,ua=8,us=8,vn=8,za=8";
- private static ExtraInfoDescriptor createWithBridgeIpsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpVersionsLine = "bridge-ip-versions v4=8,v6=16";
- private static ExtraInfoDescriptor createWithBridgeIpVersionsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpVersionsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private String bridgeIpTransportsLine = "bridge-ip-transports "
- + "<OR>=8,obfs2=792,obfs3=1728";
- private static ExtraInfoDescriptor createWithBridgeIpTransportsLine(
- String line) throws DescriptorParseException {
- BridgeStatsBuilder bsb = new BridgeStatsBuilder();
- bsb.bridgeIpTransportsLine = line;
- return DescriptorBuilder.createWithBridgeStatsLines(
- bsb.buildBridgeStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithBridgeStatsLines(
- new BridgeStatsBuilder().buildBridgeStatsLines());
- }
- private String buildBridgeStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.bridgeStatsEndLine != null) {
- sb.append(this.bridgeStatsEndLine).append("\n");
- }
- if (this.bridgeIpsLine != null) {
- sb.append(this.bridgeIpsLine).append("\n");
- }
- if (this.bridgeIpVersionsLine != null) {
- sb.append(this.bridgeIpVersionsLine).append("\n");
- }
- if (this.bridgeIpTransportsLine != null) {
- sb.append(this.bridgeIpTransportsLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- /* Helper class to build a set of hidserv-stats lines based on default
- * data and modifications requested by test methods. */
- private static class HidservStatsBuilder {
- private String hidservStatsEndLine = "hidserv-stats-end 2015-12-03 "
- + "14:26:56 (86400 s)";
- private static ExtraInfoDescriptor createWithHidservStatsEndLine(
- String line) throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservStatsEndLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private String hidservRendRelayedCellsLine =
- "hidserv-rend-relayed-cells 36474281 delta_f=2048 epsilon=0.30 "
- + "bin_size=1024";
- private static ExtraInfoDescriptor
- createWithHidservRendRelayedCellsLine(String line)
- throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservRendRelayedCellsLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private String hidservDirOnionsSeenLine = "hidserv-dir-onions-seen "
- + "-3 delta_f=8 epsilon=0.30 bin_size=8";
- private static ExtraInfoDescriptor createWithHidservDirOnionsSeenLine(
- String line) throws DescriptorParseException {
- HidservStatsBuilder hsb = new HidservStatsBuilder();
- hsb.hidservDirOnionsSeenLine = line;
- return DescriptorBuilder.createWithHidservStatsLines(
- hsb.buildHidservStatsLines());
- }
- private static ExtraInfoDescriptor createWithDefaultLines()
- throws DescriptorParseException {
- return DescriptorBuilder.createWithHidservStatsLines(
- new HidservStatsBuilder().buildHidservStatsLines());
- }
- private String buildHidservStatsLines() {
- StringBuilder sb = new StringBuilder();
- if (this.hidservStatsEndLine != null) {
- sb.append(this.hidservStatsEndLine).append("\n");
- }
- if (this.hidservRendRelayedCellsLine != null) {
- sb.append(this.hidservRendRelayedCellsLine).append("\n");
- }
- if (this.hidservDirOnionsSeenLine != null) {
- sb.append(this.hidservDirOnionsSeenLine).append("\n");
- }
- String lines = sb.toString();
- if (lines.endsWith("\n")) {
- lines = lines.substring(0, lines.length() - 1);
- }
- return lines;
- }
- }
-
- @Test()
- public void testSampleDescriptor() throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- ExtraInfoDescriptor descriptor =
- new RelayExtraInfoDescriptorImpl(db.buildDescriptor(), true);
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- assertNotNull(descriptor.getWriteHistory());
- assertEquals(1328951019000L, descriptor.getWriteHistory().
- getHistoryEndMillis());
- assertEquals(900L, descriptor.getWriteHistory().getIntervalLength());
- assertEquals(4572675072L, (long) descriptor.getWriteHistory().
- getBandwidthValues().get(1328951019000L));
- assertNotNull(descriptor.getReadHistory());
- assertNotNull(descriptor.getDirreqWriteHistory());
- assertNotNull(descriptor.getDirreqReadHistory());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoLineMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine(null);
- }
-
- @Test()
- public void testExtraInfoOpt() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- }
-
- @Test()
- public void testExtraInfoNicknameTwoSpaces()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoLine("opt extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- assertEquals("chaoscomputerclub5", descriptor.getNickname());
- assertEquals("A9C039A5FD02FCA06303DCFAABE25C5912C63B26",
- descriptor.getFingerprint());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoLineNotFirst()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8\n"
- + "extra-info chaoscomputerclub5 "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameInvalidChar() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub% A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5ReallyLongNickname "
- + "A9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintG() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 G9C039A5FD02FCA06303DCFAABE25C5912C63B26");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C6");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoLine("extra-info "
- + "chaoscomputerclub5 A9C039A5FD02FCA06303DCFAABE25C5912C63B26"
- + "A9C0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine(null);
- }
-
- @Test()
- public void testPublishedOpt() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-02-11 09:08:36");
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- }
-
- @Test()
- public void testPublishedMillis() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-02-11 09:08:36.123");
- assertEquals(1328951316000L, descriptor.getPublishedMillis());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNegativeBytes()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-02-11 09:03:39 (900 s) "
- + "-4713350144,-4723824640,-4710717440,-4572675072");
- }
-
- @Test()
- public void testReadHistoryTabInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (900\ts) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test()
- public void testReadHistoryTabIntervalBytes()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (900 s)\t"
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testReadHistoryNegativeInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (-900 s) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test()
- public void testReadHistoryNonStandardInterval()
- throws DescriptorParseException {
- DescriptorBuilder.createWithReadHistoryLine("read-history "
- + "2012-02-11 09:03:39 (1800 s) "
- + "4707695616,4699666432,4650004480,4489718784");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqWriteHistoryMissingBytesBegin()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqWriteHistoryLine(
- "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
- + ",64996352,60625920,67922944");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqWriteHistoryMissingBytesMiddle()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqWriteHistoryLine(
- "dirreq-write-history 2012-02-11 09:03:39 (900 s) "
- + "81281024,,60625920,67922944");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqReadHistoryMissingBytesEnd()
- throws DescriptorParseException {
- DescriptorBuilder.createWithDirreqReadHistoryLine(
- "dirreq-read-history 2012-02-11 09:03:39 (900 s) "
- + "17074176,16235520,16005120,");
- }
-
- @Test()
- public void testGeoipDbDigestValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithGeoipDbDigestLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
- descriptor.getGeoipDbDigest());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestTooShort()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301C");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestIllegalChars()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest "
- + "&%6A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipDbDigestMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithGeoipDbDigestLine("geoip-db-digest");
- }
-
- @Test()
- public void testGeoip6DbDigestValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithGeoip6DbDigestLine("geoip6-db-digest "
- + "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8");
- assertEquals("916A3CA8B7DF61473D5AE5B21711F35F301CE9E8",
- descriptor.getGeoip6DbDigest());
- }
-
- @Test()
- public void testGeoipStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = GeoipStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328898771000L, descriptor.getGeoipStartTimeMillis());
- SortedMap<String, Integer> ips = descriptor.getGeoipClientOrigins();
- assertNotNull(ips);
- assertEquals(1152, ips.get("de").intValue());
- assertEquals(896, ips.get("cn").intValue());
- assertFalse(ips.containsKey("pl"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipStartTimeDateOnly()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipStartTimeLine("geoip-start-time "
- + "2012-02-10");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsDash()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de-1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsZero()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=zero,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsNone()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=none,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsOther()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,other=200");
- }
-
- @Test()
- public void testGeoipClientOriginsQuestionMarks()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,??=200");
- }
-
- @Test()
- public void testGeoipClientOriginsCapital()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins DE=1152,CN=896,US=712,IT=504,RU=352,FR=208,"
- + "GB=208,IR=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingBegin()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins ,cn=896,us=712,it=504,ru=352,fr=208,gb=208,"
- + "ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingMiddle()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,,us=712,it=504,ru=352,fr=208,"
- + "gb=208,ir=200");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testGeoipClientOriginsMissingEnd()
- throws DescriptorParseException {
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,cn=896,us=712,it=504,ru=352,fr=208,"
- + "gb=208,");
- }
-
- @Test()
- public void testGeoipClientOriginsDuplicate()
- throws DescriptorParseException {
- /* dir-spec.txt doesn't say anything about duplicate country codes, so
- * this line is valid, even though it leads to a somewhat undefined
- * parse result. */
- GeoipStatsBuilder.createWithGeoipClientOriginsLine(
- "geoip-client-origins de=1152,de=952,cn=896,us=712,it=504,"
- + "ru=352,fr=208,gb=208,ir=200");
- }
-
- @Test()
- public void testDirreqStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DirreqStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328921993000L, descriptor.getDirreqStatsEndMillis());
- assertEquals(86400L, descriptor.getDirreqStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getDirreqV3Ips();
- assertNotNull(ips);
- assertEquals(1544, ips.get("us").intValue());
- assertFalse(ips.containsKey("no"));
- assertTrue(descriptor.getDirreqV2Ips().isEmpty());
- SortedMap<String, Integer> reqs = descriptor.getDirreqV3Reqs();
- assertEquals(832, reqs.get("fr").intValue());
- assertTrue(descriptor.getDirreqV2Reqs().isEmpty());
- SortedMap<String, Integer> resp = descriptor.getDirreqV3Resp();
- assertEquals(10848, resp.get("ok").intValue());
- assertEquals(8, resp.get("not-enough-sigs").intValue());
- resp = descriptor.getDirreqV2Resp();
- assertEquals(1576, resp.get("not-found").intValue());
- assertEquals(0.37, descriptor.getDirreqV2Share(), 0.0001);
- assertEquals(0.37, descriptor.getDirreqV3Share(), 0.0001);
- SortedMap<String, Integer> dl = descriptor.getDirreqV3DirectDl();
- assertEquals(36, dl.get("complete").intValue());
- dl = descriptor.getDirreqV2DirectDl();
- assertEquals(0, dl.get("timeout").intValue());
- dl = descriptor.getDirreqV3TunneledDl();
- assertEquals(10608, dl.get("complete").intValue());
- dl = descriptor.getDirreqV2TunneledDl();
- assertEquals(0, dl.get("complete").intValue());
- }
-
- @Test()
- public void testDirreqStatsIntervalTwoDays()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqStatsEndLine("dirreq-stats-end "
- + "2012-02-11 00:59:53 (172800 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3IpsThreeLetterCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3IpsLine("dirreq-v3-ips "
- + "usa=1544");
- }
-
- @Test()
- public void testDirreqV2IpsDigitCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2IpsLine("dirreq-v2-ips 00=8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3ReqsOneLetterCountry()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3ReqsLine("dirreq-v3-reqs "
- + "u=1744");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2ReqsNoNumber()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2ReqsLine("dirreq-v2-reqs us=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3RespTwoEqualSigns()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3RespLine("dirreq-v3-resp "
- + "ok==10848");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2RespNull()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2RespLine("dirreq-v2-resp "
- + "ok=null");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV2ShareComma()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2ShareLine("dirreq-v2-share "
- + "0,37%");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3ShareNoPercent()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3ShareLine("dirreq-v3-share "
- + "0.37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3DirectDlSpace()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3DirectDlLine(
- "dirreq-v3-direct-dl complete 36");
- }
-
- @Test()
- public void testDirreqV2DirectDlNegative()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2DirectDlLine(
- "dirreq-v2-direct-dl complete=-8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3TunneledDlTooLarge()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV3TunneledDlLine(
- "dirreq-v3-tunneled-dl complete=2147483648");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirreqV3TunneledDlDouble()
- throws DescriptorParseException {
- DirreqStatsBuilder.createWithDirreqV2TunneledDlLine(
- "dirreq-v2-tunneled-dl complete=0.001");
- }
-
- @Test()
- public void testEntryStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = EntryStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getEntryStatsEndMillis());
- assertEquals(86400L, descriptor.getEntryStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getEntryIps();
- assertNotNull(ips);
- assertEquals(25368, ips.get("ir").intValue());
- assertFalse(ips.containsKey("no"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEntryStatsEndNoDate() throws DescriptorParseException {
- EntryStatsBuilder.createWithEntryStatsEndLine("entry-stats-end "
- + "01:59:39 (86400 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEntryStatsIpsSemicolon()
- throws DescriptorParseException {
- EntryStatsBuilder.createWithEntryIpsLine("entry-ips "
- + "ir=25368;us=15744");
- }
-
- @Test()
- public void testCellStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = CellStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getCellStatsEndMillis());
- assertEquals(86400L, descriptor.getCellStatsIntervalLength());
- List<Integer> processedCells = descriptor.getCellProcessedCells();
- assertEquals(10, processedCells.size());
- assertEquals(1441, processedCells.get(0).intValue());
- assertEquals(11, processedCells.get(1).intValue());
- List<Double> queuedCells = descriptor.getCellQueuedCells();
- assertEquals(10, queuedCells.size());
- assertEquals(3.29, queuedCells.get(0), 0.001);
- assertEquals(0.00, queuedCells.get(1), 0.001);
- List<Integer> timeInQueue = descriptor.getCellTimeInQueue();
- assertEquals(10, timeInQueue.size());
- assertEquals(524, timeInQueue.get(0).intValue());
- assertEquals(1, timeInQueue.get(1).intValue());
- assertEquals(866, descriptor.getCellCircuitsPerDecile());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellStatsEndNoSeconds()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellStatsEndLine("cell-stats-end "
- + "2012-02-11 01:59:39 (86400)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellProcessedCellsNineComma()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellProcessedCellsLine(
- "cell-processed-cells 1441,11,6,4,2,1,1,1,1,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellProcessedCellsEleven()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellQueuedCellsLine("cell-queued-cells "
- + "3.29,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellTimeInQueueDouble()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellTimeInQueueLine("cell-time-in-queue "
- + "524.0,1.0,1.0,0.0,0.0,25.0,0.0,0.0,0.0,0.0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCellCircuitsPerDecileNegative()
- throws DescriptorParseException {
- CellStatsBuilder.createWithCellCircuitsPerDecileLine(
- "cell-circuits-per-decile -866");
- }
-
- @Test()
- public void testConnBiDirectValid()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithConnBiDirectLine("conn-bi-direct 2012-02-11 01:59:39 "
- + "(86400 s) 42173,1591,1310,1744");
- assertEquals(1328925579000L,
- descriptor.getConnBiDirectStatsEndMillis());
- assertEquals(86400L, descriptor.getConnBiDirectStatsIntervalLength());
- assertEquals(42173, descriptor.getConnBiDirectBelow());
- assertEquals(1591, descriptor.getConnBiDirectRead());
- assertEquals(1310, descriptor.getConnBiDirectWrite());
- assertEquals(1744, descriptor.getConnBiDirectBoth());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConnBiDirectStatsFive()
- throws DescriptorParseException {
- DescriptorBuilder.createWithConnBiDirectLine("conn-bi-direct "
- + "2012-02-11 01:59:39 (86400 s) 42173,1591,1310,1744,42");
- }
-
- @Test()
- public void testExitStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = ExitStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getExitStatsEndMillis());
- assertEquals(86400L, descriptor.getExitStatsIntervalLength());
- String[] ports = new String[] { "25", "80", "443", "49755",
- "52563", "52596", "57528", "60912", "61351", "64811", "other" };
- int[] writtenValues = new int[] { 74647, 31370, 20577, 23, 12, 1111,
- 4, 11, 6, 3365, 2592 };
- int i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitKibibytesWritten().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(writtenValues[i++], e.getValue().intValue());
- }
- int[] readValues = new int[] { 35562, 1254256, 110279, 9396, 1911,
- 648, 1188, 1427, 1824, 14, 3054 };
- i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitKibibytesRead().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(readValues[i++], e.getValue().intValue());
- }
- int[] streamsValues = new int[] { 369748, 64212, 151660, 4, 4, 4, 4,
- 4, 4, 4, 1212 };
- i = 0;
- for (Map.Entry<String, Long> e :
- descriptor.getExitStreamsOpened().entrySet()) {
- assertEquals(ports[i], e.getKey());
- assertEquals(streamsValues[i++], e.getValue().intValue());
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsEndNoSeconds()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitStatsEndLine("exit-stats-end "
- + "2012-02-11 01:59 (86400 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsWrittenNegativePort()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesWrittenLine(
- "exit-kibibytes-written -25=74647");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsWrittenUnknown()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesWrittenLine(
- "exit-kibibytes-written unknown=74647");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitStatsReadNegativeBytes()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesReadLine(
- "exit-kibibytes-read 25=-35562");
- }
-
- @Test()
- public void testExitStatsReadTooLarge()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitKibibytesReadLine(
- "exit-kibibytes-read other=2282907805");
- }
-
- @Test()
- public void testExitStatsStreamsTooLarge()
- throws DescriptorParseException {
- ExitStatsBuilder.createWithExitStreamsOpenedLine(
- "exit-streams-opened 25=2147483648");
- }
-
- @Test()
- public void testBridgeStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = BridgeStatsBuilder.
- createWithDefaultLines();
- assertEquals(1328925579000L, descriptor.getBridgeStatsEndMillis());
- assertEquals(86400L, descriptor.getBridgeStatsIntervalLength());
- SortedMap<String, Integer> ips = descriptor.getBridgeIps();
- assertNotNull(ips);
- assertEquals(24, ips.get("ir").intValue());
- assertEquals(16, ips.get("sy").intValue());
- assertFalse(ips.containsKey("no"));
- SortedMap<String, Integer> ver = descriptor.getBridgeIpVersions();
- assertNotNull(ver);
- assertEquals(8, ver.get("v4").intValue());
- assertEquals(16, ver.get("v6").intValue());
- assertFalse(ver.containsKey("v8"));
- SortedMap<String, Integer> trans = descriptor.getBridgeIpTransports();
- assertNotNull(trans);
- assertEquals(8, trans.get("<OR>").intValue());
- assertEquals(792, trans.get("obfs2").intValue());
- assertEquals(1728, trans.get("obfs3").intValue());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeStatsEndIntervalZero()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeStatsEndLine("bridge-stats-end "
- + "2012-02-11 01:59:39 (0 s)");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpsLine("bridge-ips ir=24.5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpsNonAsciiKeyword()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
- 0x14, (byte) 0xfe, 0x18, // non-ascii chars
- 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2d, // "bridge-"
- 0x69, 0x70, 0x73 }, false); // "ips" (no newline)
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpVersionsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpVersionsLine(
- "bridge-ip-versions v4=24.5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBridgeIpTransportsDouble()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpTransportsLine(
- "bridge-ip-transports obfs2=24.5");
- }
-
- @Test()
- public void testBridgeIpTransportsUnderscore()
- throws DescriptorParseException {
- BridgeStatsBuilder.createWithBridgeIpTransportsLine(
- "bridge-ip-transports meek=32,obfs3_websocket=8,websocket=64");
- }
-
- @Test()
- public void testHidservStatsValid() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor = HidservStatsBuilder.
- createWithDefaultLines();
- assertEquals(1449152816000L, descriptor.getHidservStatsEndMillis());
- assertEquals(86400L, descriptor.getHidservStatsIntervalLength());
- assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
- 0.0001);
- Map<String, Double> params =
- descriptor.getHidservRendRelayedCellsParameters();
- assertTrue(params.containsKey("delta_f"));
- assertEquals(2048.0, params.remove("delta_f"), 0.0001);
- assertTrue(params.containsKey("epsilon"));
- assertEquals(0.3, params.remove("epsilon"), 0.0001);
- assertTrue(params.containsKey("bin_size"));
- assertEquals(1024.0, params.remove("bin_size"), 0.0001);
- assertTrue(params.isEmpty());
- assertEquals(-3.0, descriptor.getHidservDirOnionsSeen(), 0.0001);
- params = descriptor.getHidservDirOnionsSeenParameters();
- assertTrue(params.containsKey("delta_f"));
- assertEquals(8.0, params.remove("delta_f"), 0.0001);
- assertTrue(params.containsKey("epsilon"));
- assertEquals(0.3, params.remove("epsilon"), 0.0001);
- assertTrue(params.containsKey("bin_size"));
- assertEquals(8.0, params.remove("bin_size"), 0.0001);
- assertTrue(params.isEmpty());
- }
-
- @Test()
- public void testHidservStatsEndLineMissing()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- HidservStatsBuilder.createWithHidservStatsEndLine(null);
- assertEquals(-1L, descriptor.getHidservStatsEndMillis());
- assertEquals(-1L, descriptor.getHidservStatsIntervalLength());
- }
-
- @Test()
- public void testHidservRendRelayedCellsNoParams()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- HidservStatsBuilder.createWithHidservRendRelayedCellsLine(
- "hidserv-rend-relayed-cells 36474281");
- assertEquals(36474281.0, descriptor.getHidservRendRelayedCells(),
- 0.0001);
- assertTrue(
- descriptor.getHidservRendRelayedCellsParameters().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHidservDirOnionsSeenCommaSeparatedParams()
- throws DescriptorParseException {
- HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
- "hidserv-dir-onions-seen -3 delta_f=8,epsilon=0.30,bin_size=8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHidservDirOnionsSeenNoDoubleParams()
- throws DescriptorParseException {
- HidservStatsBuilder.createWithHidservDirOnionsSeenLine(
- "hidserv-dir-onions-seen -3 delta_f=A epsilon=B bin_size=C");
- }
-
- @Test()
- public void testRouterSignatureOpt()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("opt "
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "crypto lines are ignored anyway\n"
- + "-----END SIGNATURE-----");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterSignatureNotLastLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----\npublished 2012-02-11 09:08:36");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ExtraInfoDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- private static final String IDENTITY_ED25519_LINES =
- "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
- + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
- + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
- + "\n"
- + "-----END ED25519 CERT-----";
-
- private static final String MASTER_KEY_ED25519_LINE =
- "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
-
- private static final String ROUTER_SIG_ED25519_LINE =
- "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
- + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
-
- @Test()
- public void testEd25519() throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- assertEquals(IDENTITY_ED25519_LINES.substring(
- IDENTITY_ED25519_LINES.indexOf("\n") + 1),
- descriptor.getIdentityEd25519());
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- assertEquals(ROUTER_SIG_ED25519_LINE.substring(
- ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getRouterSignatureEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityMasterKeyMismatch()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519IdentityMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(null,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
- + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityEmptyCrypto()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519MasterKeyMissing()
- throws DescriptorParseException {
- ExtraInfoDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- null, ROUTER_SIG_ED25519_LINE);
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519MasterKeyDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519RouterSigMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519RouterSigDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
- + ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testExtraInfoDigestSha256Relay()
- throws DescriptorParseException {
- byte[] descriptorBytes = ("extra-info Unnamed "
- + "EA5B335055D2F03013FF030381F02B1C631EC723\n"
- + "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiZRAenzZorGtx6xapoEeaqcLLOk3uWwJXTvOVLluSXXbRSZAQAgBADLN5"
- + "wp\nCEOrRbshSbj1NDAUgc6cxU65M/Vx1x+b5+EXbkQZ5uiyB4pphVF5kPPT1P"
- + "SleYqM\n8j+tlKh2i6+Xr0xScSPpmtG00/D0MoRlT7ZdaaaT5iw1DWDQCZ8BHG"
- + "lAZwU=\n"
- + "-----END ED25519 CERT-----\n"
- + "published 2015-12-01 04:38:12\n"
- + "write-history 2015-12-01 01:40:37 (14400 s) 88704000,60825600,"
- + "61747200,76953600,61516800,59443200\n"
- + "read-history 2015-12-01 01:40:37 (14400 s) 87321600,59443200,"
- + "59904000,74880000,60364800,58060800\n"
- + "router-sig-ed25519 c6eUeJs/SVjun3JhmjByEeWdRDyunSMAnGVhx71JiRj"
- + "YzR8x5IcPebylG7m10wiolFxinvw78UhrrGo9Sq5ZBw\n"
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "oC2qFHCDOKSRoIPR86jdRxEYia390Z4d8fT0yr/1mg4RQ7lHmxlzFT6QxCswdX"
- + "Ry\nvGNGR0wARySgyE+YKKWYn/Hp547JhhWd9Oc7BuFMY0XMvl/HOo+B9VjW+l"
- + "nv6UBE\niqxx3C3Iw0ymohvOenyCUa/7TmsT7eVotDO57uIoGEc=\n"
- + "-----END SIGNATURE-----\n"
- + "").getBytes();
- RelayExtraInfoDescriptor descriptor =
- new RelayExtraInfoDescriptorImpl(descriptorBytes, true);
- assertEquals("Pt1BtzfRwhYqGCDo8jjchS8nJP3ovrDyHGn+dqPbMgw",
- descriptor.getExtraInfoDigestSha256());
- }
-
- @Test()
- public void testExtraInfoDigestSha256Bridge()
- throws DescriptorParseException {
- byte[] descriptorBytes = ("extra-info idideditheconfig "
- + "DC28749EC9E26E61DE492E46CD830379E9931B09\n"
- + "master-key-ed25519 "
- + "38FzmOIE6Mm85Ytx0MhFM6X9EuxWRUgb6HjyMGuO2AU\n"
- + "published 2015-12-03 13:23:19\n"
- + "write-history 2015-12-03 09:59:32 (14400 s) 53913600,52992000,"
- + "53222400,53222400,53452800,53222400\n"
- + "read-history 2015-12-03 09:59:32 (14400 s) 61056000,60364800,"
- + "60364800,60134400,60595200,60364800\n"
- + "geoip-db-digest 5BF366AD4A0572D82A1A0F6628AF8EF7725E3AB9\n"
- + "geoip6-db-digest 212DE17D5A368DCAFA19B95F168BFFA101145A93\n"
- + "router-digest-sha256 "
- + "TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4\n"
- + "router-digest 00B98F076B586272C3172B7F3DA29ADEE75F2ED8\n").getBytes();
- BridgeExtraInfoDescriptor descriptor =
- new BridgeExtraInfoDescriptorImpl(descriptorBytes, true);
- assertEquals("TvrqpjI7OmCtwGwair/NHUxg5ROVVQYz6/EDyXsDHR4",
- descriptor.getExtraInfoDigestSha256());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java b/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
deleted file mode 100644
index abb51db..0000000
--- a/test/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.torproject.descriptor.impl;
-
-import org.junit.Test;
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.Microdescriptor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-public class MicrodescriptorImplTest {
-
- /* Helper class to build a microdescriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String onionKeyLines = "onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALNZ4pNsHHkl7a+kFWbBmPHNAepjjvuhjTr1TaMB3UKuCRaXJmS2Qr"
- + "CW\nkTmINqdQUccwb3ghb7EBZfDtCUvjcwMSEsRRTVIZqVQsYj6m3n1CegOc4o"
- + "UutXaZ\nfkyty5XOgV4Qucx9wokzTMCHlO0V0x9y0FwFsK5Nb6ugqfQLLQ6XAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static Microdescriptor createWithDefaultLines()
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- return new MicrodescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyLine =
- "ntor-onion-key PXLa7IGE+TzPDMsM5j9rFnDa37rd6kfZa5QuzqqJukw=";
- private String idLine = "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8";
- private static Microdescriptor createWithIdLine(String line)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.idLine = line;
- return new MicrodescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.onionKeyLines != null) {
- sb.append(this.onionKeyLines).append("\n");
- }
- if (this.ntorOnionKeyLine != null) {
- sb.append(this.ntorOnionKeyLine).append("\n");
- }
- if (this.idLine != null) {
- sb.append(this.idLine).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- @Test()
- public void testDefaults() throws DescriptorParseException {
- DescriptorBuilder.createWithDefaultLines();
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024TooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id rsa1024 AAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024TooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id ed25519 AAAAAAAAAAAAAAAAAAAAAA"
- + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa512() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine("id rsa512 "
- + "bvegfGxp8k7T9QFpjPTrPaJTa/8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdEd25519Duplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithIdLine(
- "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8\n"
- + "id rsa1024 bvegfGxp8k7T9QFpjPTrPaJTa/8");
- }
-}
diff --git a/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
deleted file mode 100644
index d864337..0000000
--- a/test/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
+++ /dev/null
@@ -1,1272 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-
-import org.junit.Test;
-import org.torproject.descriptor.NetworkStatusEntry;
-import org.torproject.descriptor.RelayNetworkStatusConsensus;
-
-/* TODO Add test cases for all lines starting with "opt ". */
-
-/* Test parsing of network status consensuses. The main focus is on
- * making sure that the parser is as robust as possible and doesn't break,
- * no matter what gets fed into it. A secondary focus is to ensure that
- * a parsed consensus is fully compatible to dir-spec.txt. */
-public class RelayNetworkStatusConsensusImplTest {
-
- /* Helper class to build a directory source based on default data and
- * modifications requested by test methods. */
- private static class DirSourceBuilder {
- private static RelayNetworkStatusConsensus
- createWithDirSource(String dirSourceString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.dirSources.add(dirSourceString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String nickname = "gabelmoo";
- private static RelayNetworkStatusConsensus
- createWithNickname(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.nickname = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
- private static RelayNetworkStatusConsensus
- createWithIdentity(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.identity = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String hostName = "212.112.245.170";
- private static RelayNetworkStatusConsensus
- createWithHostName(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.hostName = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String address = "212.112.245.170";
- private static RelayNetworkStatusConsensus
- createWithAddress(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.address = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String dirPort = "80";
- private static RelayNetworkStatusConsensus
- createWithDirPort(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.dirPort = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String orPort = "443";
- private static RelayNetworkStatusConsensus
- createWithOrPort(String string)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.orPort = string;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String contactLine = "contact 4096R/C5AA446D Sebastian Hahn "
- + "<tor(a)sebastianhahn.net>";
- private static RelayNetworkStatusConsensus
- createWithContactLine(String line)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.contactLine = line;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String voteDigestLine =
- "vote-digest 0F398A5834D2C139E1D92310B09F814F243354D1";
- private static RelayNetworkStatusConsensus
- createWithVoteDigestLine(String line)
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.voteDigestLine = line;
- return createWithDirSource(dsb.buildDirSource());
- }
- private String buildDirSource() {
- StringBuilder sb = new StringBuilder();
- String dirSourceLine = "dir-source " + this.nickname + " "
- + this.identity + " " + this.hostName + " " + this.address + " "
- + this.dirPort + " " + this.orPort;
- sb.append(dirSourceLine).append("\n");
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.voteDigestLine != null) {
- sb.append(this.voteDigestLine).append("\n");
- }
- String dirSourceWithTrailingNewLine = sb.toString();
- String dirSource = dirSourceWithTrailingNewLine.substring(0,
- dirSourceWithTrailingNewLine.length() - 1);
- return dirSource;
- }
- }
-
- /* Helper class to build a status entry based on default data and
- * modifications requested by test methods. */
- private static class StatusEntryBuilder {
- private static RelayNetworkStatusConsensus
- createWithStatusEntry(String statusEntryString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(statusEntryString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String nickname = "right2privassy3";
- private static RelayNetworkStatusConsensus
- createWithNickname(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.nickname = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String fingerprintBase64 = "ADQ6gCT3DiFHKPDFr3rODBUI8HM";
- private static RelayNetworkStatusConsensus
- createWithFingerprintBase64(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.fingerprintBase64 = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String descriptorBase64 = "Yiti+nayuT2Efe2X1+M4nslwVuU";
- private static RelayNetworkStatusConsensus
- createWithDescriptorBase64(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.descriptorBase64 = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String publishedString = "2011-11-29 21:34:27";
- private static RelayNetworkStatusConsensus
- createWithPublishedString(String string)
- throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.publishedString = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String address = "50.63.8.215";
- private static RelayNetworkStatusConsensus
- createWithAddress(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.address = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String orPort = "9023";
- private static RelayNetworkStatusConsensus
- createWithOrPort(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.orPort = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String dirPort = "0";
- private static RelayNetworkStatusConsensus
- createWithDirPort(String string) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.dirPort = string;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String sLine = "s Exit Fast Named Running Stable Valid";
- private static RelayNetworkStatusConsensus
- createWithSLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.sLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String vLine = "v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)";
- private static RelayNetworkStatusConsensus
- createWithVLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.vLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String wLine = "w Bandwidth=1";
- private static RelayNetworkStatusConsensus
- createWithWLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.wLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String pLine = "p accept 80,1194,1220,1293";
- private static RelayNetworkStatusConsensus
- createWithPLine(String line) throws DescriptorParseException {
- StatusEntryBuilder seb = new StatusEntryBuilder();
- seb.pLine = line;
- return createWithStatusEntry(seb.buildStatusEntry());
- }
- private String buildStatusEntry() {
- StringBuilder sb = new StringBuilder();
- String rLine = "r " + nickname + " " + fingerprintBase64 + " "
- + descriptorBase64 + " " + publishedString + " " + address + " "
- + orPort + " " + dirPort;
- sb.append(rLine).append("\n");
- if (this.sLine != null) {
- sb.append(this.sLine).append("\n");
- }
- if (this.vLine != null) {
- sb.append(this.vLine).append("\n");
- }
- if (this.wLine != null) {
- sb.append(this.wLine).append("\n");
- }
- if (this.pLine != null) {
- sb.append(this.pLine).append("\n");
- }
- String statusEntryWithTrailingNewLine = sb.toString();
- String statusEntry = statusEntryWithTrailingNewLine.substring(0,
- statusEntryWithTrailingNewLine.length() - 1);
- return statusEntry;
- }
- }
-
- /* Helper class to build a directory signature based on default data and
- * modifications requested by test methods. */
- private static class DirectorySignatureBuilder {
- private static RelayNetworkStatusConsensus
- createWithDirectorySignature(String directorySignatureString)
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.addDirectorySignature(directorySignatureString);
- return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(),
- true);
- }
- private String identity = "ED03BB616EB2F60BEC80151114BB25CEF515B226";
- private static RelayNetworkStatusConsensus
- createWithIdentity(String string)
- throws DescriptorParseException {
- DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
- dsb.identity = string;
- return createWithDirectorySignature(dsb.buildDirectorySignature());
- }
- private String signingKey =
- "845CF1D0B370CA443A8579D18E7987E7E532F639";
- private static RelayNetworkStatusConsensus
- createWithSigningKey(String string)
- throws DescriptorParseException {
- DirectorySignatureBuilder dsb = new DirectorySignatureBuilder();
- dsb.signingKey = string;
- return createWithDirectorySignature(dsb.buildDirectorySignature());
- }
- private String buildDirectorySignature() {
- String directorySignature = "directory-signature " + identity + " "
- + signingKey + "\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "gE64+/4BH43v1+7jS9FK1tu2+94at8xhVSPn4O/PpOx7b0Yb+S1hac1QHAiS"
- + "Ll+k\n"
- + "6OiANKzhj54WHSrUswBPrOzjmKj0OhGXSAe5nHZUFX9a1MDQLDCoZBj536X9"
- + "P3JG\n"
- + "z89A+wrsN17I5490y66AEvws54BYZMbgRfp8HXn/0Ss=\n"
- + "-----END SIGNATURE-----";
- return directorySignature;
- }
- }
-
- @Test()
- public void testSampleConsensus() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertEquals(3, consensus.getNetworkStatusVersion());
- assertEquals(11, consensus.getConsensusMethod());
- assertEquals(1322643600000L, consensus.getValidAfterMillis());
- assertEquals(1322647200000L, consensus.getFreshUntilMillis());
- assertEquals(1322654400000L, consensus.getValidUntilMillis());
- assertEquals(300L, consensus.getVoteSeconds());
- assertEquals(300L, consensus.getDistSeconds());
- assertTrue(consensus.getRecommendedClientVersions().contains(
- "0.2.3.8-alpha"));
- assertTrue(consensus.getRecommendedServerVersions().contains(
- "0.2.3.8-alpha"));
- assertTrue(consensus.getKnownFlags().contains("Running"));
- assertEquals(30000, (int) consensus.getConsensusParams().get(
- "CircuitPriorityHalflifeMsec"));
- assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
- "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getHostname());
- assertEquals("86.59.21.38", consensus.getDirSourceEntries().get(
- "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4").getIp());
- assertTrue(consensus.containsStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894"));
- assertEquals("188.177.149.216", consensus.getStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894").getAddress());
- for (DirectorySignature signature : consensus.getSignatures()) {
- if ("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4".equals(
- signature.getIdentity())) {
- assertEquals("3509BA5A624403A905C74DA5C8A0CEC9E0D3AF86",
- signature.getSigningKeyDigest());
- }
- }
- assertEquals(285, (int) consensus.getBandwidthWeights().get("Wbd"));
- assertTrue(consensus.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLineSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n ");
- }
-
- @Test()
- public void testNetworkStatusVersionPrefixLineAtChar()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "@consensus\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "directory-footer\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLinePoundChar()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "#consensus\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersion42()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 42");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionFourtyTwo()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- "network-status-version FourtyTwo");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionSpaceBefore()
- throws DescriptorParseException {
- ConsensusBuilder.createWithNetworkStatusVersionLine(
- " network-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusSpaceBefore() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(" vote-status consensus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status ");
- }
-
- @Test()
- public void testVoteStatusConsensusOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status consensus ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusVote() throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine("vote-status vote");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusTheMagicVoteStatus()
- throws DescriptorParseException {
- ConsensusBuilder.createWithVoteStatusLine(
- "vote-status TheMagicVoteStatus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoLine()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodOneSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodEleven()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(
- "consensus-method eleven");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodMinusOne()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNinePeriod()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine("consensus-method "
- + "999999999999999999999999999999999999999999999999999999999999");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodTwoLines()
- throws DescriptorParseException {
- ConsensusBuilder.createWithConsensusMethodLine(
- "consensus-method 1\nconsensus-method 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterLongAgo() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine("valid-after long ago");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterFeb30() throws DescriptorParseException {
- ConsensusBuilder.createWithValidAfterLine(
- "valid-after 2011-02-30 09:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithFreshUntilLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilAroundTen() throws DescriptorParseException {
- ConsensusBuilder.createWithFreshUntilLine(
- "fresh-until 2011-11-30 around ten");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidUntilTomorrowMorning()
- throws DescriptorParseException {
- ConsensusBuilder.createWithValidUntilLine(
- "valid-until tomorrow morning");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayTriple() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine(
- "voting-delay 300 300 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelaySingle() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneTwo() throws DescriptorParseException {
- ConsensusBuilder.createWithVotingDelayLine("voting-delay one two");
- }
-
- @Test()
- public void testClientServerVersionsNoLine()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.clientVersionsLine = null;
- cb.serverVersionsLine = null;
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertNull(consensus.getRecommendedClientVersions());
- assertNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testServerVersionsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithServerVersionsLine(null);
- assertNotNull(consensus.getRecommendedClientVersions());
- assertNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testClientVersionsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine(null);
- assertNull(consensus.getRecommendedClientVersions());
- assertNotNull(consensus.getRecommendedServerVersions());
- }
-
- @Test()
- public void testClientVersionsNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine("client-versions");
- assertNotNull(consensus.getRecommendedClientVersions());
- assertTrue(consensus.getRecommendedClientVersions().isEmpty());
- }
-
- @Test()
- public void testClientVersionsOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithClientVersionsLine("client-versions ");
- assertNotNull(consensus.getRecommendedClientVersions());
- assertTrue(consensus.getRecommendedClientVersions().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsComma() throws DescriptorParseException {
- ConsensusBuilder.createWithClientVersionsLine("client-versions ,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsCommaVersion()
- throws DescriptorParseException {
- ConsensusBuilder.createWithClientVersionsLine(
- "client-versions ,0.2.2.34");
- }
-
- @Test()
- public void testPackageNone() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(null);
- assertNull(consensus.getPackageLines());
- }
-
- @Test()
- public void testPackageOne() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http digest=digest";
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(packageLine);
- assertEquals(packageLine.substring("package ".length()),
- consensus.getPackageLines().get(0));
- }
-
- @Test()
- public void testPackageTwo() throws DescriptorParseException {
- List<String> packageLines = Arrays.asList(
- "package shouldbesecond 0 http digest=digest",
- "package outoforder 0 http digest=digest");
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithPackageLines(packageLines.get(0)
- + "\n" + packageLines.get(1));
- for (int i = 0; i < packageLines.size(); i++) {
- assertEquals(packageLines.get(i).substring("package ".length()),
- consensus.getPackageLines().get(i));
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPackageIncomplete() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http";
- ConsensusBuilder.createWithPackageLines(packageLine);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoLine() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine("known-flags");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsOneSpace() throws DescriptorParseException {
- ConsensusBuilder.createWithKnownFlagsLine("known-flags ");
- }
-
- @Test()
- public void testParamsNoLine() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine(null);
- assertNull(consensus.getConsensusParams());
- }
-
- @Test()
- public void testParamsNoSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test()
- public void testParamsOneSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params ");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test()
- public void testParamsThreeSpaces() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params ");
- assertNotNull(consensus.getConsensusParams());
- assertTrue(consensus.getConsensusParams().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsNoEqualSign() throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params key-value");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsOneTooLargeNegative()
- throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params min=-2147483649");
- }
-
- @Test()
- public void testParamsLargestNegative()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params min=-2147483648");
- assertEquals(1, consensus.getConsensusParams().size());
- assertEquals(-2147483648,
- (int) consensus.getConsensusParams().get("min"));
- }
-
- @Test()
- public void testParamsLargestPositive()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithParamsLine("params max=2147483647");
- assertEquals(1, consensus.getConsensusParams().size());
- assertEquals(2147483647,
- (int) consensus.getConsensusParams().get("max"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testParamsOneTooLargePositive()
- throws DescriptorParseException {
- ConsensusBuilder.createWithParamsLine("params max=2147483648");
- }
-
- @Test()
- public void testDirSourceLegacyNickname()
- throws DescriptorParseException {
- DirSourceBuilder dsb = new DirSourceBuilder();
- dsb.nickname = "gabelmoo-legacy";
- dsb.identity = "81349FC1F2DBA2C2C11B45CB9706637D480AB913";
- dsb.contactLine = null;
- dsb.voteDigestLine = null;
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithDirSource(dsb.buildDirSource());
- assertEquals(3, consensus.getDirSourceEntries().size());
- assertTrue(consensus.getDirSourceEntries().get(
- "81349FC1F2DBA2C2C11B45CB9706637D480AB913").isLegacy());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceNicknameTooLong()
- throws DescriptorParseException {
- DirSourceBuilder.createWithNickname("gabelmooisfinebutthisistoolong");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceIdentityTooShort()
- throws DescriptorParseException {
- DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceIdentityTooLong()
- throws DescriptorParseException {
- DirSourceBuilder.createWithIdentity("ED03BB616EB2F60BEC8015111"
- + "4BB25CEF515B226ED03BB616EB2F60BEC8");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceHostnameMissing()
- throws DescriptorParseException {
- DirSourceBuilder.createWithHostName("");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceAddress24() throws DescriptorParseException {
- DirSourceBuilder.createWithAddress("212.112.245");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceAddress40() throws DescriptorParseException {
- DirSourceBuilder.createWithAddress("212.112.245.170.123");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPortMinusOne()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("-1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPort66666()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceDirPortOnions()
- throws DescriptorParseException {
- DirSourceBuilder.createWithDirPort("onions");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceOrPortOnions()
- throws DescriptorParseException {
- DirSourceBuilder.createWithOrPort("onions");
- }
-
- @Test()
- public void testDirSourceContactNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine(null);
- assertNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test()
- public void testDirSourceContactLineNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine("contact");
- assertNotNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test()
- public void testDirSourceContactLineOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- DirSourceBuilder.createWithContactLine("contact ");
- assertNotNull(consensus.getDirSourceEntries().get(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226").getContactLine());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestNoLine()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestLineNoSpace()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine("vote-digest");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceVoteDigestLineOneSpace()
- throws DescriptorParseException {
- DirSourceBuilder.createWithVoteDigestLine("vote-digest ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameNotAllowedChars()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithNickname("notAll()wed");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithNickname("1234567890123456789tooLong");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64("TooShort");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintEndsWithEqualSign()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithFingerprintBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8HMAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorTooShort() throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64("TooShort");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorEndsWithEqualSign()
- throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64(
- "ADQ6gCT3DiFHKPDFr3rODBUI8H=");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDescriptorTooLong() throws DescriptorParseException {
- StatusEntryBuilder.createWithDescriptorBase64(
- "Yiti+nayuT2Efe2X1+M4nslwVuUAAAA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished1960() throws DescriptorParseException {
- StatusEntryBuilder.createWithPublishedString("1960-11-29 21:34:27");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished9999() throws DescriptorParseException {
- StatusEntryBuilder.createWithPublishedString("9999-11-29 21:34:27");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress256() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("256.63.8.215");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress24() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("50.63.8/24");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressV6() throws DescriptorParseException {
- StatusEntryBuilder.createWithAddress("::1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPort66666() throws DescriptorParseException {
- StatusEntryBuilder.createWithOrPort("66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortEighty() throws DescriptorParseException {
- StatusEntryBuilder.createWithOrPort("eighty");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMinusOne() throws DescriptorParseException {
- StatusEntryBuilder.createWithDirPort("-1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortZero() throws DescriptorParseException {
- StatusEntryBuilder.createWithDirPort("zero");
- }
-
- @Test()
- public void testSLineNoSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithSLine("s");
- assertTrue(consensus.getStatusEntry(
- "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
- }
-
- @Test()
- public void testSLineOneSpace() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithSLine("s ");
- assertTrue(consensus.getStatusEntry(
- "00343A8024F70E214728F0C5AF7ACE0C1508F073").getFlags().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoSLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.sLine = sb.sLine + "\n" + sb.sLine;
- 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");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWLineOneSpace() throws DescriptorParseException {
- StatusEntryBuilder.createWithWLine("w ");
- }
-
- @Test()
- public void testWLineWarpSeven() throws DescriptorParseException {
- StatusEntryBuilder.createWithWLine("w Warp=7");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoWLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.wLine = sb.wLine + "\n" + sb.wLine;
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
-
- @Test()
- public void testWLineUnmeasured() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.wLine = "w Bandwidth=42424242 Unmeasured=1";
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
- if (s.getBandwidth() == 42424242L) {
- assertTrue(s.getUnmeasured());
- }
- }
- }
-
- @Test()
- public void testWLineNotUnmeasured() throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- StatusEntryBuilder.createWithWLine("w Bandwidth=20");
- for (NetworkStatusEntry s : consensus.getStatusEntries().values()) {
- assertFalse(s.getUnmeasured());
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPolicy() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPorts() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p accept");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineNoPolicyNoPorts() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPLineProject() throws DescriptorParseException {
- StatusEntryBuilder.createWithPLine("p project 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoPLines() throws DescriptorParseException {
- StatusEntryBuilder sb = new StatusEntryBuilder();
- sb.pLine = sb.pLine + "\n" + sb.pLine;
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.add(sb.buildStatusEntry());
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- }
-
- @Test()
- public void testNoStatusEntries() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.statusEntries.clear();
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertFalse(consensus.containsStatusEntry(
- "00795A6E8D91C270FC23B30F388A495553E01894"));
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectoryFooterNoLine()
- throws DescriptorParseException {
- /* This breaks, because a bandwidth-weights line without a preceding
- * directory-footer line is not allowed. */
- ConsensusBuilder.createWithDirectoryFooterLine(null);
- }
-
- @Test()
- public void testDirectoryFooterMissing()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.setDirectoryFooterLine(null);
- cb.setBandwidthWeightsLine(null);
- /* This does not break, because directory footers were optional before
- * consensus method 9. */
- RelayNetworkStatusConsensus consensus =
- new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true);
- assertNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testDirectoryFooterLineSpace()
- throws DescriptorParseException {
- ConsensusBuilder.createWithDirectoryFooterLine("directory-footer ");
- }
-
- @Test()
- public void testBandwidthWeightsNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus =
- ConsensusBuilder.createWithBandwidthWeightsLine(null);
- assertNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testBandwidthWeightsLineNoSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithBandwidthWeightsLine("bandwidth-weights");
- assertNotNull(consensus.getBandwidthWeights());
- }
-
- @Test()
- public void testBandwidthWeightsLineOneSpace()
- throws DescriptorParseException {
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithBandwidthWeightsLine("bandwidth-weights ");
- assertNotNull(consensus.getBandwidthWeights());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthWeightsLineNoEqualSign()
- throws DescriptorParseException {
- ConsensusBuilder.createWithBandwidthWeightsLine(
- "bandwidth-weights Wbd-285");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignatureIdentityTooShort()
- throws DescriptorParseException {
- DirectorySignatureBuilder.createWithIdentity("ED03BB616EB2F60");
- }
-
- @Test()
- public void testDirectorySignatureIdentityTooLong()
- throws DescriptorParseException {
- /* This hex string has an unusual length of 58 hex characters, but
- * dir-spec.txt only requires a hex string, and we can't know all hex
- * string lengths for all future digest algorithms, so let's just
- * accept this. */
- DirectorySignatureBuilder.createWithIdentity(
- "ED03BB616EB2F60BEC80151114BB25CEF515B226ED03BB616EB2F60BEC");
- }
-
- @Test()
- public void testDirectorySignatureSigningKeyTooShort()
- throws DescriptorParseException {
- /* See above, we accept this hex string even though it's unusually
- * short. */
- DirectorySignatureBuilder.createWithSigningKey("845CF1D0B370CA");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignatureSigningKeyTooShortOddNumber()
- throws DescriptorParseException {
- /* We don't accept this hex string, because it contains an odd number
- * of hex characters. */
- DirectorySignatureBuilder.createWithSigningKey("845");
- }
-
- @Test()
- public void testDirectorySignatureSigningKeyTooLong()
- throws DescriptorParseException {
- /* See above, we accept this hex string even though it's unusually
- * long. */
- DirectorySignatureBuilder.createWithSigningKey(
- "845CF1D0B370CA443A8579D18E7987E7E532F639845CF1D0B370CA443A");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNonAsciiByte20() throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- byte[] consensusBytes = cb.buildConsensus();
- consensusBytes[20] = (byte) 200;
- new RelayNetworkStatusConsensusImpl(consensusBytes, true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNonAsciiByteMinusOne()
- throws DescriptorParseException {
- ConsensusBuilder cb = new ConsensusBuilder();
- cb.networkStatusVersionLine = "Xnetwork-status-version 3";
- byte[] consensusBytes = cb.buildConsensus();
- consensusBytes[0] = (byte) 200;
- new RelayNetworkStatusConsensusImpl(consensusBytes, true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedHeaderLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedHeaderLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedHeaderLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirSourceLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirSourceLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedStatusEntryLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedStatusEntryLine(
- unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedStatusEntryLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedStatusEntryLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirectoryFooterLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedFooterLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirectoryFooterLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedFooterLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirectorySignatureLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ConsensusBuilder.createWithUnrecognizedDirectorySignatureLine(
- unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedDirectorySignatureLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusConsensus consensus = ConsensusBuilder.
- createWithUnrecognizedDirectorySignatureLine(unrecognizedLine,
- false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, consensus.getUnrecognizedLines());
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
deleted file mode 100644
index 1c840f5..0000000
--- a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
+++ /dev/null
@@ -1,1373 +0,0 @@
-/* Copyright 2011--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import org.torproject.descriptor.DirectorySignature;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.RelayNetworkStatusVote;
-
-/* TODO Add test cases for all lines starting with "opt ". */
-
-/* Test parsing of network status votes. Some of the vote-parsing code is
- * already tested in the consensus-parsing tests. The tests in this class
- * focus on the differences between votes and consensuses that are mostly
- * in the directory header. */
-public class RelayNetworkStatusVoteImplTest {
-
- /* Helper class to build a vote based on default data and modifications
- * requested by test methods. */
- private static class VoteBuilder {
- private String networkStatusVersionLine = "network-status-version 3";
- private static RelayNetworkStatusVote
- createWithNetworkStatusVersionLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.networkStatusVersionLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String voteStatusLine = "vote-status vote";
- private static RelayNetworkStatusVote
- createWithVoteStatusLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.voteStatusLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String consensusMethodsLine =
- "consensus-methods 1 2 3 4 5 6 7 8 9 10 11";
- private static RelayNetworkStatusVote
- createWithConsensusMethodsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.consensusMethodsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String publishedLine = "published 2011-11-30 08:50:01";
- private static RelayNetworkStatusVote
- createWithPublishedLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.publishedLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String validAfterLine = "valid-after 2011-11-30 09:00:00";
- private static RelayNetworkStatusVote
- createWithValidAfterLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.validAfterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String freshUntilLine = "fresh-until 2011-11-30 10:00:00";
- private static RelayNetworkStatusVote
- createWithFreshUntilLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.freshUntilLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String validUntilLine = "valid-until 2011-11-30 12:00:00";
- private static RelayNetworkStatusVote
- createWithValidUntilLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.validUntilLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String votingDelayLine = "voting-delay 300 300";
- private static RelayNetworkStatusVote
- createWithVotingDelayLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.votingDelayLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String clientVersionsLine = "client-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- private static RelayNetworkStatusVote
- createWithClientVersionsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.clientVersionsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String serverVersionsLine = "server-versions 0.2.1.31,"
- + "0.2.2.34,0.2.3.6-alpha,0.2.3.7-alpha,0.2.3.8-alpha";
- private static RelayNetworkStatusVote
- createWithServerVersionsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.serverVersionsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String packageLines = null;
- protected static RelayNetworkStatusVote
- createWithPackageLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.packageLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String knownFlagsLine = "known-flags Authority BadExit Exit "
- + "Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid";
- private static RelayNetworkStatusVote
- createWithKnownFlagsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.knownFlagsLine = 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 "
- + "guard-bw-exc-exits=184320 enough-mtbf=1";
- private static RelayNetworkStatusVote
- createWithFlagThresholdsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.flagThresholdsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String paramsLine = "params "
- + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
- + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
- + "bwauthtd=5000 bwauthti=50000 bwauthtidecay=5000 cbtnummodes=3 "
- + "cbtquantile=80 circwindow=1000 refuseunknownexits=1";
- private static RelayNetworkStatusVote
- createWithParamsLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.paramsLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirSourceLine = "dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80";
- private static RelayNetworkStatusVote
- createWithDirSourceLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirSourceLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String contactLine = "contact 4096R/E012B42D Jacob Appelbaum "
- + "<jacob(a)appelbaum.net>";
- private static RelayNetworkStatusVote
- createWithContactLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.contactLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String legacyDirKeyLine = null;
- private static RelayNetworkStatusVote
- createWithLegacyDirKeyLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.legacyDirKeyLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCertificateVersionLine =
- "dir-key-certificate-version 3";
- private static RelayNetworkStatusVote
- createWithDirKeyCertificateVersionLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCertificateVersionLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String fingerprintLine = "fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C";
- private static RelayNetworkStatusVote
- createWithFingerprintLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.fingerprintLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyPublishedLine = "dir-key-published 2011-04-27 "
- + "05:34:37";
- private static RelayNetworkStatusVote
- createWithDirKeyPublishedLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyPublishedLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyExpiresLine = "dir-key-expires 2012-04-27 "
- + "05:34:37";
- private static RelayNetworkStatusVote
- createWithDirKeyExpiresLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyExpiresLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirIdentityKeyLines = "dir-identity-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIIBigKCAYEAtKpuLgVK25sfScjsxfVU1ljofrDygt9GP7bNJl/rghX42KUT97"
- + "5W\nrGp/fbhF7p+FcKCzNOhJFINQbRf/5E3lN8mzoamIU43QqQ9RRVf94688Us"
- + "azVsAN\nNVT0v9J0cr387WePjenRuIE1MmiP0nmw/XdvbPTayqax7VYlcUMXGH"
- + "l8DnWix1EN\nRwmeig+JBte0JS12oo2HG9zcSfjLJVjY6ZmvRrVycXiRxGc/Jg"
- + "NlSrV4cxUNykaB\nJ6pO6J499OZfQu7m1vAPTENrVJ4yEfRGRwFIY+d/s8BkKc"
- + "aiWtXAfTe31uBI6GEH\nmS3HNu1JVSuoaUiQIvVYDLMfBvMcNyAx97UT1l6E0T"
- + "n6a7pgChrquGwXai1xGzk8\n58aXwdSFoFBSTCkyemopq5H20p/nkPAO0pHL1k"
- + "TvcaKz9CEj4XcKm+kOmzejYmIa\nkbWNcRpXPiUZ+xmwGtsq30xrzqiONmERkx"
- + "qlmf7bVQPFvh3Kz6hGcmTBhTbHSe9h\nzDgmdaTNn3EHAgMBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static RelayNetworkStatusVote
- createWithDirIdentityKeyLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirIdentityKeyLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirSigningKeyLines = "dir-signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAN05qyHFQlTqykMP8yLuD4G2UuYulD4Xs8iSX5uqF+WGsUA1E4zZh4"
- + "8h\nDFj8+drFiCu3EqhMEmVG4ACtJK2uz6D1XohUsbPWTR6LSnWJ8q6/zfTSLu"
- + "mBGsN7\nPUXyMNjwRKL6UvrcbYk1d2mRBLO7SAP/sFW5fHhIBVeLIWrzQ19rAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static RelayNetworkStatusVote
- createWithDirSigningKeyLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirSigningKeyLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCrosscertLines = "dir-key-crosscert\n"
- + "-----BEGIN ID SIGNATURE-----\n"
- + "rPBFn6IJ6TvAHj4pSwlg+RTn1fP89JGSVa08wuyJr5dAvZsdakQXvRjamT9oJU"
- + "aZ\nnY5Rl/tRlGuSQ0BglTPPKoXdKERK0FUr9f0EKrQy7NDUgE2j9losiRuyKz"
- + "hA3neZ\nK4yF8bhqAwM51u7fzAhIjNeRif9c04rhFJJCseco84w=\n"
- + "-----END ID SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirKeyCrosscertLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCrosscertLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String dirKeyCertificationLines = "dir-key-certification\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "hPSh6FuohNF5ccjiMbkvr8cZJwGFuL11cNtwN9k0X3pUdFZVATIEkqBe7z+rE2"
- + "PX\nPw+BGyC6wYAieoTVIhLpwKqd7DXLYjuhPZ28+7MQaDL01AqYeRp5PT01Px"
- + "rFY0Um\nlVf95uqUitgvDT76Ne4ExWk6UvGlYB9OBgBySZz8VWe9znoMqb0uHn"
- + "/p8IzqTApT\nAxRWXBHClntMeRqtGxaj8DcdJFn8yMxQiZG7MfDg2sq2ySPJyG"
- + "lN+neoVDVhZiDI\n9LTNmw60gWlUp2erFeam8Mo1ZBC4DPNjQEm6QeHZFZMkhD"
- + "uO6SwS/FL712A42+Co\nYtMaVot/p5FG2ZSBXbgl2XP5/z8ELnpmXqMbPAoWRo"
- + "3BPNSJkIQQNog8Q5ZrK+av\nZDw5eGPltGKsXOkvuzIMM8nBeAnDPDgYvzrIFO"
- + "bEGbvY/P8mzVAZxp3Yz+sRtNel\nC1SWz/Fx+Saex5oI7DJ3xtSD4XqKb/wYwZ"
- + "FT8IxDYq1t2tFXdHxd4QPRVcvc0zYC\n"
- + "-----END SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirKeyCertificationLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.dirKeyCertificationLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private List<String> statusEntries = null;
- private static RelayNetworkStatusVote createWithStatusEntries(
- List<String> statusEntries) throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.statusEntries = statusEntries;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String directoryFooterLine = "directory-footer";
- private static RelayNetworkStatusVote
- createWithDirectoryFooterLine(String line)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.directoryFooterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String directorySignatureLines = "directory-signature "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C "
- + "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
- + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
- + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----";
- private static RelayNetworkStatusVote
- createWithDirectorySignatureLines(String lines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.directorySignatureLines = lines;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
- private String unrecognizedHeaderLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedHeaderLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedHeaderLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirSourceLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedDirSourceLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedDirSourceLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedStatusEntryLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedStatusEntryLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedStatusEntryLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedFooterLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedFooterLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedFooterLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
- private String unrecognizedDirectorySignatureLine = null;
- protected static RelayNetworkStatusVote
- createWithUnrecognizedDirectorySignatureLine(String line,
- boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.unrecognizedDirectorySignatureLine = line;
- return new RelayNetworkStatusVoteImpl(vb.buildVote(),
- failUnrecognizedDescriptorLines);
- }
-
- private VoteBuilder() {
- if (this.statusEntries != null) {
- return;
- }
- this.statusEntries = new ArrayList<>();
- this.statusEntries.add("r right2privassy3 "
- + "ADQ6gCT3DiFHKPDFr3rODBUI8HM lJY5Vf7kXec+VdkGW2flEsfkFC8 "
- + "2011-11-12 00:03:40 50.63.8.215 9023 0\n"
- + "s Exit Fast Guard Running Stable Valid\n"
- + "opt v Tor 0.2.1.29 (r8e9b25e6c7a2e70c)\n"
- + "w Bandwidth=297 Measured=73\n"
- + "p accept 80,1194,1220,1293,1500,1533,1677,1723,1863,"
- + "2082-2083,2086-2087,2095-2096,2102-2104,3128,3389,3690,4321,"
- + "4643,5050,5190,5222-5223,5228,5900,6660-6669,6679,6697,8000,"
- + "8008,8074,8080,8087-8088,8443,8888,9418,9999-10000,19294,"
- + "19638\n"
- + "m 8,9,10,11 "
- + "sha256=9ciEx9t0McXk9A06I7qwN7pxuNOdpCP64RV/6cx2Zkc");
- }
- private byte[] buildVote() {
- StringBuilder sb = new StringBuilder();
- this.appendHeader(sb);
- this.appendDirSource(sb);
- this.appendStatusEntries(sb);
- this.appendFooter(sb);
- this.appendDirectorySignature(sb);
- return sb.toString().getBytes();
- }
- private void appendHeader(StringBuilder sb) {
- if (this.networkStatusVersionLine != null) {
- sb.append(this.networkStatusVersionLine).append("\n");
- }
- if (this.voteStatusLine != null) {
- sb.append(this.voteStatusLine).append("\n");
- }
- if (this.consensusMethodsLine != null) {
- sb.append(this.consensusMethodsLine).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.validAfterLine != null) {
- sb.append(this.validAfterLine).append("\n");
- }
- if (this.freshUntilLine != null) {
- sb.append(this.freshUntilLine).append("\n");
- }
- if (this.validUntilLine != null) {
- sb.append(this.validUntilLine).append("\n");
- }
- if (this.votingDelayLine != null) {
- sb.append(this.votingDelayLine).append("\n");
- }
- if (this.clientVersionsLine != null) {
- sb.append(this.clientVersionsLine).append("\n");
- }
- if (this.serverVersionsLine != null) {
- sb.append(this.serverVersionsLine).append("\n");
- }
- if (this.packageLines != null) {
- sb.append(this.packageLines).append("\n");
- }
- if (this.knownFlagsLine != null) {
- sb.append(this.knownFlagsLine).append("\n");
- }
- if (this.flagThresholdsLine != null) {
- sb.append(this.flagThresholdsLine).append("\n");
- }
- if (this.paramsLine != null) {
- sb.append(this.paramsLine).append("\n");
- }
- if (this.unrecognizedHeaderLine != null) {
- sb.append(this.unrecognizedHeaderLine).append("\n");
- }
- }
- private void appendDirSource(StringBuilder sb) {
- if (this.dirSourceLine != null) {
- sb.append(this.dirSourceLine).append("\n");
- }
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.legacyDirKeyLine != null) {
- sb.append(this.legacyDirKeyLine).append("\n");
- }
- if (this.dirKeyCertificateVersionLine != null) {
- sb.append(this.dirKeyCertificateVersionLine).append("\n");
- }
- if (this.fingerprintLine != null) {
- sb.append(this.fingerprintLine).append("\n");
- }
- if (this.dirKeyPublishedLine != null) {
- sb.append(this.dirKeyPublishedLine).append("\n");
- }
- if (this.dirKeyExpiresLine != null) {
- sb.append(this.dirKeyExpiresLine).append("\n");
- }
- if (this.dirIdentityKeyLines != null) {
- sb.append(this.dirIdentityKeyLines).append("\n");
- }
- if (this.dirSigningKeyLines != null) {
- sb.append(this.dirSigningKeyLines).append("\n");
- }
- if (this.dirKeyCrosscertLines != null) {
- sb.append(this.dirKeyCrosscertLines).append("\n");
- }
- if (this.dirKeyCertificationLines != null) {
- sb.append(this.dirKeyCertificationLines).append("\n");
- }
- if (this.unrecognizedDirSourceLine != null) {
- sb.append(this.unrecognizedDirSourceLine).append("\n");
- }
- }
- private void appendStatusEntries(StringBuilder sb) {
- for (String statusEntry : this.statusEntries) {
- sb.append(statusEntry).append("\n");
- }
- if (this.unrecognizedStatusEntryLine != null) {
- sb.append(this.unrecognizedStatusEntryLine).append("\n");
- }
- }
- private void appendFooter(StringBuilder sb) {
- if (this.directoryFooterLine != null) {
- sb.append(this.directoryFooterLine).append("\n");
- }
- if (this.unrecognizedFooterLine != null) {
- sb.append(this.unrecognizedFooterLine).append("\n");
- }
- }
- private void appendDirectorySignature(StringBuilder sb) {
- if (this.directorySignatureLines != null) {
- sb.append(directorySignatureLines).append("\n");
- }
- if (this.unrecognizedDirectorySignatureLine != null) {
- sb.append(this.unrecognizedDirectorySignatureLine).append("\n");
- }
- }
- }
-
- @Test()
- public void testSampleVote() throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- RelayNetworkStatusVote vote =
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- assertEquals(3, vote.getNetworkStatusVersion());
- List<Integer> consensusMethods = Arrays.asList(
- new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
- assertEquals(vote.getConsensusMethods(), consensusMethods);
- assertEquals(1322643001000L, vote.getPublishedMillis());
- assertEquals(1322643600000L, vote.getValidAfterMillis());
- assertEquals(1322647200000L, vote.getFreshUntilMillis());
- assertEquals(1322654400000L, vote.getValidUntilMillis());
- assertEquals(300L, vote.getVoteSeconds());
- assertEquals(300L, vote.getDistSeconds());
- assertTrue(vote.getKnownFlags().contains("Running"));
- assertEquals(30000, (int) vote.getConsensusParams().get(
- "CircuitPriorityHalflifeMsec"));
- assertEquals("Tor 0.2.1.29 (r8e9b25e6c7a2e70c)",
- vote.getStatusEntry("00343A8024F70E214728F0C5AF7ACE0C1508F073").
- getVersion());
- assertEquals(3, vote.getDirKeyCertificateVersion());
- assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
- vote.getIdentity());
- assertEquals(1303882477000L, /* 2011-04-27 05:34:37 */
- vote.getDirKeyPublishedMillis());
- assertEquals(1335504877000L, /* 2012-04-27 05:34:37 */
- vote.getDirKeyExpiresMillis());
- assertEquals("-----BEGIN RSA PUBLIC KEY-----",
- vote.getDirIdentityKey().split("\n")[0]);
- assertEquals("-----BEGIN RSA PUBLIC KEY-----",
- vote.getDirSigningKey().split("\n")[0]);
- assertEquals("-----BEGIN ID SIGNATURE-----",
- vote.getDirKeyCrosscert().split("\n")[0]);
- assertEquals("-----BEGIN SIGNATURE-----",
- vote.getDirKeyCertification().split("\n")[0]);
- assertEquals(1, vote.getSignatures().size());
- DirectorySignature signature = vote.getSignatures().get(0);
- assertEquals("sha1", signature.getAlgorithm());
- assertEquals("80550987E1D626E3EBA5E5E75A458DE0626D088C",
- signature.getIdentity());
- assertEquals("EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19",
- signature.getSigningKeyDigest());
- assertEquals("-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxn"
- + "F3Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40Oi"
- + "kfOIwEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----\n", signature.getSignature());
- assertTrue(vote.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNewLineSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 3\n ");
- }
-
- @Test()
- public void testNetworkStatusVersionPrefixLineAtChar()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "@vote\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLine()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "directory-footer\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionPrefixLinePoundChar()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "#vote\nnetwork-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersion42()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version 42");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionFourtyTwo()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- "network-status-version FourtyTwo");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoLine() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNetworkStatusVersionSpaceBefore()
- throws DescriptorParseException {
- VoteBuilder.createWithNetworkStatusVersionLine(
- " network-status-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusSpaceBefore() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(" vote-status vote");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status ");
- }
-
- @Test()
- public void testVoteStatusVoteOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status vote ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusConsensus() throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine("vote-status consensus");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVoteStatusTheMagicVoteStatus()
- throws DescriptorParseException {
- VoteBuilder.createWithVoteStatusLine(
- "vote-status TheMagicVoteStatus");
- }
-
- @Test()
- public void testConsensusMethodNoLine()
- throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithConsensusMethodsLine(null);
- assertNull(vote.getConsensusMethods());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodEleven()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine(
- "consensus-methods eleven");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodMinusOne()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodNinePeriod()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine("consensus-methods "
- + "999999999999999999999999999999999999999999999999999999999999");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testConsensusMethodTwoLines()
- throws DescriptorParseException {
- VoteBuilder.createWithConsensusMethodsLine(
- "consensus-method 1\nconsensus-method 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedNoLine() throws DescriptorParseException {
- VoteBuilder.createWithPublishedLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoLine() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterLongAgo() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine("valid-after long ago");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidAfterFeb30() throws DescriptorParseException {
- VoteBuilder.createWithValidAfterLine(
- "valid-after 2011-02-30 09:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilNoLine() throws DescriptorParseException {
- VoteBuilder.createWithFreshUntilLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFreshUntilAroundTen() throws DescriptorParseException {
- VoteBuilder.createWithFreshUntilLine(
- "fresh-until 2011-11-30 around ten");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testValidUntilTomorrowMorning()
- throws DescriptorParseException {
- VoteBuilder.createWithValidUntilLine(
- "valid-until tomorrow morning");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoLine() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayTriple() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine(
- "voting-delay 300 300 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelaySingle() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay 300");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testVotingDelayOneTwo() throws DescriptorParseException {
- VoteBuilder.createWithVotingDelayLine("voting-delay one two");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsComma() throws DescriptorParseException {
- VoteBuilder.createWithClientVersionsLine("client-versions ,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testClientVersionsCommaVersion()
- throws DescriptorParseException {
- VoteBuilder.createWithClientVersionsLine(
- "client-versions ,0.2.2.34");
- }
-
- @Test()
- public void testPackageNone() throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(null);
- assertNull(vote.getPackageLines());
- }
-
- @Test()
- public void testPackageOne() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http digest=digest";
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(packageLine);
- assertEquals(packageLine.substring("package ".length()),
- vote.getPackageLines().get(0));
- }
-
- @Test()
- public void testPackageTwo() throws DescriptorParseException {
- List<String> packageLines = Arrays.asList(
- "package shouldbesecond 0 http digest=digest",
- "package outoforder 0 http digest=digest");
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithPackageLines(packageLines.get(0)
- + "\n" + packageLines.get(1));
- for (int i = 0; i < packageLines.size(); i++) {
- assertEquals(packageLines.get(i).substring("package ".length()),
- vote.getPackageLines().get(i));
- }
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPackageIncomplete() throws DescriptorParseException {
- String packageLine = "package shouldbesecond 0 http";
- ConsensusBuilder.createWithPackageLines(packageLine);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoLine() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsNoSpace() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine("known-flags");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testKnownFlagsOneSpace() throws DescriptorParseException {
- VoteBuilder.createWithKnownFlagsLine("known-flags ");
- }
-
- @Test()
- public void testFlagThresholdsLine() throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- RelayNetworkStatusVote vote =
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- assertEquals(693369L, vote.getStableUptime());
- assertEquals(153249L, vote.getStableMtbf());
- assertEquals(40960L, vote.getFastBandwidth());
- assertEquals(94.669, vote.getGuardWfu(), 0.001);
- assertEquals(691200L, vote.getGuardTk());
- assertEquals(174080L, vote.getGuardBandwidthIncludingExits());
- assertEquals(184320L, vote.getGuardBandwidthExcludingExits());
- assertEquals(1, vote.getEnoughMtbfInfo());
- }
-
- @Test()
- public void testFlagThresholdsNoLine() throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithFlagThresholdsLine(null);
- assertEquals(-1L, vote.getStableUptime());
- assertEquals(-1L, vote.getStableMtbf());
- assertEquals(-1L, vote.getFastBandwidth());
- assertEquals(-1.0, vote.getGuardWfu(), 0.001);
- assertEquals(-1L, vote.getGuardTk());
- assertEquals(-1L, vote.getGuardBandwidthIncludingExits());
- assertEquals(-1L, vote.getGuardBandwidthExcludingExits());
- assertEquals(-1, vote.getEnoughMtbfInfo());
- }
-
- @Test()
- public void testFlagThresholdsAllZeroes()
- throws DescriptorParseException {
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds "
- + "stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.0% "
- + "guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 "
- + "enough-mtbf=0");
- assertEquals(0L, vote.getStableUptime());
- assertEquals(0L, vote.getStableMtbf());
- assertEquals(0L, vote.getFastBandwidth());
- assertEquals(0.0, vote.getGuardWfu(), 0.001);
- assertEquals(0L, vote.getGuardTk());
- assertEquals(0L, vote.getGuardBandwidthIncludingExits());
- assertEquals(0L, vote.getGuardBandwidthExcludingExits());
- assertEquals(0, vote.getEnoughMtbfInfo());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdsNoSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdsOneSpace()
- throws DescriptorParseException {
- VoteBuilder.createWithFlagThresholdsLine("flag-thresholds ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFlagThresholdDuplicate()
- throws DescriptorParseException {
- VoteBuilder vb = new VoteBuilder();
- vb.flagThresholdsLine = vb.flagThresholdsLine + "\n"
- + vb.flagThresholdsLine;
- new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source "
- + "urrassssssssssssssssssssssssssssssssssssssssssssssss "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameIllegalCharacters()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urra$ "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test()
- public void testFingerprintLowerCase() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987e1d626e3eba5e5e75a458de0626d088c 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintIllegalCharacters()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "ABCDEFGHIJKLM6E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + " 208.83.223.34 208.83.223.34 443 80");
- }
-
- @Test()
- public void testHostname256()
- throws DescriptorParseException {
- /* This test doesn't fail, because we're not parsing the hostname. */
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 256.256.256.256 "
- + "208.83.223.34 443 80");
- assertEquals("256.256.256.256", vote.getHostname());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHostnameMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
- + "80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress256()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "256.256.256.256 443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 443 "
- + "80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMinus443()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 -443 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortFourFourThree()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 four-four-three 80");
- }
-
- @Test()
- public void testDirPort0() throws DescriptorParseException {
- /* This test doesn't fail, because we're accepting DirPort 0, even
- * though it doesn't make sense from Tor's view. */
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 0 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortMissing() throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 ");
- }
-
- @Test()
- public void testDirPortOrPortIdentical()
- throws DescriptorParseException {
- /* This test doesn't fail, even though identical OR and Dir port don't
- * make much sense from Tor's view. */
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 80 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSourceLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSourceLine("dir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80\ndir-source urras "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C 208.83.223.34 "
- + "208.83.223.34 443 80");
- }
-
- @Test()
- public void testContactLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithContactLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testContactLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithContactLine("contact 4096R/E012B42D Jacob "
- + "Appelbaum <jacob(a)appelbaum.net>\ncontact 4096R/E012B42D Jacob "
- + "Appelbaum <jacob(a)appelbaum.net>");
- }
-
- @Test()
- public void testLegacyDirKeyLine() throws DescriptorParseException {
- RelayNetworkStatusVote vote = VoteBuilder.createWithLegacyDirKeyLine(
- "legacy-dir-key 81349FC1F2DBA2C2C11B45CB9706637D480AB913");
- assertEquals("81349FC1F2DBA2C2C11B45CB9706637D480AB913",
- vote.getLegacyDirKey());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testLegacyDirKeyLineNoId() throws DescriptorParseException {
- VoteBuilder.createWithLegacyDirKeyLine("legacy-dir-key ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificateVersionLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificateVersionLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificateVersionLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificateVersionLine(
- "dir-key-certificate-version 3\ndir-key-certificate-version 3");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C\nfingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineTooLong()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D088C8055");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintLineTooShort()
- throws DescriptorParseException {
- VoteBuilder.createWithFingerprintLine("fingerprint "
- + "80550987E1D626E3EBA5E5E75A458DE0626D");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublished3011()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "3011-04-27 05:34:37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublishedRecentlyAtNoon()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "recently 12:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyPublishedRecentlyNoTime()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyPublishedLine("dir-key-published "
- + "recently");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresSoonAtNoon()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires "
- + "soon 12:00:00");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyExpiresLineDuplicate()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyExpiresLine("dir-key-expires 2012-04-27 "
- + "05:34:37\ndir-key-expires 2012-04-27 05:34:37");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirIdentityKeyLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirIdentityKeyLines(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirSigningKeyLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirSigningKeyLines(null);
- }
-
- @Test()
- public void testDirKeyCrosscertLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCrosscertLines(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirKeyCertificationLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirKeyCertificationLines(null);
- }
-
- @Test()
- public void testDirectoryFooterLineMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirectoryFooterLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirectorySignaturesLinesMissing()
- throws DescriptorParseException {
- VoteBuilder.createWithDirectorySignatureLines(null);
- }
-
- @Test()
- public void testDirectorySignaturesLinesTwoAlgorithms()
- throws DescriptorParseException {
- String identitySha256 = "32519E5CB7254AB5A94CC9925EC7676E53D5D52EEAB7"
- + "914BD3ED751E537CAFCC";
- String signingKeyDigestSha256 = "5A59D99C17831B9254422B6C5AA10CC59381"
- + "6CAA5241E22ECAE8BBB4E8E9D1FC";
- String signatureSha256 = "-----BEGIN SIGNATURE-----\n"
- + "x57Alc424/zHS73SHokghGtNBVrBjtUz+gSL5w9AHGKUQcMyfw4Z9aDlKpTbFc"
- + "5W\nnyIvFmM9C2OAH0S1+a647HHIxhE0zKf4+yKSwzqSyL6sbKQygVlJsRHNRr"
- + "cFg8lp\nqBxEwvxQoA4xEDqnerR92pbK9l42nNLiKOcoReUqbbQ=\n"
- + "-----END SIGNATURE-----";
- String identitySha1 = "80550987E1D626E3EBA5E5E75A458DE0626D088C";
- String signingKeyDigestSha1 =
- "EEB9299D295C1C815E289FBF2F2BBEA5F52FDD19";
- String signatureSha1 = "-----BEGIN SIGNATURE-----\n"
- + "iHEU3Iidya5RIrjyYgv8tlU0R+rF56/3/MmaaZi0a67e7ZkISfQ4dghScHxnF3"
- + "Yh\nrXVaaoP07r6Ta+s0g1Zijm3lms50Nk/4tV2p8Y63c3F4Q3DAnK40OikfOI"
- + "wEj+Ny\n+zBRQssP3hPhTPOj/A7o3mZZwtL6x1sxpeu/nME1l5E=\n"
- + "-----END SIGNATURE-----";
- String signaturesLines = String.format(
- "directory-signature sha256 %s %s\n%s\n"
- + "directory-signature %s %s\n%s", identitySha256,
- signingKeyDigestSha256, signatureSha256, identitySha1,
- signingKeyDigestSha1, signatureSha1);
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
- assertEquals(2, vote.getSignatures().size());
- DirectorySignature firstSignature = vote.getSignatures().get(0);
- assertEquals("sha256", firstSignature.getAlgorithm());
- assertEquals(identitySha256, firstSignature.getIdentity());
- assertEquals(signingKeyDigestSha256,
- firstSignature.getSigningKeyDigest());
- assertEquals(signatureSha256 + "\n", firstSignature.getSignature());
- DirectorySignature secondSignature = vote.getSignatures().get(1);
- assertEquals("sha1", secondSignature.getAlgorithm());
- assertEquals(identitySha1, secondSignature.getIdentity());
- assertEquals(signingKeyDigestSha1,
- secondSignature.getSigningKeyDigest());
- assertEquals(signatureSha1 + "\n", secondSignature.getSignature());
- assertEquals(signingKeyDigestSha1, vote.getSigningKeyDigest());
- }
-
- @Test()
- public void testDirectorySignaturesLinesTwoAlgorithmsSameDigests()
- throws DescriptorParseException {
- String signaturesLines = "directory-signature 00 00\n"
- + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----\n"
- + "directory-signature sha256 00 00\n"
- + "-----BEGIN SIGNATURE-----\n00\n-----END SIGNATURE-----";
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithDirectorySignatureLines(signaturesLines);
- assertEquals(2, vote.getSignatures().size());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedHeaderLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedHeaderLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedHeaderLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedHeaderLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedDirSourceLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedDirSourceLine(unrecognizedLine,
- true);
- }
-
- @Test()
- public void testUnrecognizedDirSourceLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedDirSourceLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedFooterLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- VoteBuilder.createWithUnrecognizedFooterLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedFooterLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- RelayNetworkStatusVote vote = VoteBuilder.
- createWithUnrecognizedFooterLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, vote.getUnrecognizedLines());
- }
-
- @Test()
- public void testIdEd25519MasterKey()
- throws DescriptorParseException {
- String masterKey25519 = "8RH34kO07Pp+XYwzdoATVyCibIvmbslUjRkAm7J4IA8";
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r PDrelay1 AAFJ5u9xAqrKlpDW6N0pMhJLlKs "
- + "bgJiI/la3e9u0K7cQ5pMSXhigHI 2015-12-01 04:54:30 95.215.44.189 "
- + "8080 0\n"
- + "id ed25519 " + masterKey25519);
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithStatusEntries(statusEntries);
- String fingerprint = vote.getStatusEntries().firstKey();
- assertEquals(masterKey25519,
- vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
- }
-
- @Test()
- public void testIdEd25519None()
- throws DescriptorParseException {
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
- + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
- + "443 9030\n"
- + "id ed25519 none");
- RelayNetworkStatusVote vote =
- VoteBuilder.createWithStatusEntries(statusEntries);
- String fingerprint = vote.getStatusEntries().firstKey();
- assertEquals("none",
- vote.getStatusEntry(fingerprint).getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIdRsa1024None()
- throws DescriptorParseException {
- List<String> statusEntries = new ArrayList<>();
- statusEntries.add("r MathematicalApology AAPJIrV9nhfgTYQs0vsTghFaP2A "
- + "uA7p0m68O8ILXsf3aLZUj0EvDNE 2015-12-01 18:01:49 172.99.69.177 "
- + "443 9030\n"
- + "id rsa1024 none");
- VoteBuilder.createWithStatusEntries(statusEntries);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
deleted file mode 100644
index cd3f1a5..0000000
--- a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ /dev/null
@@ -1,1605 +0,0 @@
-/* Copyright 2012--2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import org.torproject.descriptor.DescriptorParseException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.SortedMap;
-
-import org.junit.Test;
-import org.torproject.descriptor.BandwidthHistory;
-import org.torproject.descriptor.ServerDescriptor;
-
-/* Test parsing of relay server descriptors. */
-public class ServerDescriptorImplTest {
-
- /* Helper class to build a descriptor based on default data and
- * modifications requested by test methods. */
- private static class DescriptorBuilder {
- private String routerLine = "router saberrider2008 94.134.192.243 "
- + "9001 0 0";
- private static ServerDescriptor createWithRouterLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String bandwidthLine = "bandwidth 51200 51200 53470";
- private static ServerDescriptor createWithBandwidthLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.bandwidthLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String platformLine = "platform Tor 0.2.2.35 "
- + "(git-b04388f9e7546a9f) on Linux i686";
- private static ServerDescriptor createWithPlatformLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.platformLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String publishedLine = "published 2012-01-01 04:03:19";
- private static ServerDescriptor createWithPublishedLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.publishedLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String fingerprintLine = "opt fingerprint D873 3048 FC8E "
- + "C910 2466 AD8F 3098 622B F1BF 71FD";
- private static ServerDescriptor createWithFingerprintLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.fingerprintLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hibernatingLine = null;
- private static ServerDescriptor createWithHibernatingLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hibernatingLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String uptimeLine = "uptime 48";
- private static ServerDescriptor createWithUptimeLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.uptimeLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String onionKeyLines = "onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
- + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
- + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static ServerDescriptor createWithOnionKeyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.onionKeyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String signingKeyLines = "signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
- + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
- + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----";
- private static ServerDescriptor createWithSigningKeyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.signingKeyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String onionKeyCrosscertLines = null;
- private static ServerDescriptor createWithOnionKeyCrosscertLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.onionKeyCrosscertLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyCrosscertLines = null;
- private static ServerDescriptor createWithNtorOnionKeyCrosscertLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ntorOnionKeyCrosscertLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String exitPolicyLines = "reject *:*";
- private static ServerDescriptor createWithExitPolicyLines(
- String lines) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.exitPolicyLines = lines;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String contactLine = "contact Random Person <nobody AT "
- + "example dot com>";
- private static ServerDescriptor createWithContactLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.contactLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String familyLine = null;
- private static ServerDescriptor createWithFamilyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.familyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String readHistoryLine = null;
- private static ServerDescriptor createWithReadHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.readHistoryLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String writeHistoryLine = null;
- private static ServerDescriptor createWithWriteHistoryLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.writeHistoryLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String eventdnsLine = null;
- private static ServerDescriptor createWithEventdnsLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.eventdnsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String cachesExtraInfoLine = null;
- private static ServerDescriptor createWithCachesExtraInfoLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.cachesExtraInfoLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String extraInfoDigestLine = "opt extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74";
- private static ServerDescriptor createWithExtraInfoDigestLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.extraInfoDigestLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String hiddenServiceDirLine = "opt hidden-service-dir";
- private static ServerDescriptor createWithHiddenServiceDirLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.hiddenServiceDirLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String protocolsLine = "opt protocols Link 1 2 Circuit 1";
- private static ServerDescriptor createWithProtocolsLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.protocolsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String allowSingleHopExitsLine = null;
- private static ServerDescriptor
- createWithAllowSingleHopExitsLine(String line)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.allowSingleHopExitsLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ipv6PolicyLine = null;
- private static ServerDescriptor createWithIpv6PolicyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ipv6PolicyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String ntorOnionKeyLine = null;
- private static ServerDescriptor createWithNtorOnionKeyLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.ntorOnionKeyLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String tunnelledDirServerLine = null;
- private static ServerDescriptor createWithTunnelledDirServerLine(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.tunnelledDirServerLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String routerSignatureLines = "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----";
- private static ServerDescriptor createWithRouterSignatureLines(
- String line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.routerSignatureLines = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private String unrecognizedLine = null;
- private static ServerDescriptor createWithUnrecognizedLine(
- String line, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.unrecognizedLine = line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private byte[] nonAsciiLineBytes = null;
- private static ServerDescriptor createWithNonAsciiLineBytes(
- byte[] lineBytes, boolean failUnrecognizedDescriptorLines)
- throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.nonAsciiLineBytes = lineBytes;
- return new RelayServerDescriptorImpl(db.buildDescriptor(),
- failUnrecognizedDescriptorLines);
- }
- private String identityEd25519Lines = null,
- masterKeyEd25519Line = null, routerSigEd25519Line = null;
- private static ServerDescriptor createWithEd25519Lines(
- String identityEd25519Lines, String masterKeyEd25519Line,
- String routerSigEd25519Line) throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- db.identityEd25519Lines = identityEd25519Lines;
- db.masterKeyEd25519Line = masterKeyEd25519Line;
- db.routerSigEd25519Line = routerSigEd25519Line;
- return new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- }
- private byte[] buildDescriptor() {
- StringBuilder sb = new StringBuilder();
- if (this.routerLine != null) {
- sb.append(this.routerLine).append("\n");
- }
- if (this.identityEd25519Lines != null) {
- sb.append(this.identityEd25519Lines).append("\n");
- }
- if (this.masterKeyEd25519Line != null) {
- sb.append(this.masterKeyEd25519Line).append("\n");
- }
- if (this.bandwidthLine != null) {
- sb.append(this.bandwidthLine).append("\n");
- }
- if (this.platformLine != null) {
- sb.append(this.platformLine).append("\n");
- }
- if (this.publishedLine != null) {
- sb.append(this.publishedLine).append("\n");
- }
- if (this.fingerprintLine != null) {
- sb.append(this.fingerprintLine).append("\n");
- }
- if (this.hibernatingLine != null) {
- sb.append(this.hibernatingLine).append("\n");
- }
- if (this.uptimeLine != null) {
- sb.append(this.uptimeLine).append("\n");
- }
- if (this.onionKeyLines != null) {
- sb.append(this.onionKeyLines).append("\n");
- }
- if (this.signingKeyLines != null) {
- sb.append(this.signingKeyLines).append("\n");
- }
- if (this.onionKeyCrosscertLines != null) {
- sb.append(this.onionKeyCrosscertLines).append("\n");
- }
- if (this.ntorOnionKeyCrosscertLines != null) {
- sb.append(this.ntorOnionKeyCrosscertLines).append("\n");
- }
- if (this.exitPolicyLines != null) {
- sb.append(this.exitPolicyLines).append("\n");
- }
- if (this.contactLine != null) {
- sb.append(this.contactLine).append("\n");
- }
- if (this.familyLine != null) {
- sb.append(this.familyLine).append("\n");
- }
- if (this.readHistoryLine != null) {
- sb.append(this.readHistoryLine).append("\n");
- }
- if (this.writeHistoryLine != null) {
- sb.append(this.writeHistoryLine).append("\n");
- }
- if (this.eventdnsLine != null) {
- sb.append(this.eventdnsLine).append("\n");
- }
- if (this.cachesExtraInfoLine != null) {
- sb.append(this.cachesExtraInfoLine).append("\n");
- }
- if (this.extraInfoDigestLine != null) {
- sb.append(this.extraInfoDigestLine).append("\n");
- }
- if (this.hiddenServiceDirLine != null) {
- sb.append(this.hiddenServiceDirLine).append("\n");
- }
- if (this.protocolsLine != null) {
- sb.append(this.protocolsLine).append("\n");
- }
- if (this.allowSingleHopExitsLine != null) {
- sb.append(this.allowSingleHopExitsLine).append("\n");
- }
- if (this.ipv6PolicyLine != null) {
- sb.append(this.ipv6PolicyLine).append("\n");
- }
- if (this.ntorOnionKeyLine != null) {
- sb.append(this.ntorOnionKeyLine).append("\n");
- }
- if (this.tunnelledDirServerLine != null) {
- sb.append(this.tunnelledDirServerLine).append("\n");
- }
- if (this.unrecognizedLine != null) {
- sb.append(this.unrecognizedLine).append("\n");
- }
- if (this.nonAsciiLineBytes != null) {
- try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(sb.toString().getBytes());
- baos.write(this.nonAsciiLineBytes);
- baos.write("\n".getBytes());
- if (this.routerSignatureLines != null) {
- baos.write(this.routerSignatureLines.getBytes());
- }
- return baos.toByteArray();
- } catch (IOException e) {
- return null;
- }
- }
- if (this.routerSigEd25519Line != null) {
- sb.append(this.routerSigEd25519Line).append("\n");
- }
- if (this.routerSignatureLines != null) {
- sb.append(this.routerSignatureLines).append("\n");
- }
- return sb.toString().getBytes();
- }
- }
-
- @Test()
- public void testSampleDescriptor() throws DescriptorParseException {
- DescriptorBuilder db = new DescriptorBuilder();
- ServerDescriptor descriptor =
- new RelayServerDescriptorImpl(db.buildDescriptor(), true);
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- assertEquals(9001, (int) descriptor.getOrPort());
- assertEquals(0, (int) descriptor.getSocksPort());
- 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(1325390599000L, descriptor.getPublishedMillis());
- assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
- descriptor.getFingerprint());
- assertEquals(48, descriptor.getUptime().longValue());
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(53470, (int) descriptor.getBandwidthObserved());
- assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
- descriptor.getExtraInfoDigest());
- assertEquals(Arrays.asList(new Integer[] {2}),
- descriptor.getHiddenServiceDirVersions());
- assertEquals("Random Person <nobody AT example dot com>",
- descriptor.getContact());
- assertEquals(Arrays.asList(new String[] {"reject *:*"}),
- descriptor.getExitPolicyLines());
- assertFalse(descriptor.isHibernating());
- assertNull(descriptor.getFamilyEntries());
- assertNull(descriptor.getReadHistory());
- assertNull(descriptor.getWriteHistory());
- assertFalse(descriptor.getUsesEnhancedDnsLogic());
- assertFalse(descriptor.getCachesExtraInfo());
- assertFalse(descriptor.getAllowSingleHopExits());
- assertTrue(descriptor.getUnrecognizedLines().isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterLineMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine(null);
- }
-
- @Test()
- public void testRouterOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithRouterLine("opt router saberrider2008 "
- + "94.134.192.243 9001 0 0");
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- assertEquals(9001, (int) descriptor.getOrPort());
- assertEquals(0, (int) descriptor.getSocksPort());
- assertEquals(0, (int) descriptor.getDirPort());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterLinePrecedingHibernatingLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("hibernating 1\nrouter "
- + "saberrider2008 94.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router 94.134.192.243 9001 "
- + "0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameInvalidChar() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router $aberrider2008 "
- + "94.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNicknameTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router "
- + "saberrider2008ReallyLongNickname 94.134.192.243 9001 0 0");
- }
-
- @Test()
- public void testNicknameTwoSpaces() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 9001 0 0");
- assertEquals("saberrider2008", descriptor.getNickname());
- assertEquals("94.134.192.243", descriptor.getAddress());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress24() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192/24 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddress294() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "294.134.192.243 9001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAddressMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 9001 "
- + "0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPort99001() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 99001 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortOne() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 one 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOrPortNewline() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 0\n 0 0");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testDirPortMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithRouterLine("router saberrider2008 "
- + "94.134.192.243 9001 0 ");
- }
-
- @Test()
- public void testPlatformMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine(null);
- assertNull(descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("opt platform Tor 0.2.2.35 "
- + "(git-b04388f9e7546a9f) on Linux i686");
- assertEquals("Tor 0.2.2.35 (git-b04388f9e7546a9f) on Linux i686",
- descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformNoSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("platform");
- assertEquals("", descriptor.getPlatform());
- }
-
- @Test()
- public void testPlatformSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPlatformLine("platform ");
- assertEquals("", descriptor.getPlatform());
- }
-
- @Test()
- public void testProtocolsNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithProtocolsLine("protocols Link 1 2 Circuit 1");
- assertEquals(Arrays.asList(new Integer[] {1, 2}),
- descriptor.getLinkProtocolVersions());
- assertEquals(Arrays.asList(new Integer[] {1}),
- descriptor.getCircuitProtocolVersions());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testProtocolsAB() throws DescriptorParseException {
- DescriptorBuilder.createWithProtocolsLine("opt protocols Link A B "
- + "Circuit 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testProtocolsNoCircuitVersions()
- throws DescriptorParseException {
- DescriptorBuilder.createWithProtocolsLine("opt protocols Link 1 2");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine(null);
- }
-
- @Test()
- public void testPublishedOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-01-01 04:03:19");
- assertEquals(1325390599000L, descriptor.getPublishedMillis());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished2039() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2039-01-01 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublished1912() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 1912-01-01 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedFeb31() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2012-02-31 "
- + "04:03:19");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testPublishedNoTime() throws DescriptorParseException {
- DescriptorBuilder.createWithPublishedLine("published 2012-01-01");
- }
-
- @Test()
- public void testPublishedMillis() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithPublishedLine("opt published 2012-01-01 04:03:19.123");
- assertEquals(1325390599000L, descriptor.getPublishedMillis());
- }
-
- @Test()
- public void testFingerprintNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFingerprintLine("fingerprint D873 3048 FC8E C910 2466 "
- + "AD8F 3098 622B F1BF 71FD");
- assertEquals("D8733048FC8EC9102466AD8F3098622BF1BF71FD",
- descriptor.getFingerprint());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintG() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint G873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooShort() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintTooLong() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint D873 "
- + "3048 FC8E C910 2466 AD8F 3098 622B F1BF 71FD D873");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFingerprintNoSpaces() throws DescriptorParseException {
- DescriptorBuilder.createWithFingerprintLine("opt fingerprint "
- + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- }
-
- @Test()
- public void testUptimeMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUptimeLine(null);
- assertNull(descriptor.getUptime());
- }
-
- @Test()
- public void testUptimeOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUptimeLine("opt uptime 48");
- assertEquals(48, descriptor.getUptime().longValue());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeFourtyEight() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime fourty-eight");
- }
-
- @Test()
- public void testUptimeMinusOne() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime -1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeNoSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUptimeFourEight() throws DescriptorParseException {
- DescriptorBuilder.createWithUptimeLine("uptime 4 8");
- }
-
- @Test()
- public void testBandwidthOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithBandwidthLine("opt bandwidth 51200 51200 53470");
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(53470, (int) descriptor.getBandwidthObserved());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine(null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthOneValue() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth 51200");
- }
-
- @Test()
- public void testBandwidthTwoValues() throws DescriptorParseException {
- /* This is allowed, because Tor versions 0.0.8 and older only wrote
- * bandwidth lines with rate and burst values, but no observed
- * value. */
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithBandwidthLine("bandwidth 51200 51200");
- assertEquals(51200, (int) descriptor.getBandwidthRate());
- assertEquals(51200, (int) descriptor.getBandwidthBurst());
- assertEquals(-1, (int) descriptor.getBandwidthObserved());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthFourValues() throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth 51200 51200 "
- + "53470 53470");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testBandwidthMinusOneTwoThree()
- throws DescriptorParseException {
- DescriptorBuilder.createWithBandwidthLine("bandwidth -1 -2 -3");
- }
-
- @Test()
- public void testExtraInfoDigestNoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine("extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B74");
- assertEquals("1469D1550738A25B1E7B47CDDBCD7B2899F51B74",
- descriptor.getExtraInfoDigest());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestNoSpace()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestTooShort()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest 1469D1550738A25B1E7B47CDDBCD7B2899F5");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExtraInfoDigestTooLong()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExtraInfoDigestLine("opt "
- + "extra-info-digest "
- + "1469D1550738A25B1E7B47CDDBCD7B2899F51B741469");
- }
-
- @Test()
- public void testExtraInfoDigestMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine(null);
- assertNull(descriptor.getExtraInfoDigest());
- }
-
- @Test()
- public void testExtraInfoDigestAdditionalDigest()
- throws DescriptorParseException {
- String extraInfoDigest = "0879DB7B765218D7B3AE7557669D20307BB21CAA";
- String additionalExtraInfoDigest =
- "V609l+N6ActBveebfNbH5lQ6wHDNstDkFgyqEhBHwtA";
- String extraInfoDigestLine = String.format("extra-info-digest %s %s",
- extraInfoDigest, additionalExtraInfoDigest);
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExtraInfoDigestLine(extraInfoDigestLine);
- assertEquals(extraInfoDigest, descriptor.getExtraInfoDigest());
- }
-
- @Test()
- public void testOnionKeyOpt() throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyLines("opt onion-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ1U4V9SeiKooSo5Bp"
- + "PL\no3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3olIynCI4QryfCE"
- + "uC3cTF\n9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKFfacOkpAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----");
- }
-
- @Test()
- public void testSigningKeyOpt() throws DescriptorParseException {
- DescriptorBuilder.createWithSigningKeyLines("opt signing-key\n"
- + "-----BEGIN RSA PUBLIC KEY-----\n"
- + "MIGJAoGBALMm3r3QDh482Ewe6Ub9wvRIfmEkoNX6q5cEAtQRNHSDcNx41gjELb"
- + "cl\nEniVMParBYACKfOxkS+mTTnIRDKVNEJTsDOwryNrc4X9JnPc/nn6ymYPiN"
- + "DhUROG\n8URDIhQoixcUeyyrVB8sxliSstKimulGnB7xpjYOlO8JKaHLNL4TAg"
- + "MBAAE=\n"
- + "-----END RSA PUBLIC KEY-----");
- }
-
- @Test()
- public void testHiddenServiceDirMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine(null);
- assertNull(descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testHiddenServiceDirNoOpt()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine("hidden-service-dir");
- assertEquals(Arrays.asList(new Integer[] {2}),
- descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testHiddenServiceDirVersions2And3()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHiddenServiceDirLine("hidden-service-dir 2 3");
- assertEquals(Arrays.asList(new Integer[] {2, 3}),
- descriptor.getHiddenServiceDirVersions());
- }
-
- @Test()
- public void testContactMissing() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine(null);
- assertNull(descriptor.getContact());
- }
-
- @Test()
- public void testContactOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("opt contact Random Person");
- assertEquals("Random Person", descriptor.getContact());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testContactDuplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithContactLine("contact Random "
- + "Person\ncontact Random Person");
- }
-
- @Test()
- public void testContactNoSpace() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("contact");
- assertEquals("", descriptor.getContact());
- }
-
- @Test()
- public void testContactCarriageReturn()
- throws DescriptorParseException {
- String contactString = "Random "
- + "Person -----BEGIN PGP PUBLIC KEY BLOCK-----\r"
- + "Version: GnuPG v1 dot 4 dot 7 (Darwin)\r\r"
- + "mQGiBEbb0rcRBADqBiUXsmtpJifh74irNnkHbhKMj8O4TqenaZYhdjLWouZsZd"
- + "07\rmTQoP40G4zqOrVEOOcXpdSiRnHWJYfgTnkibNZrOZEZLn3H1ywpovEgESm"
- + "oGEdAX\roid3XuIYRpRnqoafbFg9sg+OofX/mGrO+5ACfagQ9rlfx2oxCWijYw"
- + "pYFRk3NhCY=\r=Xaw3\r-----END PGP PUBLIC KEY BLOCK-----";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithContactLine("contact " + contactString);
- assertEquals(contactString, descriptor.getContact());
- }
-
- @Test()
- public void testExitPolicyRejectAllAcceptAll()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("reject *:*\naccept *:*");
- assertEquals(Arrays.asList(new String[] {"reject *:*", "accept *:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test()
- public void testExitPolicyOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("opt reject *:*");
- assertEquals(Arrays.asList(new String[] {"reject *:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyNoPort() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject *");
- }
-
- @Test()
- public void testExitPolicyAccept80RejectAll()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("accept *:80\nreject *:*");
- assertEquals(Arrays.asList(new String[] {"accept *:80",
- "reject *:*"}), descriptor.getExitPolicyLines());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyReject321() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject "
- + "123.123.123.321:80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyRejectPort66666()
- throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("reject *:66666");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyProjectAll() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines("project *:*");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testExitPolicyMissing() throws DescriptorParseException {
- DescriptorBuilder.createWithExitPolicyLines(null);
- }
-
- @Test()
- public void testExitPolicyMaskTypes() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithExitPolicyLines("reject 192.168.0.0/16:*\n"
- + "reject 94.134.192.243/255.255.255.0:*");
- assertEquals(Arrays.asList(new String[] { "reject 192.168.0.0/16:*",
- "reject 94.134.192.243/255.255.255.0:*"}),
- descriptor.getExitPolicyLines());
- }
-
- @Test()
- public void testRouterSignatureOpt()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("opt "
- + "router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "crypto lines are ignored anyway\n"
- + "-----END SIGNATURE-----");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testRouterSignatureNotLastLine()
- throws DescriptorParseException {
- DescriptorBuilder.createWithRouterSignatureLines("router-signature\n"
- + "-----BEGIN SIGNATURE-----\n"
- + "o4j+kH8UQfjBwepUnr99v0ebN8RpzHJ/lqYsTojXHy9kMr1RNI9IDeSzA7PSqT"
- + "uV\n4PL8QsGtlfwthtIoZpB2srZeyN/mcpA9fa1JXUrt/UN9K/+32Cyaad7h0n"
- + "HE6Xfb\njqpXDpnBpvk4zjmzjjKYnIsUWTnADmu0fo3xTRqXi7g=\n"
- + "-----END SIGNATURE-----\ncontact me");
- }
-
- @Test()
- public void testHibernatingOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("opt hibernating 1");
- assertTrue(descriptor.isHibernating());
- }
-
- @Test()
- public void testHibernatingFalse() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("hibernating 0");
- assertFalse(descriptor.isHibernating());
- }
-
- @Test()
- public void testHibernatingTrue() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithHibernatingLine("hibernating 1");
- assertTrue(descriptor.isHibernating());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHibernatingYep() throws DescriptorParseException {
- DescriptorBuilder.createWithHibernatingLine("hibernating yep");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testHibernatingNoSpace() throws DescriptorParseException {
- DescriptorBuilder.createWithHibernatingLine("hibernating");
- }
-
- @Test()
- public void testFamilyOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("opt family saberrider2008");
- assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyFingerprint() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- assertEquals(Arrays.asList(new String[] {
- "$D8733048FC8EC9102466AD8F3098622BF1BF71FD"}),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyNickname() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family saberrider2008");
- assertEquals(Arrays.asList(new String[] {"saberrider2008"}),
- descriptor.getFamilyEntries());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyDuplicate() throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family "
- + "saberrider2008\nfamily saberrider2008");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyNicknamePrefix() throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family $saberrider2008");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testFamilyFingerprintNoPrefix()
- throws DescriptorParseException {
- DescriptorBuilder.createWithFamilyLine("family "
- + "D8733048FC8EC9102466AD8F3098622BF1BF71FD");
- }
-
- @Test()
- public void testFamilyFingerprintNicknameNamed()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008");
- assertEquals(Arrays.asList(new String[]
- { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD=saberrider2008" }),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testFamilyFingerprintNicknameUnnamed()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithFamilyLine("family "
- + "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008");
- assertEquals(Arrays.asList(new String[]
- { "$D8733048FC8EC9102466AD8F3098622BF1BF71FD~saberrider2008" }),
- descriptor.getFamilyEntries());
- }
-
- @Test()
- public void testWriteHistory() throws DescriptorParseException {
- String writeHistoryLine = "write-history 2012-01-01 03:51:44 (900 s) "
- + "4345856,261120,7591936,1748992";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine(writeHistoryLine);
- assertNotNull(descriptor.getWriteHistory());
- BandwidthHistory parsedWriteHistory = descriptor.getWriteHistory();
- assertEquals(writeHistoryLine, parsedWriteHistory.getLine());
- assertEquals(1325389904000L, (long) parsedWriteHistory.
- getHistoryEndMillis());
- assertEquals(900L, (long) parsedWriteHistory.getIntervalLength());
- SortedMap<Long, Long> bandwidthValues = parsedWriteHistory.
- getBandwidthValues();
- assertEquals(4345856L, (long) bandwidthValues.remove(1325387204000L));
- assertEquals(261120L, (long) bandwidthValues.remove(1325388104000L));
- assertEquals(7591936L, (long) bandwidthValues.remove(1325389004000L));
- assertEquals(1748992L, (long) bandwidthValues.remove(1325389904000L));
- assertTrue(bandwidthValues.isEmpty());
- }
-
- @Test()
- public void testWriteHistoryOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("opt write-history 2012-01-01 "
- + "03:51:44 (900 s) 4345856,261120,7591936,1748992");
- assertNotNull(descriptor.getWriteHistory());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistory3012() throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "3012-01-01 03:51:44 (900 s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoSeconds()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51 (900 s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoParathenses()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 900 s 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoSpaceSeconds()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900s) 4345856,261120,7591936,1748992");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryTrailingComma()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) 4345856,261120,7591936,");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryOneTwoThree()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) one,two,three");
- }
-
- @Test()
- public void testWriteHistoryNoValuesSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(900 s) ");
- assertEquals(900, (long) descriptor.getWriteHistory().
- getIntervalLength());
- assertTrue(descriptor.getWriteHistory().getBandwidthValues().
- isEmpty());
- }
-
- @Test()
- public void testWriteHistoryNoValuesNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(900 s)");
- assertEquals(900, (long) descriptor.getWriteHistory().
- getIntervalLength());
- assertTrue(descriptor.getWriteHistory().getBandwidthValues().
- isEmpty());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryNoS() throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine(
- "write-history 2012-01-01 03:51:44 (900 ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testWriteHistoryTrailingNumber()
- throws DescriptorParseException {
- DescriptorBuilder.createWithWriteHistoryLine("write-history "
- + "2012-01-01 03:51:44 (900 s) 4345856 1");
- }
-
- @Test()
- public void testWriteHistory1800Seconds()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithWriteHistoryLine("write-history 2012-01-01 03:51:44 "
- + "(1800 s) 4345856");
- assertEquals(1800L, (long) descriptor.getWriteHistory().
- getIntervalLength());
- }
-
- @Test()
- public void testReadHistory() throws DescriptorParseException {
- String readHistoryLine = "read-history 2012-01-01 03:51:44 (900 s) "
- + "4268032,139264,7797760,1415168";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithReadHistoryLine(readHistoryLine);
- assertNotNull(descriptor.getReadHistory());
- BandwidthHistory parsedReadHistory = descriptor.getReadHistory();
- assertEquals(readHistoryLine, parsedReadHistory.getLine());
- assertEquals(1325389904000L, (long) parsedReadHistory.
- getHistoryEndMillis());
- assertEquals(900L, (long) parsedReadHistory.getIntervalLength());
- SortedMap<Long, Long> bandwidthValues = parsedReadHistory.
- getBandwidthValues();
- assertEquals(4268032L, (long) bandwidthValues.remove(1325387204000L));
- assertEquals(139264L, (long) bandwidthValues.remove(1325388104000L));
- assertEquals(7797760L, (long) bandwidthValues.remove(1325389004000L));
- assertEquals(1415168L, (long) bandwidthValues.remove(1325389904000L));
- assertTrue(bandwidthValues.isEmpty());
- }
-
- @Test()
- public void testReadHistoryTwoSpaces() throws DescriptorParseException {
- /* There are some server descriptors from older Tor versions that
- * contain "opt read-history " lines. */
- String readHistoryLine = "opt read-history 2012-01-01 03:51:44 "
- + "(900 s) 4268032,139264,7797760,1415168";
- DescriptorBuilder.createWithReadHistoryLine(readHistoryLine);
- }
-
- @Test()
- public void testEventdnsOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("opt eventdns 1");
- assertTrue(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test()
- public void testEventdns1() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("eventdns 1");
- assertTrue(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test()
- public void testEventdns0() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithEventdnsLine("eventdns 0");
- assertFalse(descriptor.getUsesEnhancedDnsLogic());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEventdnsTrue() throws DescriptorParseException {
- DescriptorBuilder.createWithEventdnsLine("eventdns true");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEventdnsNo() throws DescriptorParseException {
- DescriptorBuilder.createWithEventdnsLine("eventdns no");
- }
-
- @Test()
- public void testCachesExtraInfoOpt() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithCachesExtraInfoLine("opt caches-extra-info");
- assertTrue(descriptor.getCachesExtraInfo());
- }
-
- @Test()
- public void testCachesExtraInfoNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithCachesExtraInfoLine("caches-extra-info");
- assertTrue(descriptor.getCachesExtraInfo());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testCachesExtraInfoTrue() throws DescriptorParseException {
- DescriptorBuilder.createWithCachesExtraInfoLine("caches-extra-info "
- + "true");
- }
-
- @Test()
- public void testAllowSingleHopExitsOpt()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithAllowSingleHopExitsLine("opt allow-single-hop-exits");
- assertTrue(descriptor.getAllowSingleHopExits());
- }
-
- @Test()
- public void testAllowSingleHopExitsNoSpace()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithAllowSingleHopExitsLine("allow-single-hop-exits");
- assertTrue(descriptor.getAllowSingleHopExits());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAllowSingleHopExitsTrue()
- throws DescriptorParseException {
- DescriptorBuilder.createWithAllowSingleHopExitsLine(
- "allow-single-hop-exits true");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testAllowSingleHopExitsNonAsciiKeyword()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNonAsciiLineBytes(new byte[] {
- 0x14, (byte) 0xfe, 0x18, // non-ascii chars
- 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x2d, // "allow-"
- 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2d, // "single-"
- 0x68, 0x6f, 0x70, 0x2d, // "hop-"
- 0x65, 0x78, 0x69, 0x74, 0x73 }, // "exits" (no newline)
- false);
- }
-
- @Test()
- public void testIpv6PolicyLine() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithIpv6PolicyLine("ipv6-policy accept 80,1194,1220,1293");
- assertEquals("accept", descriptor.getIpv6DefaultPolicy());
- assertEquals("80,1194,1220,1293", descriptor.getIpv6PortList());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPolicy()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPorts()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy accept");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineNoPolicyNoPorts()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testIpv6PolicyLineProject()
- throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine("ipv6-policy project 80");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoIpv6PolicyLines() throws DescriptorParseException {
- DescriptorBuilder.createWithIpv6PolicyLine(
- "ipv6-policy accept 80,1194,1220,1293\n"
- + "ipv6-policy accept 80,1194,1220,1293");
- }
-
- @Test()
- public void testNtorOnionKeyLine() throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY=");
- assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
- descriptor.getNtorOnionKey());
- }
-
- @Test()
- public void testNtorOnionKeyLineNoPadding()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
- assertEquals("Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY",
- descriptor.getNtorOnionKey());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyLineNoKey()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key ");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyLineTwoKeys()
- throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTwoNtorOnionKeyLines() throws DescriptorParseException {
- DescriptorBuilder.createWithNtorOnionKeyLine("ntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\nntor-onion-key "
- + "Y/XgaHcPIJVa4D55kir9QLH8rEYAaLXuv3c3sm8jYhY\n");
- }
-
- @Test()
- public void testTunnelledDirServerTrue()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder
- .createWithTunnelledDirServerLine("tunnelled-dir-server");
- assertTrue(descriptor.getTunnelledDirServer());
- }
-
- @Test()
- public void testTunnelledDirServerFalse()
- throws DescriptorParseException {
- ServerDescriptor descriptor = DescriptorBuilder
- .createWithTunnelledDirServerLine(null);
- assertFalse(descriptor.getTunnelledDirServer());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerTypo()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunneled-dir-server");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerTwice()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunnelled-dir-server\ntunnelled-dir-server");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testTunnelledDirServerArgs()
- throws DescriptorParseException {
- DescriptorBuilder.createWithTunnelledDirServerLine(
- "tunnelled-dir-server 1");
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testUnrecognizedLineFail()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- DescriptorBuilder.createWithUnrecognizedLine(unrecognizedLine, true);
- }
-
- @Test()
- public void testUnrecognizedLineIgnore()
- throws DescriptorParseException {
- String unrecognizedLine = "unrecognized-line 1";
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(unrecognizedLine, false);
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add(unrecognizedLine);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- @Test()
- public void testSomeOtherKey() throws DescriptorParseException {
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add("some-other-key");
- unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
- unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
- + "1U4V9SeiKooSo5BpPL");
- unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
- + "IynCI4QryfCEuC3cTF");
- unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
- + "facOkpAgMBAAE=");
- unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
- StringBuilder sb = new StringBuilder();
- for (String line : unrecognizedLines) {
- sb.append("\n").append(line);
- }
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(sb.toString().substring(1), false);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- @Test()
- public void testUnrecognizedCryptoBlockNoKeyword()
- throws DescriptorParseException {
- List<String> unrecognizedLines = new ArrayList<>();
- unrecognizedLines.add("-----BEGIN RSA PUBLIC KEY-----");
- unrecognizedLines.add("MIGJAoGBAKM+iiHhO6eHsvd6Xjws9z9EQB1V/Bpuy5ciGJ"
- + "1U4V9SeiKooSo5BpPL");
- unrecognizedLines.add("o3XT+6PIgzl3R6uycjS3Ejk47vLEJdcVTm/VG6E0ppu3ol"
- + "IynCI4QryfCEuC3cTF");
- unrecognizedLines.add("9wE4WXY4nX7w0RTN18UVLxrt1A9PP0cobFNiPs9rzJCbKF"
- + "facOkpAgMBAAE=");
- unrecognizedLines.add("-----END RSA PUBLIC KEY-----");
- StringBuilder sb = new StringBuilder();
- for (String line : unrecognizedLines) {
- sb.append("\n").append(line);
- }
- ServerDescriptor descriptor = DescriptorBuilder.
- createWithUnrecognizedLine(sb.toString().substring(1), false);
- assertEquals(unrecognizedLines, descriptor.getUnrecognizedLines());
- }
-
- private static final String IDENTITY_ED25519_LINES =
- "identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQQABiX1AVGv5BuzJroQXbOh6vv1nbwc5rh2S13PyRFuLhTiifK4AQAgBACBCMwr"
- + "\n4qgIlFDIzoC9ieJOtSkwrK+yXJPKlP8ojvgkx8cGKvhokOwA1eYDombzfwHcJ1"
- + "EV\nbhEn/6g8i7wzO3LoqefIUrSAeEExOAOmm5mNmUIzL8EtnT6JHCr/sqUTUgA="
- + "\n"
- + "-----END ED25519 CERT-----";
-
- private static final String MASTER_KEY_ED25519_LINE =
- "master-key-ed25519 gQjMK+KoCJRQyM6AvYniTrUpMKyvslyTypT/KI74JMc";
-
- private static final String ROUTER_SIG_ED25519_LINE =
- "router-sig-ed25519 y7WF9T2GFwkSDPZEhB55HgquIFOl5uXUFMYJPq3CXXUTKeJ"
- + "kSrtaZUB5s34fWdHQNtl84mH4dVaFMunHnwgYAw";
-
- @Test()
- public void testEd25519() throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- assertEquals(IDENTITY_ED25519_LINES.substring(
- IDENTITY_ED25519_LINES.indexOf("\n") + 1),
- descriptor.getIdentityEd25519());
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- assertEquals(ROUTER_SIG_ED25519_LINE.substring(
- ROUTER_SIG_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getRouterSignatureEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityMasterKeyMismatch()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- "master-key-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519IdentityMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(null,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES + "\n"
- + IDENTITY_ED25519_LINES, MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519IdentityEmptyCrypto()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines("identity-ed25519\n"
- + "-----BEGIN ED25519 CERT-----\n-----END ED25519 CERT-----",
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519MasterKeyMissing()
- throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- null, ROUTER_SIG_ED25519_LINE);
- assertEquals(MASTER_KEY_ED25519_LINE.substring(
- MASTER_KEY_ED25519_LINE.indexOf(" ") + 1),
- descriptor.getMasterKeyEd25519());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519MasterKeyDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE + "\n" + MASTER_KEY_ED25519_LINE,
- ROUTER_SIG_ED25519_LINE);
- }
-
- @Test()
- public void testEd25519RouterSigMissing()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, null);
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testEd25519RouterSigDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithEd25519Lines(IDENTITY_ED25519_LINES,
- MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n"
- + ROUTER_SIG_ED25519_LINE);
- }
-
- private static final String ONION_KEY_CROSSCERT_LINES =
- "onion-key-crosscert\n"
- + "-----BEGIN CROSSCERT-----\n"
- + "gVWpiNgG2FekW1uonr4KKoqykjr4bqUBKGZfu6s9rvsV1TThnquZNP6ZhX2IPdQA"
- + "\nlfKtzFggGu/4BiJ5oTSDj2sK2DMjY3rjrMQZ3I/wJ25yhc9gxjqYqUYO9MmJwA"
- + "Lp\nfYkqp/t4WchJpyva/4hK8vITsI6eT2BfY/DWMy/suIE=\n"
- + "-----END CROSSCERT-----";
-
- private static final String NTOR_ONION_KEY_CROSSCERT_LINES =
- "ntor-onion-key-crosscert 1\n"
- + "-----BEGIN ED25519 CERT-----\n"
- + "AQoABiUeAdauu1MxYGMmGLTCPaoes0RvW7udeLc1t8LZ4P3CDo5bAN4nrRfbCfOt"
- + "\nz2Nwqn8tER1a+Ry6Vs+ilMZA55Rag4+f6Zdb1fmHWknCxbQlLHpqHACMtemPda"
- + "Ka\nErPtMuiEqAc=\n"
- + "-----END ED25519 CERT-----";
-
- @Test()
- public void testOnionKeyCrosscert() throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- ONION_KEY_CROSSCERT_LINES);
- assertEquals(ONION_KEY_CROSSCERT_LINES.substring(
- ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
- descriptor.getOnionKeyCrosscert());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testOnionKeyCrosscertDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- ONION_KEY_CROSSCERT_LINES + "\n" + ONION_KEY_CROSSCERT_LINES);
- }
-
- @Test()
- public void testNtorOnionKeyCrosscert()
- throws DescriptorParseException {
- ServerDescriptor descriptor =
- DescriptorBuilder.createWithNtorOnionKeyCrosscertLines(
- NTOR_ONION_KEY_CROSSCERT_LINES);
- assertEquals(NTOR_ONION_KEY_CROSSCERT_LINES.substring(
- NTOR_ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1),
- descriptor.getNtorOnionKeyCrosscert());
- assertEquals(1, descriptor.getNtorOnionKeyCrosscertSign());
- }
-
- @Test(expected = DescriptorParseException.class)
- public void testNtorOnionKeyCrosscertDuplicate()
- throws DescriptorParseException {
- DescriptorBuilder.createWithOnionKeyCrosscertLines(
- NTOR_ONION_KEY_CROSSCERT_LINES + "\n"
- + NTOR_ONION_KEY_CROSSCERT_LINES);
- }
-}
-
diff --git a/test/org/torproject/descriptor/impl/TorperfResultImplTest.java b/test/org/torproject/descriptor/impl/TorperfResultImplTest.java
deleted file mode 100644
index b5cde0a..0000000
--- a/test/org/torproject/descriptor/impl/TorperfResultImplTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* Copyright 2015 The Tor Project
- * See LICENSE for licensing information */
-package org.torproject.descriptor.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.util.List;
-
-import org.junit.Test;
-import org.torproject.descriptor.Descriptor;
-
-public class TorperfResultImplTest {
-
- @Test()
- public void testAnnotatedInput() throws Exception{
- TorperfResultImpl result = (TorperfResultImpl)
- (TorperfResultImpl.parseTorperfResults((torperfAnnotation + input)
- .getBytes("US-ASCII"), false).get(0));
- assertEquals("Expected one annotation.", 1,
- result.getAnnotations().size());
- assertEquals(torperfAnnotation.substring(0, 17),
- result.getAnnotations().get(0));
- int count = 0;
- for (Long l: result.getDataPercentiles().values()) {
- assertNotNull(l);
- assertEquals(l.longValue(), deciles[count++]);
- }
- }
-
- @Test()
- public void testPartiallyAnnotatedInput() throws Exception{
- byte[] asciiBytes = (torperfAnnotation
- + input + input + input).getBytes("US-ASCII");
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- asciiBytes, false);
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
- assertEquals(3, result.size());
- assertEquals("Expected zero annotations.", 0,
- ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
- assertEquals("Expected zero annotations.", 0,
- ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
- }
-
- @Test()
- public void testAllAnnotatedInput() throws Exception {
- byte[] asciiBytes = (torperfAnnotation + input
- + torperfAnnotation + input
- + torperfAnnotation + input).getBytes("US-ASCII");
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- asciiBytes, false);
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(0))).getAnnotations().size());
- assertEquals(3, result.size());
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(1))).getAnnotations().size());
- assertEquals("Expected one annotation.", 1,
- ((TorperfResultImpl)(result.get(2))).getAnnotations().size());
- }
-
- private static long[] deciles = new long[] {
- 1441065602980L, 1441065603030L, 1441065603090L, 1441065603120L,
- 1441065603230L, 1441065603250L, 1441065603310L, 1441065603370L,
- 1441065603370L };
-
- private static final String torperfAnnotation = "@type torperf 1.0\n";
-
- private static final String input =
- "BUILDTIMES=0.872834920883,1.09103679657,1.49180984497 "
- + "CIRC_ID=1228 CONNECT=1441065601.86 DATACOMPLETE=1441065603.39 "
- + "DATAPERC10=1441065602.98 DATAPERC20=1441065603.03 "
- + "DATAPERC30=1441065603.09 DATAPERC40=1441065603.12 "
- + "DATAPERC50=1441065603.23 DATAPERC60=1441065603.25 "
- + "DATAPERC70=1441065603.31 DATAPERC80=1441065603.37 "
- + "DATAPERC90=1441065603.37 DATAREQUEST=1441065602.38 "
- + "DATARESPONSE=1441065602.84 DIDTIMEOUT=0 FILESIZE=51200 "
- + "LAUNCH=1441065361.30 NEGOTIATE=1441065601.86 "
- + "PATH=$C4C9C332D25B3546BEF4E1250CF410E97EF996E6,"
- + "$C43FA6474A9F071E9120DF63ED6EB8FDBA105234,"
- + "$7C0AA4E3B73E407E9F5FEB1912F8BE26D8AA124D QUANTILE=0.800000 "
- + "READBYTES=51416 REQUEST=1441065601.86 RESPONSE=1441065602.38 "
- + "SOCKET=1441065601.86 SOURCE=moria START=1441065601.86 "
- + "TIMEOUT=1500 USED_AT=1441065603.40 USED_BY=2475 WRITEBYTES=75\n";
-
- @Test()
- public void testDatapercNonNumeric() throws Exception {
- List<Descriptor> result = TorperfResultImpl.parseTorperfResults(
- ("DATAPERMILLE=2.0 " + input).getBytes(), false);
- assertEquals(1, result.size());
- TorperfResultImpl torperfResult = (TorperfResultImpl) result.get(0);
- assertEquals(1, torperfResult.getUnrecognizedKeys().size());
- assertEquals("DATAPERMILLE",
- torperfResult.getUnrecognizedKeys().firstKey());
- }
-}
-
1
0
[metrics-lib/master] Adapt the branch rate to 42% for cobertura check.
by karsten@torproject.org 06 Jul '16
by karsten@torproject.org 06 Jul '16
06 Jul '16
commit 304e72a2ac5bdd992cd09586bafe16c60cc7bec5
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Wed Jul 6 09:02:27 2016 +0200
Adapt the branch rate to 42% for cobertura check.
Suggested by iwakeh on #19571.
---
build.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.xml b/build.xml
index 3f62199..86c8ee8 100644
--- a/build.xml
+++ b/build.xml
@@ -253,7 +253,7 @@
<include name="**/*.java" />
</fileset>
</cobertura-report>
- <cobertura-check branchrate="0" totallinerate="31" totalbranchrate="41" >
+ <cobertura-check branchrate="0" totallinerate="31" totalbranchrate="42" >
<regex pattern="org.torproject.descriptor.benchmark.*" branchrate="0" linerate="0"/>
</cobertura-check>
</target>
1
0
[metrics-lib/master] Added test and bugfix for RelayNetworkStatusImpl.
by karsten@torproject.org 06 Jul '16
by karsten@torproject.org 06 Jul '16
06 Jul '16
commit 0f367a046ec49eebe2b356ff01850eca68b2245e
Author: iwakeh <iwakeh(a)torproject.org>
Date: Tue Jul 5 22:20:59 2016 +0000
Added test and bugfix for RelayNetworkStatusImpl.
---
.../descriptor/impl/BridgePoolAssignmentImpl.java | 1 -
.../descriptor/impl/RelayNetworkStatusImpl.java | 3 +-
.../impl/RelayNetworkStatusImplTest.java | 53 ++++++++++++++++++++++
3 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
index aaef048..75451a2 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
@@ -15,7 +15,6 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-/* TODO Write a test class. */
public class BridgePoolAssignmentImpl extends DescriptorImpl
implements BridgePoolAssignment {
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
index 666e879..1807b33 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
@@ -20,8 +20,6 @@ import java.util.TreeSet;
import javax.xml.bind.DatatypeConverter;
-/* TODO Write unit tests. */
-
public class RelayNetworkStatusImpl extends NetworkStatusImpl
implements RelayNetworkStatus {
@@ -142,6 +140,7 @@ public class RelayNetworkStatusImpl extends NetworkStatusImpl
+ "block in v2 network status.");
}
nextCrypto = "";
+ break;
default:
if (crypto != null) {
crypto.append(line).append("\n");
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusImplTest.java
new file mode 100644
index 0000000..636c7d9
--- /dev/null
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusImplTest.java
@@ -0,0 +1,53 @@
+/* Copyright 2016 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import org.torproject.descriptor.DescriptorParseException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.RelayNetworkStatus;
+
+public class RelayNetworkStatusImplTest {
+
+ private final static String validHeader = "network-status-version 2\n"
+ + "dir-source 194.109.206.212 194.109.206.212 80\n"
+ + "fingerprint 7EA6EAD6FD83083C538F44038BBFA077587DD755\n"
+ + "contact 1024R/8D56913D Alex de Joode <adejoode(a)sabotage.org>\n"
+ + "published 2012-03-01 00:10:43\n"
+ + "dir-options\n"
+ + "dir-signing-key\n"
+ + "-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MIGJAoGBAL7QJ6cmXhMlexV97ehnV5hn5ePOeo0sbDYXhlfw52CheEycoUqSD9Y/\n"
+ + "3qEo0Rm7XTEol0dRW34ca1LMIXGM4B4whXxBKCRRYe1RY6nF70zb2EUuaHWEWc+f\n"
+ + "c6JWYUWZSPpW1uyjyLPUI/ikyyH7zmtR4MfhSeNdt2zSakojYNaPAgMBAAE=\n"
+ + "-----END RSA PUBLIC KEY-----\n";
+
+ private final static String validStatus =
+ "@type network-status-2 1.0\n" + validHeader;
+
+ @Test(expected = DescriptorParseException.class)
+ public void testParseBrokenHeader() throws DescriptorParseException {
+ RelayNetworkStatusImpl rnsi
+ = new RelayNetworkStatusImpl(validStatus.getBytes(), true);
+ rnsi.parseHeader("network-status-version 2\nxyx\nabc".getBytes());
+ }
+
+ @Test()
+ public void testValidHeader() throws DescriptorParseException {
+ RelayNetworkStatusImpl rnsi
+ = new RelayNetworkStatusImpl(validStatus.getBytes(), true);
+ rnsi.parseHeader(validHeader.getBytes());
+ assertEquals(rnsi.getContactLine(),
+ "1024R/8D56913D Alex de Joode <adejoode(a)sabotage.org>");
+ }
+
+}
+
1
0
06 Jul '16
commit 7eff3cc0d908bb0962ad34d71e941e5b5f53c82e
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Tue Jul 5 15:58:42 2016 +0200
Update copyright notice to 2016.
---
LICENSE | 2 +-
src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java | 2 +-
.../org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/DescriptorParseException.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java | 2 +-
.../org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/DirectorySignatureImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java | 2 +-
.../java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/ExitListImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/ParseHelper.java | 2 +-
src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java | 2 +-
.../org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java | 2 +-
.../org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java | 2 +-
src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java | 2 +-
.../java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java | 2 +-
src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java | 2 +-
.../org/torproject/descriptor/impl/DescriptorCollectorImplTest.java | 2 +-
src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java | 2 +-
.../org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java | 2 +-
.../torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java | 2 +-
.../org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java | 2 +-
.../java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java | 2 +-
src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java | 2 +-
45 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/LICENSE b/LICENSE
index 9040894..72c1c0d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2011--2015 The Tor Project
+Copyright 2011--2016 The Tor Project
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
diff --git a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
index 0d998f6..fc0cae0 100644
--- a/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BandwidthHistoryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
index c9e0590..7ec395b 100644
--- a/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BlockingIteratorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
index 81d0623..24a4eda 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeExtraInfoDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
index 49abc5c..ab44452 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
index f28dcce..aaef048 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgePoolAssignmentImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
index f9e9ba8..67a6292 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeServerDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
index 5776827..fb6aa2e 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorCollectorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
index 637c80e..2c37740 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorDownloaderImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
index 479f855..56d10a5 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorFileImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
index a32b6cc..e16e1ee 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
index 9015471..22959c9 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParseException.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
index 24cbf15..d22ff59 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
index b77b3b1..e3c07e5 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorReaderImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
index f27796f..2ea4e77 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorRequestImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
index 2782925..2898f3d 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirSourceEntryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
index d3a707a..89cf721 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryDownloader.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
index d76b32c..28bc65d 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
index 771b632..b2fa705 100644
--- a/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
index fbad62d..ac4a0c9 100644
--- a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinator.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
index 0c31736..331e7d9 100644
--- a/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DownloadCoordinatorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
index e62fb91..1f92afd 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListEntryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
index ab3e516..6470130 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExitListImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
index f67c793..586d385 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
index 7f07da9..c1f607b 100644
--- a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2014--2015 The Tor Project
+/* Copyright 2014--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index 6ffaf62..b21bb60 100644
--- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
index 4c99aca..c458961 100644
--- a/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
index a40a351..fb57e93 100644
--- a/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/main/java/org/torproject/descriptor/impl/ParseHelper.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
index 1df9c55..69709ad 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayDirectoryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
index 6fe3fc6..246978c 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayExtraInfoDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index eae3ace..87971a4 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
index 2ea9fdf..666e879 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index a9bb928..8735017 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
index b40423d..c1f9b0c 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayServerDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
index 1ada050..f7f896a 100644
--- a/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RouterStatusEntryImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
index 47d1b5b..c75d5a0 100644
--- a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
index 0847e13..1cac579 100644
--- a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
index 29a2d47..7421b8a 100644
--- a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
+++ b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
index fde8e57..4355888 100644
--- a/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/DescriptorCollectorImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
index a563857..4458451 100644
--- a/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ExitListImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
index 6843196..5556a8b 100644
--- a/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
index d864337..ebe37c9 100644
--- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
index 1c840f5..1695a2a 100644
--- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2011--2015 The Tor Project
+/* Copyright 2011--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
index cd3f1a5..2d93266 100644
--- a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2012--2015 The Tor Project
+/* Copyright 2012--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
diff --git a/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
index b5cde0a..9ade629 100644
--- a/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/TorperfResultImplTest.java
@@ -1,4 +1,4 @@
-/* Copyright 2015 The Tor Project
+/* Copyright 2015--2016 The Tor Project
* See LICENSE for licensing information */
package org.torproject.descriptor.impl;
1
0