commit 98a8842c9ab800f9891a01caf35d02f2dfc60809 Author: Damian Johnson atagar@torproject.org Date: Sat Jan 12 10:46:39 2013 -0800
Supporting footers in pre-version 9 method documents
All version 3 network status documents can have a footer. The caveat is that prior to consensus method 9 they just contained signatures, and after that they were a properly marked 'directory-footer' section.
Reproed the issue that Karsten discovered in 'https://trac.torproject.org/7932' via our unit tests then fixed. --- stem/descriptor/networkstatus.py | 22 +++++++++++++++----- test/mocking.py | 22 +++++++++++++++++++- test/unit/descriptor/networkstatus/document_v3.py | 17 ++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index e3d5779..cef668a 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -115,7 +115,7 @@ HEADER_STATUS_DOCUMENT_FIELDS = ( )
FOOTER_STATUS_DOCUMENT_FIELDS = ( - ("directory-footer", True, True, True), + ("directory-footer", True, True, False), ("bandwidth-weights", False, True, False), ("directory-signature", True, True, True), ) @@ -482,7 +482,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument): validate, entry_class = DirectoryAuthority, entry_keyword = AUTH_START, - section_end_keywords = (ROUTERS_START, FOOTER_START), + section_end_keywords = (ROUTERS_START, FOOTER_START, V2_FOOTER_START), extra_args = (self._header.is_vote,), ))
@@ -496,7 +496,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument): validate, entry_class = router_type, entry_keyword = ROUTERS_START, - section_end_keywords = (FOOTER_START,), + section_end_keywords = (FOOTER_START, V2_FOOTER_START), extra_args = (self,), ))
@@ -746,15 +746,25 @@ class _DocumentFooter(object): self._unrecognized_lines = []
content = document_file.read() - if validate and content and not header.meets_consensus_method(9): - raise ValueError("Network status document's footer should only appear in consensus-method 9 or later") - elif not content and not header.meets_consensus_method(9): + + if not content: return # footer is optional and there's nothing to parse
entries = stem.descriptor._get_descriptor_components(content, validate) self._parse(entries, validate, header)
if validate: + # Check that the footer has the right initial line. Prior to consensus + # method 9 it's a 'directory-signature' and after that footers start with + # 'directory-footer'. + + if header.meets_consensus_method(9): + if entries.keys()[0] != 'directory-footer': + raise ValueError("Network status document's footer should start with a 'directory-footer' line in consensus-method 9 or later") + else: + if entries.keys()[0] != 'directory-signature': + raise ValueError("Network status document's footer should start with a 'directory-signature' line prior to consensus-method 9") + _check_for_missing_and_disallowed_fields(header, entries, FOOTER_STATUS_DOCUMENT_FIELDS) _check_for_misordered_fields(entries, FOOTER_FIELDS)
diff --git a/test/mocking.py b/test/mocking.py index e92bacd..82b9790 100644 --- a/test/mocking.py +++ b/test/mocking.py @@ -864,12 +864,30 @@ def get_network_status_document_v3(attr = None, exclude = (), authorities = None
# inject the authorities and/or routers between the header and footer if authorities: - footer_div = desc_content.find("\ndirectory-footer") + 1 + if "directory-footer" in desc_content: + footer_div = desc_content.find("\ndirectory-footer") + 1 + elif "directory-signature" in desc_content: + footer_div = desc_content.find("\ndirectory-signature") + 1 + else: + if routers: + desc_content += "\n" + + footer_div = len(desc_content) + 1 + authority_content = "\n".join([str(a) for a in authorities]) + "\n" desc_content = desc_content[:footer_div] + authority_content + desc_content[footer_div:]
if routers: - footer_div = desc_content.find("\ndirectory-footer") + 1 + if "directory-footer" in desc_content: + footer_div = desc_content.find("\ndirectory-footer") + 1 + elif "directory-signature" in desc_content: + footer_div = desc_content.find("\ndirectory-signature") + 1 + else: + if routers: + desc_content += "\n" + + footer_div = len(desc_content) + 1 + router_content = "\n".join([str(r) for r in routers]) + "\n" desc_content = desc_content[:footer_div] + router_content + desc_content[footer_div:]
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py index 42603b1..4ea6347 100644 --- a/test/unit/descriptor/networkstatus/document_v3.py +++ b/test/unit/descriptor/networkstatus/document_v3.py @@ -549,6 +549,23 @@ class TestNetworkStatusDocument(unittest.TestCase): self.assertEqual([], document.signatures) self.assertEqual([], document.get_unrecognized_lines())
+ # Prior to conensus method 9 votes can still have a signature in their + # footer... + # + # https://trac.torproject.org/7932 + + document = get_network_status_document_v3( + { + "vote-status": "vote", + "consensus-methods": "1 8", + }, + exclude = ("directory-footer",), + authorities = (get_directory_authority(is_vote = True),) + ) + + self.assertEqual([DOC_SIG], document.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.