
commit 4ae18ed246b8f9f762df154ea493adb5cfbf1e80 Author: Damian Johnson <atagar@torproject.org> Date: Sun Mar 18 15:08:57 2012 -0700 Handling the server descriptor's fingerprint line --- stem/descriptor/server_descriptor.py | 34 ++++++++++++++++++++++++++-------- stem/util/tor_tools.py | 15 +++++++++++---- test/unit/util/tor_tools.py | 4 ++-- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py index 2b92f5c..cee2bcd 100644 --- a/stem/descriptor/server_descriptor.py +++ b/stem/descriptor/server_descriptor.py @@ -70,6 +70,7 @@ class ServerDescriptorV2(Descriptor): platform (str) - operating system and tor version tor_version (stem.version.Version) - version of tor published (datetime.datetime) - time in GMT when the descriptor was generated (*) + fingerprint (str) - fourty hex digits that make up the relay's fingerprint * required fields, others are left as None if undefined """ @@ -77,7 +78,7 @@ class ServerDescriptorV2(Descriptor): nickname = address = or_port = socks_port = dir_port = None average_bandwidth = burst_bandwidth = observed_bandwidth = None platform = tor_version = None - published = None + published = fingerprint = None def __init__(self, contents): Descriptor.__init__(self, contents) @@ -127,12 +128,15 @@ class ServerDescriptorV2(Descriptor): # parse all the entries into our attributes for keyword, values in entres.items(): + value = values[0] # most just work with the first (and only) value + line = "%s %s" % (keyword, value) # original line + if keyword == "router": # "router" nickname address ORPort SocksPort DirPort - router_comp = values[0].split() + router_comp = value.split() if len(router_comp) != 5: - raise ValueError("Router line must have five values: router %s" % values[0] + raise ValueError("Router line must have five values: %s" % line elif not stem.util.tor_tools.is_valid_nickname(router_comp[0]): raise TypeError("Router line entry isn't a valid nickname: %s" % router_comp[0]) elif not stem.util.connection.is_valid_ip_address(router_comp[1]): @@ -151,10 +155,10 @@ class ServerDescriptorV2(Descriptor): self.dir_port = router_comp[4] elif keyword == "bandwidth": # "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed - bandwidth_comp = values[0].split() + bandwidth_comp = value.split() if len(bandwidth_comp) != 3: - raise ValueError("Bandwidth line must have three values: bandwidth %s" % values[0] + raise ValueError("Bandwidth line must have three values: %s" % line elif not bandwidth_comp[0].isdigit()): raise TypeError("Bandwidth line's average rate isn't numeric: %s" % bandwidth_comp[0]) elif not bandwidth_comp[1].isdigit()): @@ -168,7 +172,7 @@ class ServerDescriptorV2(Descriptor): elif keyword == "platform": # "platform" string - self.platform = values[0] + self.platform = value # This line can contain any arbitrary data, but tor seems to report its # version followed by the os like the following... @@ -186,7 +190,21 @@ class ServerDescriptorV2(Descriptor): # "published" YYYY-MM-DD HH:MM:SS try: - self.published = datetime.datetime.strptime(values[0], "%Y-%m-%d %H:%M:%S") + self.published = datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:%S") except ValueError: - raise TypeError("Published line's time wasn't parseable: %s" % values[0]) + raise TypeError("Published line's time wasn't parseable: %s" % line) + elif keyword == "fingerprint": + # This is fourty hex digits split into space separated groups of four. + # Checking that we match this pattern. + + fingerprint = value.replace(" ", "") + + for grouping in value.split(" "): + if len(grouping) != 4: + raise TypeError("Fingerprint line should have groupings of four hex digits: %s" % value) + + if not stem.util.tor_tools.is_valid_fingerprint(fingerprint): + raise TypeError("Tor relay fingerprints consist of fourty hex digits: %s" % value) + + self.fingerprint = fingerprint diff --git a/stem/util/tor_tools.py b/stem/util/tor_tools.py index 5e468c3..3c0e88a 100644 --- a/stem/util/tor_tools.py +++ b/stem/util/tor_tools.py @@ -13,20 +13,27 @@ import re # case insensitive. Tor doesn't define this in the spec so flipping a coin # and going with case insensitive. -FINGERPRINT_PATTERN = re.compile("^\$[0-9a-fA-F]{40}$") +FINGERPRINT_PATTERN = re.compile("^[0-9a-fA-F]{40}$") NICKNAME_PATTERN = re.compile("^[a-zA-Z0-9]{1,19}$") -def is_valid_fingerprint(entry): +def is_valid_fingerprint(entry, check_prefix = False): """ - Checks if a string is a properly formatted relay fingerprint. + Checks if a string is a properly formatted relay fingerprint. This checks for + a '$' prefix if check_prefix is true, otherwise this only validates the hex + digits. Arguments: - entry (str) - string to be checked + entry (str) - string to be checked + check_prefix (bool) - checks for a '$' prefix Returns: True if the string could be a relay fingerprint, False otherwise. """ + if check_prefix: + if not entry or entry[0] != "$": return False + entry = entry[1:] + return bool(FINGERPRINT_PATTERN.match(entry)) def is_valid_nickname(entry): diff --git a/test/unit/util/tor_tools.py b/test/unit/util/tor_tools.py index 5dd1abb..2ff3058 100644 --- a/test/unit/util/tor_tools.py +++ b/test/unit/util/tor_tools.py @@ -25,10 +25,10 @@ class TestTorTools(unittest.TestCase): ) for fingerprint in valid_fingerprints: - self.assertTrue(stem.util.tor_tools.is_valid_fingerprint(fingerprint)) + self.assertTrue(stem.util.tor_tools.is_valid_fingerprint(fingerprint, True)) for fingerprint in invalid_fingerprints: - self.assertFalse(stem.util.tor_tools.is_valid_fingerprint(fingerprint)) + self.assertFalse(stem.util.tor_tools.is_valid_fingerprint(fingerprint, True)) def test_is_valid_nickname(self): """