commit 3eb59411781c4fa210979cb112b800c001a59f6c Author: Damian Johnson atagar@torproject.org Date: Tue Oct 10 09:38:43 2017 -0700
Parse bridge-distribution-request in server descriptors
Bending my usual policy of only adding parsing support once fields are in the spec due to an amusing circular dependency...
https://trac.torproject.org/projects/tor/ticket/21177#comment:5 --- docs/change_log.rst | 1 + stem/descriptor/server_descriptor.py | 35 ++++++++++++++++++++++++++++++- test/unit/descriptor/server_descriptor.py | 14 ++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 6e4bcbc6..012b544d 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -75,6 +75,7 @@ The following are only available within Stem's `git repository * Sped descriptor reading by ~25% by deferring defaulting when validating * Added server descriptor's new extra_info_sha256_digest attribute (:spec:`0f03581`) * Added server descriptor's new protocol attribute (:spec:`eb4fb3c`) + * Added server descriptor's new bridge_distribution attribute (:trac:`21177`) * Added extrainfo descriptor's new padding_counts attributes (:spec:`0803997`) * Shared randomness properties weren't being read in votes (:trac:`21102`) * Updated longclaw authority's address (:trac:`23592`) diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py index 8489b332..cfb3f710 100644 --- a/stem/descriptor/server_descriptor.py +++ b/stem/descriptor/server_descriptor.py @@ -29,6 +29,22 @@ etc). This information is provided from a few sources... |- digest - calculates the upper-case hex digest value for our content |- get_annotations - dictionary of content prior to the descriptor entry +- get_annotation_lines - lines that provided the annotations + +.. data:: BridgeDistribution (enum) + + Preferred method of distributing this relay if a bridge. + + .. versionadded:: 1.6.0 + + ===================== =========== + BridgeDistribution Description + ===================== =========== + **ANY** No proference, BridgeDB will pick how the bridge is distributed. + **HTTPS** Provided via the `web interface https://bridges.torproject.org`_. + **EMAIL** Provided in response to emails to bridges@torproject.org. + **MOAT** Provided in interactive menus within Tor Browser. + **HYPHAE** Provided via a cryptographic invitation-based system. + ===================== =========== """
import base64 @@ -42,6 +58,7 @@ import stem.descriptor.extrainfo_descriptor import stem.exit_policy import stem.prereq import stem.util.connection +import stem.util.enum import stem.util.str_tools import stem.util.tor_tools import stem.version @@ -101,6 +118,7 @@ SINGLE_FIELDS = ( 'read-history', 'write-history', 'eventdns', + 'bridge-distribution-request', 'family', 'caches-extra-info', 'extra-info-digest', @@ -115,8 +133,17 @@ SINGLE_FIELDS = ( 'router-sig-ed25519', )
+BridgeDistribution = stem.util.enum.Enum( + ('ANY', 'any'), + ('HTTPS', 'https'), + ('EMAIL', 'email'), + ('MOAT', 'moat'), + ('HYPHAE', 'hyphae'), +) + DEFAULT_IPV6_EXIT_POLICY = stem.exit_policy.MicroExitPolicy('reject 1-65535') REJECT_ALL_POLICY = stem.exit_policy.ExitPolicy('reject *:*') +DEFAULT_BRIDGE_DISTRIBUTION = 'any'
def _truncated_b64encode(content): @@ -423,6 +450,7 @@ _parse_allow_single_hop_exits_line = _parse_if_present('allow-single-hop-exits', _parse_tunneled_dir_server_line = _parse_if_present('tunnelled-dir-server', 'allow_tunneled_dir_requests') _parse_proto_line = _parse_protocol_line('proto', 'protocols') _parse_caches_extra_info_line = _parse_if_present('caches-extra-info', 'extra_info_cache') +_parse_bridge_distribution_request_line = _parse_simple_line('bridge-distribution-request', 'bridge_distribution') _parse_family_line = _parse_simple_line('family', 'family', func = lambda v: set(v.split(' '))) _parse_eventdns_line = _parse_simple_line('eventdns', 'eventdns', func = lambda v: v == '1') _parse_onion_key_line = _parse_key_block('onion-key', 'onion_key', 'RSA PUBLIC KEY') @@ -456,6 +484,8 @@ class ServerDescriptor(Descriptor): :var bytes contact: contact information :var stem.exit_policy.ExitPolicy exit_policy: ***** stated exit policy :var stem.exit_policy.MicroExitPolicy exit_policy_v6: ***** exit policy for IPv6 + :var BridgeDistribution bridge_distribution: ***** preferred method of providing this relay's + address if a bridge :var set family: ***** nicknames or fingerprints of declared family
:var int average_bandwidth: ***** average rate it's willing to relay in bytes/s @@ -495,7 +525,8 @@ class ServerDescriptor(Descriptor): Added the allow_tunneled_dir_requests attribute.
.. versionchanged:: 1.6.0 - Added the extra_info_sha256_digest and protocols attributes. + Added the extra_info_sha256_digest, protocols, and bridge_distribution + attributes. """
ATTRIBUTES = { @@ -515,6 +546,7 @@ class ServerDescriptor(Descriptor): 'operating_system': (None, _parse_platform_line), 'uptime': (None, _parse_uptime_line), 'exit_policy_v6': (DEFAULT_IPV6_EXIT_POLICY, _parse_ipv6_policy_line), + 'bridge_distribution': (DEFAULT_BRIDGE_DISTRIBUTION, _parse_bridge_distribution_request_line), 'family': (set(), _parse_family_line),
'average_bandwidth': (None, _parse_bandwidth_line), @@ -565,6 +597,7 @@ class ServerDescriptor(Descriptor): 'tunnelled-dir-server': _parse_tunneled_dir_server_line, 'proto': _parse_proto_line, 'caches-extra-info': _parse_caches_extra_info_line, + 'bridge-distribution-request': _parse_bridge_distribution_request_line, 'family': _parse_family_line, 'eventdns': _parse_eventdns_line, } diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py index 7bf0b2cf..f392b44f 100644 --- a/test/unit/descriptor/server_descriptor.py +++ b/test/unit/descriptor/server_descriptor.py @@ -21,7 +21,7 @@ import test.require
from stem.util import str_type from stem.descriptor.certificate import CertType, ExtensionType -from stem.descriptor.server_descriptor import RelayDescriptor, BridgeDescriptor +from stem.descriptor.server_descriptor import BridgeDistribution, RelayDescriptor, BridgeDescriptor
from test.unit.descriptor import ( get_resource, @@ -141,6 +141,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual('D225B728768D7EA4B5587C13A7A9D22EBBEE6E66', desc.extra_info_digest) self.assertEqual(None, desc.extra_info_sha256_digest) self.assertEqual(['2'], desc.hidden_service_dir) + self.assertEqual(BridgeDistribution.ANY, desc.bridge_distribution) self.assertEqual(expected_family, desc.family) self.assertEqual(153600, desc.average_bandwidth) self.assertEqual(256000, desc.burst_bandwidth) @@ -200,6 +201,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual(None, desc.extra_info_digest) self.assertEqual(None, desc.extra_info_sha256_digest) self.assertEqual(None, desc.hidden_service_dir) + self.assertEqual(BridgeDistribution.ANY, desc.bridge_distribution) self.assertEqual(set(), desc.family) self.assertEqual(102400, desc.average_bandwidth) self.assertEqual(10485760, desc.burst_bandwidth) @@ -248,6 +250,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual('56403D838DE152421CD401B8E57DAD4483A3D56B', desc.extra_info_digest) self.assertEqual(None, desc.extra_info_sha256_digest) self.assertEqual(['2'], desc.hidden_service_dir) + self.assertEqual(BridgeDistribution.ANY, desc.bridge_distribution) self.assertEqual(set(), desc.family) self.assertEqual(102400, desc.average_bandwidth) self.assertEqual(204800, desc.burst_bandwidth) @@ -504,6 +507,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual('BB1F13AA431421BEA29B840A2E33BB1C31C2990B', desc.extra_info_digest) self.assertEqual(None, desc.extra_info_sha256_digest) self.assertEqual(None, desc.hidden_service_dir) + self.assertEqual(BridgeDistribution.ANY, desc.bridge_distribution) self.assertEqual(set(), desc.family) self.assertEqual(3220480, desc.average_bandwidth) self.assertEqual(6441984, desc.burst_bandwidth) @@ -753,6 +757,14 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= fingerprint = '4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE45' expect_invalid_attr(self, {'opt fingerprint': fingerprint}, 'fingerprint', fingerprint.replace(' ', ''))
+ def test_with_bridge_distribution(self): + """ + Include a preferred method of bridge distribution. + """ + + desc = RelayDescriptor.create({'bridge-distribution-request': 'email'}) + self.assertEqual(BridgeDistribution.EMAIL, desc.bridge_distribution) + def test_ipv6_policy(self): """ Checks a 'ipv6-policy' line.