commit 153ada322653f95eb21c360d3cac4fe6ad436ed7 Author: Damian Johnson atagar@torproject.org Date: Thu Jan 10 08:37:17 2013 -0800
Supporting '-legacy' authority entries
Directory authorities have an alternative format if their nickname has a '-legacy' suffix. Caught by Karsten on...
https://trac.torproject.org/7866 --- stem/descriptor/networkstatus.py | 29 ++++++++++++++++--- .../networkstatus/directory_authority.py | 23 +++++++++++++++ test/unit/descriptor/networkstatus/document_v3.py | 15 ++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 5da190a..e3d5779 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -909,17 +909,24 @@ class DirectoryAuthority(stem.descriptor.Descriptor): """ Directory authority information obtained from a v3 network status document.
+ Authorities can optionally use a legacy format. These are no longer found in + practice, but have the following differences... + + * The authority's nickname ends with '-legacy'. + * There's no **contact** or **vote_digest** attribute. + :var str nickname: ***** authority's nickname :var str fingerprint: ***** authority's fingerprint :var str hostname: ***** hostname of the authority :var str address: ***** authority's IP address :var int dir_port: ***** authority's DirPort :var int or_port: ***** authority's ORPort - :var str contact: ***** contact information + :var bool is_legacy: ***** if the authority's using the legacy format + :var str contact: contact information, this is included if is_legacy is **False**
**Consensus Attributes:**
- :var str vote_digest: ***** digest of the authority that contributed to the consensus + :var str vote_digest: digest of the authority that contributed to the consensus, this is included if is_legacy is **False**
**Vote Attributes:**
@@ -950,6 +957,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): self.address = None self.dir_port = None self.or_port = None + self.is_legacy = False self.contact = None
self.vote_digest = None @@ -990,7 +998,15 @@ class DirectoryAuthority(stem.descriptor.Descriptor): # check that we have mandatory fields
if validate: - required_fields, excluded_fields = ["dir-source", "contact"], [] + is_legacy, dir_source_entry = False, entries.get("dir-source") + + if dir_source_entry: + is_legacy = dir_source_entry[0][0].split()[0].endswith("-legacy") + + required_fields, excluded_fields = ["dir-source"], [] + + if not is_legacy: + required_fields += ["contact"]
if is_vote: if not key_cert_content: @@ -1001,7 +1017,9 @@ class DirectoryAuthority(stem.descriptor.Descriptor): if key_cert_content: raise ValueError("Authority consensus entries shouldn't have a key certificate:\n%s" % content)
- required_fields += ["vote-digest"] + if not is_legacy: + required_fields += ["vote-digest"] + excluded_fields += ["legacy-dir-key"]
for keyword in required_fields: @@ -1033,7 +1051,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): raise ValueError("Authority entry's 'dir-source' line must have six values: %s" % line)
if validate: - if not stem.util.tor_tools.is_valid_nickname(dir_source_comp[0]): + if not stem.util.tor_tools.is_valid_nickname(dir_source_comp[0].rstrip('-legacy')): raise ValueError("Authority's nickname is invalid: %s" % dir_source_comp[0]) elif not stem.util.tor_tools.is_valid_fingerprint(dir_source_comp[1]): raise ValueError("Authority's fingerprint is invalid: %s" % dir_source_comp[1]) @@ -1055,6 +1073,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): self.address = dir_source_comp[3] self.dir_port = None if dir_source_comp[4] == '0' else int(dir_source_comp[4]) self.or_port = int(dir_source_comp[5]) + self.is_legacy = self.nickname.endswith("-legacy") elif keyword == 'contact': # "contact" string
diff --git a/test/unit/descriptor/networkstatus/directory_authority.py b/test/unit/descriptor/networkstatus/directory_authority.py index eb685df..539b6b1 100644 --- a/test/unit/descriptor/networkstatus/directory_authority.py +++ b/test/unit/descriptor/networkstatus/directory_authority.py @@ -22,6 +22,7 @@ class TestDirectoryAuthority(unittest.TestCase): self.assertEqual("76.73.17.194", authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) + self.assertEqual(False, authority.is_legacy) self.assertEqual("Mike Perry <email>", authority.contact) self.assertEqual("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) @@ -41,6 +42,7 @@ class TestDirectoryAuthority(unittest.TestCase): self.assertEqual("76.73.17.194", authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) + self.assertEqual(False, authority.is_legacy) self.assertEqual("Mike Perry <email>", authority.contact) self.assertEqual(None, authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) @@ -55,6 +57,27 @@ class TestDirectoryAuthority(unittest.TestCase): authority = get_directory_authority({"pepperjack": "is oh so tasty!"}) self.assertEquals(["pepperjack is oh so tasty!"], authority.get_unrecognized_lines())
+ def test_legacy_authority(self): + """ + Parses an authority using the '-legacy' format. + """ + + content = "dir-source gabelmoo-legacy 81349FC1F2DBA2C2C11B45CB9706637D480AB913 212.112.245.170 212.112.245.170 80 443" + authority = DirectoryAuthority(content, is_vote = False) + + self.assertEqual("gabelmoo-legacy", authority.nickname) + self.assertEqual("81349FC1F2DBA2C2C11B45CB9706637D480AB913", authority.fingerprint) + self.assertEqual("212.112.245.170", authority.hostname) + self.assertEqual("212.112.245.170", authority.address) + self.assertEqual(80, authority.dir_port) + self.assertEqual(443, authority.or_port) + self.assertEqual(True, authority.is_legacy) + self.assertEqual(None, authority.contact) + self.assertEqual(None, authority.vote_digest) + self.assertEqual(None, authority.legacy_dir_key) + self.assertEqual(None, authority.key_certificate) + self.assertEqual([], authority.get_unrecognized_lines()) + def test_first_line(self): """ Includes a non-mandatory field before the 'dir-source' line. diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py index d15e88a..42603b1 100644 --- a/test/unit/descriptor/networkstatus/document_v3.py +++ b/test/unit/descriptor/networkstatus/document_v3.py @@ -787,6 +787,21 @@ class TestNetworkStatusDocument(unittest.TestCase): document = NetworkStatusDocumentV3(content, validate = False) self.assertEquals((authority1, authority2), document.directory_authorities)
+ def test_with_legacy_directory_authorities(self): + """ + Includes both normal authorities and those following the '-legacy' format. + """ + + legacy_content = "dir-source gabelmoo-legacy 81349FC1F2DBA2C2C11B45CB9706637D480AB913 212.112.245.170 212.112.245.170 80 443" + + authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = False) + authority2 = DirectoryAuthority(legacy_content, validate = True, is_vote = False) + authority3 = get_directory_authority({'contact': 'mister hyde'}, is_vote = False) + + document = get_network_status_document_v3({"vote-status": "consensus"}, authorities = (authority1, authority2, authority3)) + + self.assertEquals((authority1, authority2, authority3), document.directory_authorities) + def test_authority_validation_flag_propagation(self): """ Includes invalid certificate content in an authority entry. This is testing
tor-commits@lists.torproject.org