[tor-commits] [stem/master] Parsing the directory-footer attribute
atagar at torproject.org
atagar at torproject.org
Sat Oct 13 18:35:45 UTC 2012
commit 5c4a3ec4cb22fe0fff6c44f3eecf2a2639788ed6
Author: Damian Johnson <atagar at torproject.org>
Date: Sun Sep 16 17:07:03 2012 -0700
Parsing the directory-footer attribute
For being an empty attribute this sure is a strangely big change. Checking the
following...
- that footers don't appear prior to consensus-method 9
- that the directory-footer lacks any content
- that prior to consensus-method 9 we're happy to not have the line (bug I
introduced because the footer has mandatory fields)
---
stem/descriptor/networkstatus.py | 35 +++++++++++++-----
test/unit/descriptor/networkstatus/document.py | 46 +++++++++++++++++++----
2 files changed, 64 insertions(+), 17 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 2402460..e262264 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -268,6 +268,19 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
def _get_router_type(self):
return RouterStatusEntry
+ def meets_consensus_method(self, method):
+ """
+ Checks if we meet the given consensus-method. This works for both votes and
+ consensuses, checking our 'consensus-method' and 'consensus-methods'
+ entries.
+
+ :param int method: consensus-method to check for
+
+ :returns: True if we meet the given consensus-method, and False otherwise
+ """
+
+ return bool(self.consensus_method >= method or filter(lambda x: x >= method, self.consensus_methods))
+
def get_unrecognized_lines(self):
"""
Returns any unrecognized trailing lines.
@@ -398,7 +411,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
# Parameters ::= Parameter | Parameters SP Parameter
# should only appear in consensus-method 7 or later
- if validate and not (self.consensus_method >= 7 or filter(lambda x: x >= 7, self.consensus_methods)):
+ if validate and not self.meets_consensus_method(7):
raise ValueError("A network status document's 'params' line should only appear in consensus-method 7 or later")
# skip if this is a blank line
@@ -435,6 +448,11 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
if validate:
self._check_params_constraints()
+ elif keyword == "directory-footer":
+ # nothing to parse, simply checking that we don't have a value
+
+ if validate and value:
+ raise ValueError("A network status document's 'directory-footer' line shouldn't have any content, got '%s'" % line)
# doing this validation afterward so we know our 'is_consensus' and
# 'is_vote' attributes
@@ -471,12 +489,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
dirauth_data = "".join(dirauth_data).rstrip()
self.directory_authorities.append(DirectoryAuthority(dirauth_data, vote, validate))
- # footer section
- if self.consensus_method >= 9 or (vote and filter(lambda x: x >= 9, self.consensus_methods)):
- if _peek_keyword(content) == "directory-footer":
- content.readline()
- elif validate:
- raise ValueError("Network status document missing directory-footer")
+ _read_keyword_line("directory-footer", content, False, True)
if not vote:
read_keyword_line("bandwidth-weights", True)
@@ -511,7 +524,11 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
for entries, fields in ((header_entries, HEADER_STATUS_DOCUMENT_FIELDS),\
(footer_entries, FOOTER_STATUS_DOCUMENT_FIELDS)):
for field, in_votes, in_consensus, mandatory in fields:
- if mandatory and ((self.is_consensus and in_consensus) or (self.is_vote and in_votes)):
+ if field in ('directory-footer', 'directory-signature') and not self.meets_consensus_method(9):
+ # footers only appear in consensus-method 9 or later
+ if field in entries.keys():
+ disallowed_fields.append(field)
+ elif mandatory and ((self.is_consensus and in_consensus) or (self.is_vote and in_votes)):
# mandatory field, check that we have it
if not field in entries.keys():
missing_fields.append(field)
@@ -524,7 +541,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
raise ValueError("Network status document is missing mandatory field: %s" % ', '.join(missing_fields))
if disallowed_fields:
- raise ValueError("Network status document has fields that shouldn't appear in this document type: %s" % ', '.join(disallowed_fields))
+ raise ValueError("Network status document has fields that shouldn't appear in this document type or version: %s" % ', '.join(disallowed_fields))
def _check_for_misordered_fields(self, header_entries, footer_entries):
"""
diff --git a/test/unit/descriptor/networkstatus/document.py b/test/unit/descriptor/networkstatus/document.py
index 1fa932a..6d93624 100644
--- a/test/unit/descriptor/networkstatus/document.py
+++ b/test/unit/descriptor/networkstatus/document.py
@@ -30,6 +30,8 @@ NETWORK_STATUS_DOCUMENT_ATTR = {
"-----END SIGNATURE-----")),
}
+SIG = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
+
def get_network_status_document(attr = None, exclude = None, routers = None):
"""
Constructs a minimal network status document with the given attributes. This
@@ -99,8 +101,6 @@ class TestNetworkStatusDocument(unittest.TestCase):
Flag.FAST, Flag.GUARD, Flag.HSDIR, Flag.NAMED, Flag.RUNNING,
Flag.STABLE, Flag.UNNAMED, Flag.V2DIR, Flag.VALID]
- sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
-
self.assertEqual((), document.routers)
self.assertEqual("3", document.version)
self.assertEqual(True, document.is_consensus)
@@ -119,7 +119,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
self.assertEqual(DEFAULT_PARAMS, document.params)
self.assertEqual([], document.directory_authorities)
self.assertEqual(None, document.bandwidth_weights)
- self.assertEqual([sig], document.directory_signatures)
+ self.assertEqual([SIG], document.directory_signatures)
self.assertEqual([], document.get_unrecognized_lines())
def test_minimal_vote(self):
@@ -133,8 +133,6 @@ class TestNetworkStatusDocument(unittest.TestCase):
Flag.FAST, Flag.GUARD, Flag.HSDIR, Flag.NAMED, Flag.RUNNING,
Flag.STABLE, Flag.UNNAMED, Flag.V2DIR, Flag.VALID]
- sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
-
self.assertEqual((), document.routers)
self.assertEqual("3", document.version)
self.assertEqual(False, document.is_consensus)
@@ -153,7 +151,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
self.assertEqual(DEFAULT_PARAMS, document.params)
self.assertEqual([], document.directory_authorities)
self.assertEqual({}, document.bandwidth_weights)
- self.assertEqual([sig], document.directory_signatures)
+ self.assertEqual([SIG], document.directory_signatures)
self.assertEqual([], document.get_unrecognized_lines())
def test_missing_fields(self):
@@ -274,7 +272,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
# check that we default to including consensus-method 1
content = get_network_status_document({"vote-status": "vote"}, ("consensus-methods",))
- document = NetworkStatusDocument(content)
+ document = NetworkStatusDocument(content, False)
self.assertEquals([1], document.consensus_methods)
self.assertEquals(None, document.consensus_method)
@@ -304,7 +302,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
# check that we default to being consensus-method 1
content = get_network_status_document(exclude = ("consensus-method",))
- document = NetworkStatusDocument(content)
+ document = NetworkStatusDocument(content, False)
self.assertEquals(1, document.consensus_method)
self.assertEquals([], document.consensus_methods)
@@ -524,4 +522,36 @@ class TestNetworkStatusDocument(unittest.TestCase):
document = NetworkStatusDocument(content, False, default_params = False)
self.assertEquals({"unrecognized": -122, "bwauthpid": 1}, document.params)
+
+ def test_footer_consensus_method_requirement(self):
+ """
+ Check that validation will notice if a footer appears before it was
+ introduced.
+ """
+
+ content = get_network_status_document({"consensus-method": "8"})
+ self.assertRaises(ValueError, NetworkStatusDocument, content)
+
+ document = NetworkStatusDocument(content, False)
+ self.assertEqual([SIG], document.directory_signatures)
+ self.assertEqual([], document.get_unrecognized_lines())
+
+ # excludes a footer from a version that shouldn't have it
+
+ content = get_network_status_document({"consensus-method": "8"}, ("directory-footer", "directory-signature"))
+ document = NetworkStatusDocument(content)
+ self.assertEqual([], document.directory_signatures)
+ self.assertEqual([], document.get_unrecognized_lines())
+
+ def test_footer_with_value(self):
+ """
+ Tries to parse a descriptor with content on the 'directory-footer' line.
+ """
+
+ content = get_network_status_document({"directory-footer": "blarg"})
+ self.assertRaises(ValueError, NetworkStatusDocument, content)
+
+ document = NetworkStatusDocument(content, False)
+ self.assertEqual([SIG], document.directory_signatures)
+ self.assertEqual([], document.get_unrecognized_lines())
More information about the tor-commits
mailing list