[tor-commits] [stem/master] Handling version 1.1 of bridge extrainfo descriptors
atagar at torproject.org
atagar at torproject.org
Fri Jun 29 17:20:28 UTC 2012
commit c35c8a6cbb3659841f79159512c24a08a5e1d622
Author: Damian Johnson <atagar at torproject.org>
Date: Fri Jun 29 09:59:16 2012 -0700
Handling version 1.1 of bridge extrainfo descriptors
Version 1.1 of the bridge extrainfo descriptors adds a new 'transport' field.
I'm not entirely sure yet of its attributes, but recognizing the new descriptor
version and parsing the field.
I'm also expanding the tests a bit so unit tests cover bridge extrainfo
descriptors, and parsing the metrics header line rather than just doing string
matches.
This is to address...
https://trac.torproject.org/6257
---
stem/descriptor/__init__.py | 27 ++++++++++----
stem/descriptor/extrainfo_descriptor.py | 8 ++++-
test/unit/descriptor/extrainfo_descriptor.py | 50 +++++++++++++++++++++++---
3 files changed, 71 insertions(+), 14 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 929396c..21dad37 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -65,15 +65,26 @@ def parse_file(path, descriptor_file):
# Metrics descriptor handling. These contain a single descriptor per file.
first_line, desc = descriptor_file.readline().strip(), None
+ metrics_header_match = re.match("^@type (\S+) (\d+).(\d+)$", first_line)
- if first_line == "@type server-descriptor 1.0":
- desc = stem.descriptor.server_descriptor.RelayDescriptor(descriptor_file.read())
- elif first_line == "@type bridge-server-descriptor 1.0":
- desc = stem.descriptor.server_descriptor.BridgeDescriptor(descriptor_file.read())
- elif first_line in ("@type extra-info 1.0"):
- desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_file.read())
- elif first_line in ("@type bridge-extra-info 1.0"):
- desc = stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor(descriptor_file.read())
+ if metrics_header_match:
+ # still doesn't necessarily mean that this is a descriptor, check if the
+ # header contents are recognized
+
+ desc_type, major_version, minor_version = metrics_header_match.groups()
+ major_version, minor_version = int(major_version), int(minor_version)
+
+ if desc_type == "server-descriptor" and major_version == 1 and minor_version == 0:
+ desc = stem.descriptor.server_descriptor.RelayDescriptor(descriptor_file.read())
+ elif desc_type == "bridge-server-descriptor" and major_version == 1 and minor_version == 0:
+ desc = stem.descriptor.server_descriptor.BridgeDescriptor(descriptor_file.read())
+ elif desc_type == "extra-info" and major_version == 1 and minor_version == 0:
+ desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_file.read())
+ elif desc_type == "bridge-extra-info" and major_version == 1 and minor_version in (0, 1):
+ # version 1.1 introduced a 'transport' field...
+ # https://trac.torproject.org/6257
+
+ desc = stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor(descriptor_file.read())
if desc:
desc._set_path(path)
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 04f061a..979c679 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -742,10 +742,13 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
"""
Bridge extra-info descriptor (`specification <https://metrics.torproject.org/formats.html#bridgedesc>`_)
+
+ :var str transport: transport method recognized by the bridge (ex. obfs3)
"""
def __init__(self, raw_contents, validate = True):
self._digest = None
+ self.transport = None
ExtraInfoDescriptor.__init__(self, raw_contents, validate)
@@ -760,7 +763,10 @@ class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
value, _ = values[0]
line = "%s %s" % (keyword, value) # original line
- if keyword == "router-digest":
+ if keyword == "transport":
+ self.transport = value
+ del entries["transport"]
+ elif keyword == "router-digest":
if validate and not stem.util.tor_tools.is_hex_digits(value, 40):
raise ValueError("Router digest line had an invalid sha1 digest: %s" % line)
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 888180c..e9b06a0 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -4,7 +4,7 @@ Unit tests for stem.descriptor.extrainfo_descriptor.
import datetime
import unittest
-from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponses, DirStats
+from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, BridgeExtraInfoDescriptor, DirResponses, DirStats
CRYPTO_BLOB = """
K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
@@ -12,13 +12,19 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
"""
-EXTRAINFO_DESCRIPTOR_ATTR = (
+RELAY_EXTRAINFO_ATTR = (
("extra-info", "ninja B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48"),
("published", "2012-05-05 17:03:50"),
("router-signature", "\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----" % CRYPTO_BLOB),
)
-def _make_descriptor(attr = None, exclude = None):
+BRIDGE_EXTRAINFO_ATTR = (
+ ("extra-info", "ec2bridgereaac65a3 1EC248422B57D9C0BD751892FE787585407479A4"),
+ ("published", "2012-05-05 17:03:50"),
+ ("router-digest", "006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4"),
+)
+
+def _make_descriptor(attr = None, exclude = None, is_bridge = False):
"""
Constructs a minimal extrainfo descriptor with the given attributes.
@@ -31,21 +37,28 @@ def _make_descriptor(attr = None, exclude = None):
descriptor_lines = []
if attr is None: attr = {}
if exclude is None: exclude = []
+ desc_attr = BRIDGE_EXTRAINFO_ATTR if is_bridge else RELAY_EXTRAINFO_ATTR
attr = dict(attr) # shallow copy since we're destructive
- for keyword, value in EXTRAINFO_DESCRIPTOR_ATTR:
+ for keyword, value in desc_attr:
if keyword in exclude: continue
elif keyword in attr:
value = attr[keyword]
del attr[keyword]
# if this is the last entry then we should dump in any unused attributes
- if keyword == "router-signature":
+ if not is_bridge and keyword == "router-signature":
for attr_keyword, attr_value in attr.items():
descriptor_lines.append("%s %s" % (attr_keyword, attr_value))
descriptor_lines.append("%s %s" % (keyword, value))
+ # bridges don't have a router-signature so simply append any extra attributes
+ # to the end
+ if is_bridge:
+ for attr_keyword, attr_value in attr.items():
+ descriptor_lines.append("%s %s" % (attr_keyword, attr_value))
+
return "\n".join(descriptor_lines)
class TestExtraInfoDescriptor(unittest.TestCase):
@@ -492,6 +505,33 @@ class TestExtraInfoDescriptor(unittest.TestCase):
desc_text = _make_descriptor({keyword: entry})
self._expect_invalid_attr(desc_text, attr, {})
+ def test_minimal_bridge_descriptor(self):
+ """
+ Basic sanity check that we can parse a descriptor with minimal attributes.
+ """
+
+ desc_text = _make_descriptor(is_bridge = True)
+ desc = BridgeExtraInfoDescriptor(desc_text)
+
+ self.assertEquals("ec2bridgereaac65a3", desc.nickname)
+ self.assertEquals("1EC248422B57D9C0BD751892FE787585407479A4", desc.fingerprint)
+ self.assertEquals("006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4", desc.digest())
+ self.assertEquals([], desc.get_unrecognized_lines())
+
+ # check that we don't have crypto fields
+ self.assertRaises(AttributeError, getattr, desc, "signature")
+
+ def test_transport_line(self):
+ """
+ Basic exercise of the bridge descriptor's transport entry.
+ attributes.
+ """
+
+ desc_text = _make_descriptor({"transport": "obfs3"}, is_bridge = True)
+ desc = BridgeExtraInfoDescriptor(desc_text)
+ self.assertEquals("obfs3", desc.transport)
+ self.assertEquals([], desc.get_unrecognized_lines())
+
def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
"""
Asserts that construction will fail due to desc_text having a malformed
More information about the tor-commits
mailing list