[tor-commits] [stem/master] Making relay-signature field only apply to relay extra-info
atagar at torproject.org
atagar at torproject.org
Sun Jun 10 21:30:22 UTC 2012
commit 737018b7ee3da5b457689404376a7937de5a016f
Author: Damian Johnson <atagar at torproject.org>
Date: Sun Jun 10 13:33:21 2012 -0700
Making relay-signature field only apply to relay extra-info
Parsing and handling the relay-signature field as only an attribute of
extra-info descriptors for relays.
---
stem/descriptor/__init__.py | 4 +-
stem/descriptor/extrainfo_descriptor.py | 78 ++++++++++++++++++------
stem/descriptor/server_descriptor.py | 31 ++++------
test/integ/descriptor/extrainfo_descriptor.py | 2 +-
test/unit/descriptor/extrainfo_descriptor.py | 34 +++++-----
5 files changed, 91 insertions(+), 58 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index e4f23e5..929396c 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -70,9 +70,9 @@ def parse_file(path, descriptor_file):
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 bridge-extra-info 1.0"):
- desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(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 desc:
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 03c7a37..0515099 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -114,9 +114,6 @@ SINGLE_FIELDS = (
"exit-streams-opened",
)
-FIRST_FIELD = "extra-info"
-LAST_FIELD = "router-signature"
-
def parse_file(descriptor_file, validate = True):
"""
Iterates over the extra-info descriptors in a file.
@@ -139,7 +136,7 @@ def parse_file(descriptor_file, validate = True):
extrainfo_content += stem.descriptor._read_until_keyword(block_end_prefix, descriptor_file, True)
if extrainfo_content:
- yield ExtraInfoDescriptor("".join(extrainfo_content), validate)
+ yield RelayExtraInfoDescriptor("".join(extrainfo_content), validate)
else: break # done parsing file
def _parse_timestamp_and_interval(keyword, content):
@@ -180,7 +177,6 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
:var str fingerprint: **\*** identity key fingerprint
:var datetime published: **\*** time in GMT when this descriptor was made
:var str geoip_db_digest: sha1 of geoIP database file
- :var str signature: **\*** signature for this extrainfo descriptor
**Bi-directional connection usage:**
@@ -289,7 +285,6 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
self.fingerprint = None
self.published = None
self.geoip_db_digest = None
- self.signature = None
self.conn_bi_direct_end = None
self.conn_bi_direct_interval = None
@@ -364,18 +359,21 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
stem.descriptor._get_descriptor_components(raw_contents, validate, ())
if validate:
- for keyword in REQUIRED_FIELDS:
+ for keyword in self._required_fields():
if not keyword in entries:
raise ValueError("Extra-info descriptor must have a '%s' entry" % keyword)
- for keyword in REQUIRED_FIELDS + SINGLE_FIELDS:
+ for keyword in self._required_fields() + SINGLE_FIELDS:
if keyword in entries and len(entries[keyword]) > 1:
raise ValueError("The '%s' entry can only appear once in an extra-info descriptor" % keyword)
- if not first_keyword == FIRST_FIELD:
- raise ValueError("Extra-info descriptor must start with a '%s' entry" % FIRST_FIELD)
- if not last_keyword == LAST_FIELD:
- raise ValueError("Descriptor must end with a '%s' entry" % LAST_FIELD)
+ expected_first_keyword = self._first_keyword()
+ if expected_first_keyword and not first_keyword == expected_first_keyword:
+ raise ValueError("Extra-info descriptor must start with a '%s' entry" % expected_first_keyword)
+
+ expected_last_keyword = self._last_keyword()
+ if expected_last_keyword and not last_keyword == expected_last_keyword:
+ raise ValueError("Descriptor must end with a '%s' entry" % expected_last_keyword)
self._parse(entries, validate)
@@ -395,10 +393,8 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
for keyword, values in entries.items():
# most just work with the first (and only) value
- value, block_contents = values[0]
-
+ value, _ = values[0]
line = "%s %s" % (keyword, value) # original line
- if block_contents: line += "\n%s" % block_contents
if keyword == "extra-info":
# "extra-info" Nickname Fingerprint
@@ -674,23 +670,65 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
self.entry_ips = locale_usage
elif keyword == "bridge-ips":
self.bridge_ips = locale_usage
- elif keyword == "router-signature":
- if validate and not block_contents:
- raise ValueError("Router signature line must be followed by a signature block: %s" % line)
-
- self.signature = block_contents
else:
self._unrecognized_lines.append(line)
+
+ def _required_fields(self):
+ return REQUIRED_FIELDS
+
+ def _first_keyword(self):
+ return "extra-info"
+
+ def _last_keyword(self):
+ return "router-signature"
class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
"""
Relay extra-info descriptor, constructed from data such as that provided by
"GETINFO extra-info/digest/*", cached descriptors, and metrics
(`specification <https://gitweb.torproject.org/torspec.git/blob/HEAD:/dir-spec.txt>`_).
+
+ :var str signature: **\*** signature for this extrainfo descriptor
+
+ **\*** attribute is either required when we're parsed with validation or has a default value, others are left as None if undefined
"""
+
+ def __init__(self, raw_contents, validate = True):
+ self.signature = None
+
+ ExtraInfoDescriptor.__init__(self, raw_contents, validate)
+
+ def _parse(self, entries, validate):
+ entries = dict(entries) # shallow copy since we're destructive
+
+ # handles fields only in server descriptors
+ for keyword, values in entries.items():
+ value, block_contents = values[0]
+
+ line = "%s %s" % (keyword, value) # original line
+ if block_contents: line += "\n%s" % block_contents
+
+ if keyword == "router-signature":
+ if validate and not block_contents:
+ raise ValueError("Router signature line must be followed by a signature block: %s" % line)
+
+ self.signature = block_contents
+ del entries["router-signature"]
+
+ ExtraInfoDescriptor._parse(self, entries, validate)
class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
"""
Bridge extra-info descriptor (`specification <https://metrics.torproject.org/formats.html#bridgedesc>`_)
"""
+
+ def _required_fields(self):
+ excluded_fields = (
+ "router-signature",
+ )
+
+ filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS)
+
+ def _last_keyword(self):
+ return None
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 6f8079b..af31f03 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -542,10 +542,17 @@ class ServerDescriptor(stem.descriptor.Descriptor):
# Constraints that the descriptor must meet to be valid. These can be None if
# not applicable.
- def _required_fields(self): return None
- def _single_fields(self): return None
- def _first_keyword(self): return None
- def _last_keyword(self): return None
+ def _required_fields(self):
+ return REQUIRED_FIELDS
+
+ def _single_fields(self):
+ return REQUIRED_FIELDS + SINGLE_FIELDS
+
+ def _first_keyword(self):
+ return "router"
+
+ def _last_keyword(self):
+ return "router-signature"
class RelayDescriptor(ServerDescriptor):
"""
@@ -640,18 +647,6 @@ class RelayDescriptor(ServerDescriptor):
del entries["router-signature"]
ServerDescriptor._parse(self, entries, validate)
-
- def _required_fields(self):
- return REQUIRED_FIELDS
-
- def _single_fields(self):
- return REQUIRED_FIELDS + SINGLE_FIELDS
-
- def _first_keyword(self):
- return "router"
-
- def _last_keyword(self):
- return "router-signature"
class BridgeDescriptor(ServerDescriptor):
"""
@@ -780,6 +775,6 @@ class BridgeDescriptor(ServerDescriptor):
def _single_fields(self):
return self._required_fields() + SINGLE_FIELDS
- def _first_keyword(self):
- return "router"
+ def _last_keyword(self):
+ return None
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index a753d27..2739985 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -29,7 +29,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
-----END SIGNATURE-----"""
- desc = stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor(descriptor_contents)
+ desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_contents)
self.assertEquals("NINJA", desc.nickname)
self.assertEquals("B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48", desc.fingerprint)
self.assertEquals(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 5d4c910..3bfff70 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 ExtraInfoDescriptor, DirResponses, DirStats
+from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponses, DirStats
CRYPTO_BLOB = """
K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
@@ -58,7 +58,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
"""
desc_text = _make_descriptor()
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals("ninja", desc.nickname)
self.assertEquals("B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48", desc.fingerprint)
@@ -70,7 +70,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
"""
desc_text = _make_descriptor({"pepperjack": "is oh so tasty!"})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(["pepperjack is oh so tasty!"], desc.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -116,7 +116,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
geoip_db_digest = "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8"
desc_text = _make_descriptor({"geoip-db-digest": geoip_db_digest})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(geoip_db_digest, desc.geoip_db_digest)
test_entries = (
@@ -143,7 +143,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for entry in ("0", "11", "25"):
desc_text = _make_descriptor({"cell-circuits-per-decile": entry})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(int(entry), desc.cell_circuits_per_decile)
test_entries = (
@@ -169,7 +169,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
test_value = "ok=0,unavailable=0,not-found=984,not-modified=0,something-new=7"
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(0, getattr(desc, attr)[DirResponses.OK])
self.assertEquals(0, getattr(desc, attr)[DirResponses.UNAVAILABLE])
self.assertEquals(984, getattr(desc, attr)[DirResponses.NOT_FOUND])
@@ -200,7 +200,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
test_value = "complete=2712,timeout=32,running=4,min=741,d1=14507,d2=22702,q1=28881,d3=38277,d4=73729,md=111455,d6=168231,d7=257218,q3=319833,d8=390507,d9=616301,something-new=11,max=29917857"
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(2712, getattr(desc, attr)[DirStats.COMPLETE])
self.assertEquals(32, getattr(desc, attr)[DirStats.TIMEOUT])
self.assertEquals(4, getattr(desc, attr)[DirStats.RUNNING])
@@ -237,7 +237,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
"""
desc_text = _make_descriptor({"conn-bi-direct": "2012-05-03 12:07:50 (500 s) 277431,12089,0,2134"})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.conn_bi_direct_end)
self.assertEquals(500, desc.conn_bi_direct_interval)
self.assertEquals(277431, desc.conn_bi_direct_below)
@@ -287,7 +287,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for test_value, expected_value in test_entries:
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(expected_value, getattr(desc, attr))
test_entries = (
@@ -320,7 +320,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for test_value, expected_value in test_entries:
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(expected_value, getattr(desc, attr))
test_entries = (
@@ -343,7 +343,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
attr = keyword.replace('-', '_')
desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50"})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, attr))
test_entries = (
@@ -368,7 +368,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
interval_attr = end_attr[:-4] + "_interval"
desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50 (500 s)"})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
self.assertEquals(500, getattr(desc, interval_attr))
@@ -408,7 +408,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for test_values, expected_values in test_entries:
desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50 (500 s)%s" % test_values})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
self.assertEquals(500, getattr(desc, interval_attr))
self.assertEquals(expected_values, getattr(desc, values_attr))
@@ -448,7 +448,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for test_value, expected_value in test_entries:
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(expected_value, getattr(desc, attr))
test_entries = (
@@ -480,7 +480,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
for test_value, expected_value in test_entries:
desc_text = _make_descriptor({keyword: test_value})
- desc = ExtraInfoDescriptor(desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text)
self.assertEquals(expected_value, getattr(desc, attr))
test_entries = (
@@ -501,8 +501,8 @@ class TestExtraInfoDescriptor(unittest.TestCase):
value when we're constructed without validation.
"""
- self.assertRaises(ValueError, ExtraInfoDescriptor, desc_text)
- desc = ExtraInfoDescriptor(desc_text, validate = False)
+ self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text)
+ desc = RelayExtraInfoDescriptor(desc_text, validate = False)
if attr:
# check that the invalid attribute matches the expected value when
More information about the tor-commits
mailing list