[tor-commits] [stem/master] stem.descriptor.networkstatus_descriptor => stem.descriptor.networkstatus
atagar at torproject.org
atagar at torproject.org
Sat Oct 13 18:35:44 UTC 2012
commit 4e29ae8dfc69cb68a25c9cf159e56db4e943565b
Author: Ravi Chandra Padmala <neenaoffline at gmail.com>
Date: Tue Aug 7 12:09:25 2012 +0530
stem.descriptor.networkstatus_descriptor => stem.descriptor.networkstatus
---
run_tests.py | 4 +-
stem/descriptor/__init__.py | 2 +-
stem/descriptor/networkstatus.py | 614 +++++++++++++++++++++
stem/descriptor/networkstatus_descriptor.py | 614 ---------------------
test/integ/descriptor/networkstatus.py | 228 ++++++++
test/integ/descriptor/networkstatus_descriptor.py | 228 --------
6 files changed, 845 insertions(+), 845 deletions(-)
diff --git a/run_tests.py b/run_tests.py
index 10aa24c..8d115f1 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -46,7 +46,7 @@ import test.integ.socket.control_socket
import test.integ.descriptor.reader
import test.integ.descriptor.server_descriptor
import test.integ.descriptor.extrainfo_descriptor
-import test.integ.descriptor.networkstatus_descriptor
+import test.integ.descriptor.networkstatus
import test.integ.response.protocolinfo
import test.integ.util.conf
import test.integ.util.proc
@@ -135,7 +135,7 @@ INTEG_TESTS = (
test.integ.descriptor.reader.TestDescriptorReader,
test.integ.descriptor.server_descriptor.TestServerDescriptor,
test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor,
- test.integ.descriptor.networkstatus_descriptor.TestNetworkStatusDocument,
+ test.integ.descriptor.networkstatus.TestNetworkStatusDocument,
test.integ.version.TestVersion,
test.integ.response.protocolinfo.TestProtocolInfo,
test.integ.process.TestProcess,
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 5afe214..903a877 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -17,7 +17,7 @@ __all__ = [
"reader",
"extrainfo_descriptor",
"server_descriptor",
- "networkstatus_descriptor",
+ "networkstatus",
"parse_file",
"Descriptor",
]
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
new file mode 100644
index 0000000..8daf6f7
--- /dev/null
+++ b/stem/descriptor/networkstatus.py
@@ -0,0 +1,614 @@
+"""
+Parsing for Tor network status documents. Currently supports parsing v3 network
+status documents (both votes and consensus').
+
+The network status documents also contain a list of router descriptors,
+directory authorities, signatures etc.
+
+The votes and consensus' can be obtained from any of the following sources...
+
+* the 'cached-consensus' file in tor's data directory
+* tor metrics, at https://metrics.torproject.org/data.html
+* directory authorities and mirrors via their DirPort
+
+**Module Overview:**
+
+::
+
+ parse_file - parses a network status file and provides a NetworkStatusDocument
+ NetworkStatusDocument - Tor v3 network status document
+ +- MicrodescriptorConsensus - Tor microdescriptor consensus document
+ RouterDescriptor - Router descriptor; contains information about a Tor relay
+ +- RouterMicrodescriptor - Router microdescriptor; contains information that doesn't change often
+ DirectorySignature
+ DirectoryAuthority
+"""
+
+import re
+import base64
+import hashlib
+import datetime
+
+import stem.prereq
+import stem.descriptor
+import stem.descriptor.extrainfo_descriptor
+import stem.version
+import stem.exit_policy
+import stem.util.log as log
+import stem.util.connection
+import stem.util.tor_tools
+
+_bandwidth_weights_regex = re.compile(" ".join(["W%s=\d+" % weight for weight in ["bd",
+ "be", "bg", "bm", "db", "eb", "ed", "ee", "eg", "em", "gb", "gd", "gg", "gm", "mb", "md", "me", "mg", "mm"]]))
+
+def parse_file(document_file, validate = True):
+ """
+ Iterates over the router descriptors in a network status document.
+
+ :param file document_file: file with network status document content
+ :param bool validate: checks the validity of the document's contents if True, skips these checks otherwise
+
+ :returns: iterator for :class:`stem.descriptor.networkstatus_descriptor.RouterDescriptor` instances in the file
+
+ :raises:
+ * ValueError if the contents is malformed and validate is True
+ * IOError if the file can't be read
+ """
+
+ document = NetworkStatusDocument(document_file.read(), validate)
+ return document.router_descriptors
+
+def _strptime(string, validate = True, optional = False):
+ try:
+ return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
+ except ValueError, exc:
+ if validate or not optional: raise exc
+
+class NetworkStatusDocument(stem.descriptor.Descriptor):
+ """
+ A v3 network status document.
+
+ This could be a v3 consensus or vote document.
+
+ :var bool validated: **\*** whether the document is validated
+ :var str network_status_version: **\*** a document format version. For v3 documents this is "3"
+ :var str vote_status: **\*** status of the vote (is either "vote" or "consensus")
+ :var list consensus_methods: A list of supported consensus generation methods (integers)
+ :var datetime published: time when the document was published
+ :var int consensus_method: consensus method used to generate a consensus
+ :var datetime valid_after: **\*** time when the consensus becomes valid
+ :var datetime fresh_until: **\*** time until when the consensus is considered to be fresh
+ :var datetime valid_until: **\*** time until when the consensus is valid
+ :var int vote_delay: **\*** number of seconds allowed for collecting votes from all authorities
+ :var int dist_delay: number of seconds allowed for collecting signatures from all authorities
+ :var list client_versions: list of recommended Tor client versions
+ :var list server_versions: list of recommended Tor server versions
+ :var list known_flags: **\*** list of known router flags
+ :var list params: dict of parameter(str) => value(int) mappings
+ :var list router_descriptors: **\*** list of RouterDescriptor objects defined in the document
+ :var list directory_authorities: **\*** list of DirectoryAuthority objects that have generated this document
+ :var dict bandwidth_weights: dict of weight(str) => value(int) mappings
+ :var list directory_signatures: **\*** list of signatures this document has
+
+ **\*** 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_content, validate = True):
+ """
+ Parse a v3 network status document and provide a new NetworkStatusDocument object.
+
+ :param str raw_content: raw network status document data
+ :param bool validate: True if the document is to be validated, False otherwise
+
+ :raises: ValueError if the document is invalid
+ """
+
+ super(NetworkStatusDocument, self).__init__(raw_content)
+
+ self.router_descriptors = []
+ self.directory_authorities = []
+ self.directory_signatures = []
+ self.validated = validate
+
+ self.network_status_version = None
+ self.vote_status = None
+ self.consensus_methods = []
+ self.published = None
+ self.consensus_method = None
+ self.valid_after = None
+ self.fresh_until = None
+ self.valid_until = None
+ self.vote_delay = None
+ self.dist_delay = None
+ self.client_versions = []
+ self.server_versions = []
+ self.known_flags = []
+ self.params = {}
+ self.bandwidth_weights = {}
+
+ self._parse(raw_content)
+
+ def _generate_router(self, raw_content, vote, validate):
+ return RouterDescriptor(raw_content, vote, validate)
+
+ def _validate_network_status_version(self):
+ return self.network_status_version == "3"
+
+ def get_unrecognized_lines(self):
+ """
+ Returns any unrecognized trailing lines.
+
+ :returns: a list of unrecognized trailing lines
+ """
+
+ return self._unrecognized_lines
+
+ def _parse(self, raw_content):
+ # preamble
+ validate = self.validated
+ doc_parser = stem.descriptor.DescriptorParser(raw_content, validate)
+
+ read_keyword_line = lambda keyword, optional = False: setattr(self, keyword.replace("-", "_"), doc_parser.read_keyword_line(keyword, optional))
+
+ map(read_keyword_line, ["network-status-version", "vote-status"])
+ if validate and not self._validate_network_status_version():
+ raise ValueError("Invalid network-status-version: %s" % self.network_status_version)
+
+ if self.vote_status == "vote": vote = True
+ elif self.vote_status == "consensus": vote = False
+ elif validate: raise ValueError("Unrecognized document type specified in vote-status")
+
+ if vote:
+ read_keyword_line("consensus-methods", True)
+ self.consensus_methods = [int(method) for method in self.consensus_methods.split(" ")]
+ self.published = _strptime(doc_parser.read_keyword_line("published", True), validate, True)
+ else:
+ self.consensus_method = int(doc_parser.read_keyword_line("consensus-method", True))
+
+ map(read_keyword_line, ["valid-after", "fresh-until", "valid-until"])
+ self.valid_after = _strptime(self.valid_after, validate)
+ self.fresh_until = _strptime(self.fresh_until, validate)
+ self.valid_until = _strptime(self.valid_until, validate)
+ voting_delay = doc_parser.read_keyword_line("voting-delay")
+ self.vote_delay, self.dist_delay = [int(delay) for delay in voting_delay.split(" ")]
+
+ client_versions = doc_parser.read_keyword_line("client-versions", True)
+ if client_versions:
+ self.client_versions = [stem.version.Version(version_string) for version_string in client_versions.split(",")]
+ server_versions = doc_parser.read_keyword_line("server-versions", True)
+ if server_versions:
+ self.server_versions = [stem.version.Version(version_string) for version_string in server_versions.split(",")]
+ self.known_flags = doc_parser.read_keyword_line("known-flags").split(" ")
+ read_keyword_line("params", True)
+ if self.params:
+ self.params = dict([(param.split("=")[0], int(param.split("=")[1])) for param in self.params.split(" ")])
+
+ # authority section
+ while doc_parser.line.startswith("dir-source "):
+ dirauth_data = doc_parser.read_until(["dir-source", "r"])
+ self.directory_authorities.append(DirectoryAuthority(dirauth_data, vote, validate))
+
+ def _router_desc_generator(raw_content, vote, validate):
+ parser = stem.descriptor.DescriptorParser(raw_content, validate)
+ while parser.line != None:
+ descriptor = parser.read_until("r")
+ yield self._generate_router(descriptor, vote, validate)
+
+ # router descriptors
+ if doc_parser.peek_keyword() == "r":
+ router_descriptors_data = doc_parser.read_until(["bandwidth-weights", "directory-footer", "directory-signature"])
+ self.router_descriptors = _router_desc_generator(router_descriptors_data, vote, validate)
+ elif validate:
+ raise ValueError("No router descriptors found")
+
+ # footer section
+ if self.consensus_method > 9 or vote and filter(lambda x: x >= 9, self.consensus_methods):
+ if doc_parser.line == "directory-footer":
+ doc_parser.read_line()
+ elif validate:
+ raise ValueError("Network status document missing directory-footer")
+
+ if not vote:
+ read_keyword_line("bandwidth-weights", True)
+ if _bandwidth_weights_regex.match(self.bandwidth_weights):
+ self.bandwidth_weights = dict([(weight.split("=")[0], int(weight.split("=")[1])) for weight in self.bandwidth_weights.split(" ")])
+ elif validate:
+ raise ValueError("Invalid bandwidth-weights line")
+
+ while doc_parser.line.startswith("directory-signature "):
+ signature_data = doc_parser.read_until(["directory-signature"])
+ self.directory_signatures.append(DirectorySignature(signature_data))
+
+ self._unrecognized_lines = doc_parser.remaining()
+ if validate and self._unrecognized_lines: raise ValueError("Unrecognized trailing data")
+
+class DirectoryAuthority(stem.descriptor.Descriptor):
+ """
+ Contains directory authority information obtained from v3 network status
+ documents.
+
+ :var str nickname: directory authority's nickname
+ :var str identity: uppercase hex fingerprint of the authority's identity key
+ :var str address: hostname
+ :var str ip: current IP address
+ :var int dirport: current directory port
+ :var int orport: current orport
+ :var str contact: directory authority's contact information
+ :var str legacy_dir_key: fingerprint of and obsolete identity key
+ :var :class:`stem.descriptor.networkstatus_descriptor.KeyCertificate` key_certificate: directory authority's current key certificate
+ :var str vote_digest: digest of the authority that contributed to the consensus
+ """
+
+ def __init__(self, raw_content, vote = True, validate = True):
+ """
+ Parse a directory authority entry in a v3 network status document and
+ provide a DirectoryAuthority object.
+
+ :param str raw_content: raw directory authority entry information
+ :param bool validate: True if the document is to be validated, False otherwise
+
+ :raises: ValueError if the raw data is invalid
+ """
+
+ super(DirectoryAuthority, self).__init__(raw_content)
+ self.nickname, self.identity, self.address, self.ip = None, None, None, None
+ self.dirport, self.orport, self.legacy_dir_key = None, None, None
+ self.key_certificate, self.contact, self.vote_digest = None, None, None
+ parser = stem.descriptor.DescriptorParser(raw_content, validate)
+
+ dir_source = parser.read_keyword_line("dir-source")
+ self.nickname, self.identity, self.address, self.ip, self.dirport, self.orport = dir_source.split(" ")
+ self.dirport = int(self.dirport)
+ self.orport = int(self.orport)
+
+ self.contact = parser.read_keyword_line("contact")
+ if vote:
+ self.legacy_dir_key = parser.read_keyword_line("legacy-dir-key", True)
+ self.key_certificate = KeyCertificate("\n".join(parser.remaining()), validate)
+ else:
+ self.vote_digest = parser.read_keyword_line("vote-digest", True)
+ rmng = parser.remaining()
+ if rmng and validate:
+ raise ValueError("Unrecognized trailing data in directory authority information")
+
+class KeyCertificate(stem.descriptor.Descriptor):
+ """
+ Directory key certificate.
+
+ :var str key_certificate_version: **\*** version of the key certificate (Should be "3")
+ :var str ip: IP address on which the directory authority is listening
+ :var int port: port on which the directory authority is listening
+ :var str fingerprint: **\*** hex encoded fingerprint of the authority's identity key
+ :var str identity_key: **\*** long term authority identity key
+ :var datetime published: **\*** time (in GMT) when this document & the key were last generated
+ :var str expires: **\*** time (in GMT) after which this key becomes invalid
+ :var str signing_key: **\*** directory server's public signing key
+ :var str crosscert: signature made using certificate's signing key
+ :var str certification: **\*** signature of this key certificate signed with the identity key
+
+ **\*** 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_content, validate = True):
+ """
+ Parse a key certificate entry and provide a KeyCertificate object.
+
+ :param str raw_content: raw key certificate information
+ :param bool validate: True if the document is to be validated, False otherwise
+
+ :raises: ValueError if the raw data is invalid
+ """
+
+ super(KeyCertificate, self).__init__(raw_content)
+ self.key_certificate_version, self.ip, self.port = None, None, None
+ self.fingerprint, self.identity_key, self.published = None, None, None
+ self.expires, self.signing_key, self.crosscert = None, None, None
+ self.certification = None
+ parser = stem.descriptor.DescriptorParser(raw_content, validate)
+ peek_check_kw = lambda keyword: keyword == parser.peek_keyword()
+ seen_keywords = set()
+
+ self.key_certificate_version = parser.read_keyword_line("dir-key-certificate-version")
+ if validate and self.key_certificate_version != "3": raise ValueError("Unrecognized dir-key-certificate-version")
+
+ def _read_keyword_line(keyword):
+ if validate and keyword in seen_keywords:
+ raise ValueError("Invalid key certificate: '%s' appears twice" % keyword)
+ seen_keywords.add(keyword)
+ return parser.read_keyword_line(keyword)
+
+ while parser.line:
+ if peek_check_kw("dir-address"):
+ line = _read_keyword_line("dir-address")
+ try:
+ self.ip, self.port = line.rsplit(":", 1)
+ self.port = int(self.port)
+ except Exception:
+ if validate: raise ValueError("Invalid dir-address line: %s" % line)
+
+ elif peek_check_kw("fingerprint"):
+ self.fingerprint = _read_keyword_line("fingerprint")
+
+ elif peek_check_kw("dir-identity-key"):
+ _read_keyword_line("dir-identity-key")
+ self.identity_key = parser.read_block("RSA PUBLIC KEY")
+
+ elif peek_check_kw("dir-key-published"):
+ self.published = _strptime(_read_keyword_line("dir-key-published"))
+
+ elif peek_check_kw("dir-key-expires"):
+ self.expires = _strptime(_read_keyword_line("dir-key-expires"))
+
+ elif peek_check_kw("dir-signing-key"):
+ _read_keyword_line("dir-signing-key")
+ self.signing_key = parser.read_block("RSA PUBLIC KEY")
+
+ elif peek_check_kw("dir-key-crosscert"):
+ _read_keyword_line("dir-key-crosscert")
+ self.crosscert = parser.read_block("ID SIGNATURE")
+
+ elif peek_check_kw("dir-key-certification"):
+ _read_keyword_line("dir-key-certification")
+ self.certification = parser.read_block("SIGNATURE")
+ break
+
+ elif validate:
+ raise ValueError("Key certificate contains unrecognized lines: %s" % parser.line)
+
+ else:
+ # ignore unrecognized lines if we aren't validating
+ self._unrecognized_lines.append(parser.read_line())
+
+ self._unrecognized_lines = parser.remaining()
+ if self._unrecognized_lines and validate:
+ raise ValueError("Unrecognized trailing data in key certificate")
+
+ def get_unrecognized_lines(self):
+ """
+ Returns any unrecognized lines.
+
+ :returns: a list of unrecognized lines
+ """
+
+ return self._unrecognized_lines
+
+class DirectorySignature(stem.descriptor.Descriptor):
+ """
+ Contains directory signature information described in a v3 network status
+ document.
+
+ :var str identity: signature identity
+ :var str key_digest: signature key digest
+ :var str method: method used to generate the signature
+ :var str signature: the signature data
+ """
+
+ def __init__(self, raw_content, validate = True):
+ """
+ Parse a directory signature entry in a v3 network status document and
+ provide a DirectorySignature object.
+
+ :param str raw_content: raw directory signature entry information
+ :param bool validate: True if the document is to be validated, False otherwise
+
+ :raises: ValueError if the raw data is invalid
+ """
+
+ super(DirectorySignature, self).__init__(raw_content)
+ self.identity, self.key_digest, self.method, self.signature = None, None, None, None
+ parser = stem.descriptor.DescriptorParser(raw_content, validate)
+
+ signature_line = parser.read_keyword_line("directory-signature").split(" ")
+
+ if len(signature_line) == 2:
+ self.identity, self.key_digest = signature_line
+ if len(signature_line) == 3: # for microdescriptor consensuses
+ self.method, self.identity, self.key_digest = signature_line
+
+ self.signature = parser.read_block("SIGNATURE")
+ self._unrecognized_lines = parser.remaining()
+ if self._unrecognized_lines and validate:
+ raise ValueError("Unrecognized trailing data in directory signature")
+
+class RouterDescriptor(stem.descriptor.Descriptor):
+ """
+ Router descriptor object. Parses and stores router information in a router
+ entry read from a v3 network status document.
+
+ :var str nickname: **\*** router's nickname
+ :var str identity: **\*** router's identity
+ :var str digest: **\*** router's digest
+ :var datetime publication: **\*** router's publication
+ :var str ip: **\*** router's IP address
+ :var int orport: **\*** router's ORPort
+ :var int dirport: **\*** router's DirPort
+
+ :var bool is_valid: **\*** router is valid
+ :var bool is_guard: **\*** router is suitable for use as an entry guard
+ :var bool is_named: **\*** router is named
+ :var bool is_unnamed: **\*** router is unnamed
+ :var bool is_running: **\*** router is running and currently usable
+ :var bool is_stable: **\*** router is stable, i.e., it's suitable for for long-lived circuits
+ :var bool is_exit: **\*** router is an exit router
+ :var bool is_fast: **\*** router is Fast, i.e., it's usable for high-bandwidth circuits
+ :var bool is_authority: **\*** router is a directory authority
+ :var bool supports_v2dir: **\*** router supports v2dir
+ :var bool supports_v3dir: **\*** router supports v3dir
+ :var bool is_hsdir: **\*** router is a hidden status
+ :var bool is_badexit: **\*** router is marked a bad exit
+ :var bool is_baddirectory: **\*** router is a bad directory
+
+ :var :class:`stem.version.Version`,str version: Version of the Tor protocol this router is running
+
+ :var int bandwidth: router's claimed bandwidth
+ :var int measured_bandwidth: router's measured bandwidth
+
+ :var :class:`stem.exit_policy.MicrodescriptorExitPolicy` exitpolicy: router's exitpolicy
+
+ :var str microdescriptor_hashes: a list of two-tuples with a list of consensus methods(int) that may produce the digest and a dict with algorithm(str) => digest(str) mappings. algorithm is the hashing algorithm (usually "sha256") that is used to produce digest (the base64 encoding of the hash of the router's microdescriptor with trailing =s omitted).
+
+ **\*** 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, vote = True, validate = True):
+ """
+ Parse a router descriptor in a v3 network status document and provide a new
+ RouterDescriptor object.
+
+ :param str raw_content: router descriptor content to be parsed
+ :param bool validate: whether the router descriptor should be validated
+ """
+
+ super(RouterDescriptor, self).__init__(raw_contents)
+
+ self.nickname = None
+ self.identity = None
+ self.digest = None
+ self.publication = None
+ self.ip = None
+ self.orport = None
+ self.dirport = None
+
+ self.is_valid = False
+ self.is_guard = False
+ self.is_named = False
+ self.is_unnamed = False
+ self.is_running = False
+ self.is_stable = False
+ self.is_exit = False
+ self.is_fast = False
+ self.is_authority = False
+ self.supports_v2dir = False
+ self.supports_v3dir = False
+ self.is_hsdir = False
+ self.is_badexit = False
+ self.is_baddirectory = False
+
+ self.version = None
+
+ self.bandwidth = None
+ self.measured_bandwidth = None
+
+ self.exit_policy = None
+
+ self.microdescriptor_hashes = []
+
+ self._parse(raw_contents, vote, validate)
+
+ def _parse(self, raw_content, vote, validate):
+ """
+ :param dict raw_content: iptor contents to be applied
+ :param bool validate: checks the validity of descriptor content if True
+
+ :raises: ValueError if an error occures in validation
+ """
+
+ parser = stem.descriptor.DescriptorParser(raw_content, validate)
+ seen_keywords = set()
+ peek_check_kw = lambda keyword: keyword == parser.peek_keyword()
+
+ r = parser.read_keyword_line("r")
+ # r mauer BD7xbfsCFku3+tgybEZsg8Yjhvw itcuKQ6PuPLJ7m/Oi928WjO2j8g 2012-06-22 13:19:32 80.101.105.103 9001 0
+ # "r" SP nickname SP identity SP digest SP publication SP IP SP ORPort SP DirPort NL
+ seen_keywords.add("r")
+ if r:
+ values = r.split(" ")
+ self.nickname, self.identity, self.digest = values[0], values[1], values[2]
+ self.publication = _strptime(" ".join((values[3], values[4])), validate)
+ self.ip, self.orport, self.dirport = values[5], int(values[6]), int(values[7])
+ if self.dirport == 0: self.dirport = None
+ elif validate: raise ValueError("Invalid router descriptor: empty 'r' line" )
+
+ while parser.line:
+ if peek_check_kw("s"):
+ if "s" in seen_keywords: raise ValueError("Invalid router descriptor: 's' line appears twice")
+ line = parser.read_keyword_line("s")
+ if not line: continue
+ seen_keywords.add("s")
+ # s Named Running Stable Valid
+ #A series of space-separated status flags, in *lexical order*
+ flags = line.split(" ")
+ flag_map = {
+ "Valid": "is_valid",
+ "Guard": "is_guard",
+ "Named": "is_named",
+ "Unnamed": "is_unnamed",
+ "Running": "is_running",
+ "Stable": "is_stable",
+ "Exit": "is_exit",
+ "Fast": "is_fast",
+ "Authority": "is_authority",
+ "V2Dir": "supports_v2dir",
+ "V3Dir": "supports_v3dir",
+ "HSDir": "is_hsdir",
+ "BadExit": "is_badexit",
+ "BadDirectory": "is_baddirectory",
+ }
+ map(lambda flag: setattr(self, flag_map[flag], True), flags)
+
+ if self.is_unnamed: self.is_named = False
+ elif self.is_named: self.is_unnamed = False
+
+ elif peek_check_kw("v"):
+ if "v" in seen_keywords: raise ValueError("Invalid router descriptor: 'v' line appears twice")
+ line = parser.read_keyword_line("v", True)
+ seen_keywords.add("v")
+ # v Tor 0.2.2.35
+ if line:
+ if line.startswith("Tor "):
+ self.version = stem.version.Version(line[4:])
+ else:
+ self.version = line
+ elif validate: raise ValueError("Invalid router descriptor: empty 'v' line" )
+
+ elif peek_check_kw("w"):
+ if "w" in seen_keywords: raise ValueError("Invalid router descriptor: 'w' line appears twice")
+ w = parser.read_keyword_line("w", True)
+ # "w" SP "Bandwidth=" INT [SP "Measured=" INT] NL
+ seen_keywords.add("w")
+ if w:
+ values = w.split(" ")
+ if len(values) <= 2 and len(values) > 0:
+ key, value = values[0].split("=")
+ if key == "Bandwidth": self.bandwidth = int(value)
+ elif validate: raise ValueError("Router descriptor contains invalid 'w' line: expected Bandwidth, read " + key)
+
+ if len(values) == 2:
+ key, value = values[1].split("=")
+ if key == "Measured": self.measured_bandwidth = int(value)
+ elif validate: raise ValueError("Router descriptor contains invalid 'w' line: expected Measured, read " + key)
+ elif validate: raise ValueError("Router descriptor contains invalid 'w' line")
+ elif validate: raise ValueError("Router descriptor contains empty 'w' line")
+
+ elif peek_check_kw("p"):
+ if "p" in seen_keywords: raise ValueError("Invalid router descriptor: 'p' line appears twice")
+ p = parser.read_keyword_line("p", True)
+ seen_keywords.add("p")
+ # "p" SP ("accept" / "reject") SP PortList NL
+ if p:
+ self.exit_policy = stem.exit_policy.MicrodescriptorExitPolicy(p)
+ #self.exit_policy = p
+
+ elif vote and peek_check_kw("m"):
+ # microdescriptor hashes
+ m = parser.read_keyword_line("m", True)
+ methods, digests = m.split(" ", 1)
+ method_list = methods.split(",")
+ digest_dict = [digest.split("=", 1) for digest in digests.split(" ")]
+ self.microdescriptor_hashes.append((method_list, digest_dict))
+
+ elif validate:
+ raise ValueError("Router descriptor contains unrecognized trailing lines: %s" % parser.line)
+
+ else:
+ self._unrecognized_lines.append(parser.read_line()) # ignore unrecognized lines if we aren't validating
+
+ def get_unrecognized_lines(self):
+ """
+ Returns any unrecognized lines.
+
+ :returns: a list of unrecognized lines
+ """
+
+ return self._unrecognized_lines
+
diff --git a/stem/descriptor/networkstatus_descriptor.py b/stem/descriptor/networkstatus_descriptor.py
deleted file mode 100644
index 8daf6f7..0000000
--- a/stem/descriptor/networkstatus_descriptor.py
+++ /dev/null
@@ -1,614 +0,0 @@
-"""
-Parsing for Tor network status documents. Currently supports parsing v3 network
-status documents (both votes and consensus').
-
-The network status documents also contain a list of router descriptors,
-directory authorities, signatures etc.
-
-The votes and consensus' can be obtained from any of the following sources...
-
-* the 'cached-consensus' file in tor's data directory
-* tor metrics, at https://metrics.torproject.org/data.html
-* directory authorities and mirrors via their DirPort
-
-**Module Overview:**
-
-::
-
- parse_file - parses a network status file and provides a NetworkStatusDocument
- NetworkStatusDocument - Tor v3 network status document
- +- MicrodescriptorConsensus - Tor microdescriptor consensus document
- RouterDescriptor - Router descriptor; contains information about a Tor relay
- +- RouterMicrodescriptor - Router microdescriptor; contains information that doesn't change often
- DirectorySignature
- DirectoryAuthority
-"""
-
-import re
-import base64
-import hashlib
-import datetime
-
-import stem.prereq
-import stem.descriptor
-import stem.descriptor.extrainfo_descriptor
-import stem.version
-import stem.exit_policy
-import stem.util.log as log
-import stem.util.connection
-import stem.util.tor_tools
-
-_bandwidth_weights_regex = re.compile(" ".join(["W%s=\d+" % weight for weight in ["bd",
- "be", "bg", "bm", "db", "eb", "ed", "ee", "eg", "em", "gb", "gd", "gg", "gm", "mb", "md", "me", "mg", "mm"]]))
-
-def parse_file(document_file, validate = True):
- """
- Iterates over the router descriptors in a network status document.
-
- :param file document_file: file with network status document content
- :param bool validate: checks the validity of the document's contents if True, skips these checks otherwise
-
- :returns: iterator for :class:`stem.descriptor.networkstatus_descriptor.RouterDescriptor` instances in the file
-
- :raises:
- * ValueError if the contents is malformed and validate is True
- * IOError if the file can't be read
- """
-
- document = NetworkStatusDocument(document_file.read(), validate)
- return document.router_descriptors
-
-def _strptime(string, validate = True, optional = False):
- try:
- return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
- except ValueError, exc:
- if validate or not optional: raise exc
-
-class NetworkStatusDocument(stem.descriptor.Descriptor):
- """
- A v3 network status document.
-
- This could be a v3 consensus or vote document.
-
- :var bool validated: **\*** whether the document is validated
- :var str network_status_version: **\*** a document format version. For v3 documents this is "3"
- :var str vote_status: **\*** status of the vote (is either "vote" or "consensus")
- :var list consensus_methods: A list of supported consensus generation methods (integers)
- :var datetime published: time when the document was published
- :var int consensus_method: consensus method used to generate a consensus
- :var datetime valid_after: **\*** time when the consensus becomes valid
- :var datetime fresh_until: **\*** time until when the consensus is considered to be fresh
- :var datetime valid_until: **\*** time until when the consensus is valid
- :var int vote_delay: **\*** number of seconds allowed for collecting votes from all authorities
- :var int dist_delay: number of seconds allowed for collecting signatures from all authorities
- :var list client_versions: list of recommended Tor client versions
- :var list server_versions: list of recommended Tor server versions
- :var list known_flags: **\*** list of known router flags
- :var list params: dict of parameter(str) => value(int) mappings
- :var list router_descriptors: **\*** list of RouterDescriptor objects defined in the document
- :var list directory_authorities: **\*** list of DirectoryAuthority objects that have generated this document
- :var dict bandwidth_weights: dict of weight(str) => value(int) mappings
- :var list directory_signatures: **\*** list of signatures this document has
-
- **\*** 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_content, validate = True):
- """
- Parse a v3 network status document and provide a new NetworkStatusDocument object.
-
- :param str raw_content: raw network status document data
- :param bool validate: True if the document is to be validated, False otherwise
-
- :raises: ValueError if the document is invalid
- """
-
- super(NetworkStatusDocument, self).__init__(raw_content)
-
- self.router_descriptors = []
- self.directory_authorities = []
- self.directory_signatures = []
- self.validated = validate
-
- self.network_status_version = None
- self.vote_status = None
- self.consensus_methods = []
- self.published = None
- self.consensus_method = None
- self.valid_after = None
- self.fresh_until = None
- self.valid_until = None
- self.vote_delay = None
- self.dist_delay = None
- self.client_versions = []
- self.server_versions = []
- self.known_flags = []
- self.params = {}
- self.bandwidth_weights = {}
-
- self._parse(raw_content)
-
- def _generate_router(self, raw_content, vote, validate):
- return RouterDescriptor(raw_content, vote, validate)
-
- def _validate_network_status_version(self):
- return self.network_status_version == "3"
-
- def get_unrecognized_lines(self):
- """
- Returns any unrecognized trailing lines.
-
- :returns: a list of unrecognized trailing lines
- """
-
- return self._unrecognized_lines
-
- def _parse(self, raw_content):
- # preamble
- validate = self.validated
- doc_parser = stem.descriptor.DescriptorParser(raw_content, validate)
-
- read_keyword_line = lambda keyword, optional = False: setattr(self, keyword.replace("-", "_"), doc_parser.read_keyword_line(keyword, optional))
-
- map(read_keyword_line, ["network-status-version", "vote-status"])
- if validate and not self._validate_network_status_version():
- raise ValueError("Invalid network-status-version: %s" % self.network_status_version)
-
- if self.vote_status == "vote": vote = True
- elif self.vote_status == "consensus": vote = False
- elif validate: raise ValueError("Unrecognized document type specified in vote-status")
-
- if vote:
- read_keyword_line("consensus-methods", True)
- self.consensus_methods = [int(method) for method in self.consensus_methods.split(" ")]
- self.published = _strptime(doc_parser.read_keyword_line("published", True), validate, True)
- else:
- self.consensus_method = int(doc_parser.read_keyword_line("consensus-method", True))
-
- map(read_keyword_line, ["valid-after", "fresh-until", "valid-until"])
- self.valid_after = _strptime(self.valid_after, validate)
- self.fresh_until = _strptime(self.fresh_until, validate)
- self.valid_until = _strptime(self.valid_until, validate)
- voting_delay = doc_parser.read_keyword_line("voting-delay")
- self.vote_delay, self.dist_delay = [int(delay) for delay in voting_delay.split(" ")]
-
- client_versions = doc_parser.read_keyword_line("client-versions", True)
- if client_versions:
- self.client_versions = [stem.version.Version(version_string) for version_string in client_versions.split(",")]
- server_versions = doc_parser.read_keyword_line("server-versions", True)
- if server_versions:
- self.server_versions = [stem.version.Version(version_string) for version_string in server_versions.split(",")]
- self.known_flags = doc_parser.read_keyword_line("known-flags").split(" ")
- read_keyword_line("params", True)
- if self.params:
- self.params = dict([(param.split("=")[0], int(param.split("=")[1])) for param in self.params.split(" ")])
-
- # authority section
- while doc_parser.line.startswith("dir-source "):
- dirauth_data = doc_parser.read_until(["dir-source", "r"])
- self.directory_authorities.append(DirectoryAuthority(dirauth_data, vote, validate))
-
- def _router_desc_generator(raw_content, vote, validate):
- parser = stem.descriptor.DescriptorParser(raw_content, validate)
- while parser.line != None:
- descriptor = parser.read_until("r")
- yield self._generate_router(descriptor, vote, validate)
-
- # router descriptors
- if doc_parser.peek_keyword() == "r":
- router_descriptors_data = doc_parser.read_until(["bandwidth-weights", "directory-footer", "directory-signature"])
- self.router_descriptors = _router_desc_generator(router_descriptors_data, vote, validate)
- elif validate:
- raise ValueError("No router descriptors found")
-
- # footer section
- if self.consensus_method > 9 or vote and filter(lambda x: x >= 9, self.consensus_methods):
- if doc_parser.line == "directory-footer":
- doc_parser.read_line()
- elif validate:
- raise ValueError("Network status document missing directory-footer")
-
- if not vote:
- read_keyword_line("bandwidth-weights", True)
- if _bandwidth_weights_regex.match(self.bandwidth_weights):
- self.bandwidth_weights = dict([(weight.split("=")[0], int(weight.split("=")[1])) for weight in self.bandwidth_weights.split(" ")])
- elif validate:
- raise ValueError("Invalid bandwidth-weights line")
-
- while doc_parser.line.startswith("directory-signature "):
- signature_data = doc_parser.read_until(["directory-signature"])
- self.directory_signatures.append(DirectorySignature(signature_data))
-
- self._unrecognized_lines = doc_parser.remaining()
- if validate and self._unrecognized_lines: raise ValueError("Unrecognized trailing data")
-
-class DirectoryAuthority(stem.descriptor.Descriptor):
- """
- Contains directory authority information obtained from v3 network status
- documents.
-
- :var str nickname: directory authority's nickname
- :var str identity: uppercase hex fingerprint of the authority's identity key
- :var str address: hostname
- :var str ip: current IP address
- :var int dirport: current directory port
- :var int orport: current orport
- :var str contact: directory authority's contact information
- :var str legacy_dir_key: fingerprint of and obsolete identity key
- :var :class:`stem.descriptor.networkstatus_descriptor.KeyCertificate` key_certificate: directory authority's current key certificate
- :var str vote_digest: digest of the authority that contributed to the consensus
- """
-
- def __init__(self, raw_content, vote = True, validate = True):
- """
- Parse a directory authority entry in a v3 network status document and
- provide a DirectoryAuthority object.
-
- :param str raw_content: raw directory authority entry information
- :param bool validate: True if the document is to be validated, False otherwise
-
- :raises: ValueError if the raw data is invalid
- """
-
- super(DirectoryAuthority, self).__init__(raw_content)
- self.nickname, self.identity, self.address, self.ip = None, None, None, None
- self.dirport, self.orport, self.legacy_dir_key = None, None, None
- self.key_certificate, self.contact, self.vote_digest = None, None, None
- parser = stem.descriptor.DescriptorParser(raw_content, validate)
-
- dir_source = parser.read_keyword_line("dir-source")
- self.nickname, self.identity, self.address, self.ip, self.dirport, self.orport = dir_source.split(" ")
- self.dirport = int(self.dirport)
- self.orport = int(self.orport)
-
- self.contact = parser.read_keyword_line("contact")
- if vote:
- self.legacy_dir_key = parser.read_keyword_line("legacy-dir-key", True)
- self.key_certificate = KeyCertificate("\n".join(parser.remaining()), validate)
- else:
- self.vote_digest = parser.read_keyword_line("vote-digest", True)
- rmng = parser.remaining()
- if rmng and validate:
- raise ValueError("Unrecognized trailing data in directory authority information")
-
-class KeyCertificate(stem.descriptor.Descriptor):
- """
- Directory key certificate.
-
- :var str key_certificate_version: **\*** version of the key certificate (Should be "3")
- :var str ip: IP address on which the directory authority is listening
- :var int port: port on which the directory authority is listening
- :var str fingerprint: **\*** hex encoded fingerprint of the authority's identity key
- :var str identity_key: **\*** long term authority identity key
- :var datetime published: **\*** time (in GMT) when this document & the key were last generated
- :var str expires: **\*** time (in GMT) after which this key becomes invalid
- :var str signing_key: **\*** directory server's public signing key
- :var str crosscert: signature made using certificate's signing key
- :var str certification: **\*** signature of this key certificate signed with the identity key
-
- **\*** 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_content, validate = True):
- """
- Parse a key certificate entry and provide a KeyCertificate object.
-
- :param str raw_content: raw key certificate information
- :param bool validate: True if the document is to be validated, False otherwise
-
- :raises: ValueError if the raw data is invalid
- """
-
- super(KeyCertificate, self).__init__(raw_content)
- self.key_certificate_version, self.ip, self.port = None, None, None
- self.fingerprint, self.identity_key, self.published = None, None, None
- self.expires, self.signing_key, self.crosscert = None, None, None
- self.certification = None
- parser = stem.descriptor.DescriptorParser(raw_content, validate)
- peek_check_kw = lambda keyword: keyword == parser.peek_keyword()
- seen_keywords = set()
-
- self.key_certificate_version = parser.read_keyword_line("dir-key-certificate-version")
- if validate and self.key_certificate_version != "3": raise ValueError("Unrecognized dir-key-certificate-version")
-
- def _read_keyword_line(keyword):
- if validate and keyword in seen_keywords:
- raise ValueError("Invalid key certificate: '%s' appears twice" % keyword)
- seen_keywords.add(keyword)
- return parser.read_keyword_line(keyword)
-
- while parser.line:
- if peek_check_kw("dir-address"):
- line = _read_keyword_line("dir-address")
- try:
- self.ip, self.port = line.rsplit(":", 1)
- self.port = int(self.port)
- except Exception:
- if validate: raise ValueError("Invalid dir-address line: %s" % line)
-
- elif peek_check_kw("fingerprint"):
- self.fingerprint = _read_keyword_line("fingerprint")
-
- elif peek_check_kw("dir-identity-key"):
- _read_keyword_line("dir-identity-key")
- self.identity_key = parser.read_block("RSA PUBLIC KEY")
-
- elif peek_check_kw("dir-key-published"):
- self.published = _strptime(_read_keyword_line("dir-key-published"))
-
- elif peek_check_kw("dir-key-expires"):
- self.expires = _strptime(_read_keyword_line("dir-key-expires"))
-
- elif peek_check_kw("dir-signing-key"):
- _read_keyword_line("dir-signing-key")
- self.signing_key = parser.read_block("RSA PUBLIC KEY")
-
- elif peek_check_kw("dir-key-crosscert"):
- _read_keyword_line("dir-key-crosscert")
- self.crosscert = parser.read_block("ID SIGNATURE")
-
- elif peek_check_kw("dir-key-certification"):
- _read_keyword_line("dir-key-certification")
- self.certification = parser.read_block("SIGNATURE")
- break
-
- elif validate:
- raise ValueError("Key certificate contains unrecognized lines: %s" % parser.line)
-
- else:
- # ignore unrecognized lines if we aren't validating
- self._unrecognized_lines.append(parser.read_line())
-
- self._unrecognized_lines = parser.remaining()
- if self._unrecognized_lines and validate:
- raise ValueError("Unrecognized trailing data in key certificate")
-
- def get_unrecognized_lines(self):
- """
- Returns any unrecognized lines.
-
- :returns: a list of unrecognized lines
- """
-
- return self._unrecognized_lines
-
-class DirectorySignature(stem.descriptor.Descriptor):
- """
- Contains directory signature information described in a v3 network status
- document.
-
- :var str identity: signature identity
- :var str key_digest: signature key digest
- :var str method: method used to generate the signature
- :var str signature: the signature data
- """
-
- def __init__(self, raw_content, validate = True):
- """
- Parse a directory signature entry in a v3 network status document and
- provide a DirectorySignature object.
-
- :param str raw_content: raw directory signature entry information
- :param bool validate: True if the document is to be validated, False otherwise
-
- :raises: ValueError if the raw data is invalid
- """
-
- super(DirectorySignature, self).__init__(raw_content)
- self.identity, self.key_digest, self.method, self.signature = None, None, None, None
- parser = stem.descriptor.DescriptorParser(raw_content, validate)
-
- signature_line = parser.read_keyword_line("directory-signature").split(" ")
-
- if len(signature_line) == 2:
- self.identity, self.key_digest = signature_line
- if len(signature_line) == 3: # for microdescriptor consensuses
- self.method, self.identity, self.key_digest = signature_line
-
- self.signature = parser.read_block("SIGNATURE")
- self._unrecognized_lines = parser.remaining()
- if self._unrecognized_lines and validate:
- raise ValueError("Unrecognized trailing data in directory signature")
-
-class RouterDescriptor(stem.descriptor.Descriptor):
- """
- Router descriptor object. Parses and stores router information in a router
- entry read from a v3 network status document.
-
- :var str nickname: **\*** router's nickname
- :var str identity: **\*** router's identity
- :var str digest: **\*** router's digest
- :var datetime publication: **\*** router's publication
- :var str ip: **\*** router's IP address
- :var int orport: **\*** router's ORPort
- :var int dirport: **\*** router's DirPort
-
- :var bool is_valid: **\*** router is valid
- :var bool is_guard: **\*** router is suitable for use as an entry guard
- :var bool is_named: **\*** router is named
- :var bool is_unnamed: **\*** router is unnamed
- :var bool is_running: **\*** router is running and currently usable
- :var bool is_stable: **\*** router is stable, i.e., it's suitable for for long-lived circuits
- :var bool is_exit: **\*** router is an exit router
- :var bool is_fast: **\*** router is Fast, i.e., it's usable for high-bandwidth circuits
- :var bool is_authority: **\*** router is a directory authority
- :var bool supports_v2dir: **\*** router supports v2dir
- :var bool supports_v3dir: **\*** router supports v3dir
- :var bool is_hsdir: **\*** router is a hidden status
- :var bool is_badexit: **\*** router is marked a bad exit
- :var bool is_baddirectory: **\*** router is a bad directory
-
- :var :class:`stem.version.Version`,str version: Version of the Tor protocol this router is running
-
- :var int bandwidth: router's claimed bandwidth
- :var int measured_bandwidth: router's measured bandwidth
-
- :var :class:`stem.exit_policy.MicrodescriptorExitPolicy` exitpolicy: router's exitpolicy
-
- :var str microdescriptor_hashes: a list of two-tuples with a list of consensus methods(int) that may produce the digest and a dict with algorithm(str) => digest(str) mappings. algorithm is the hashing algorithm (usually "sha256") that is used to produce digest (the base64 encoding of the hash of the router's microdescriptor with trailing =s omitted).
-
- **\*** 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, vote = True, validate = True):
- """
- Parse a router descriptor in a v3 network status document and provide a new
- RouterDescriptor object.
-
- :param str raw_content: router descriptor content to be parsed
- :param bool validate: whether the router descriptor should be validated
- """
-
- super(RouterDescriptor, self).__init__(raw_contents)
-
- self.nickname = None
- self.identity = None
- self.digest = None
- self.publication = None
- self.ip = None
- self.orport = None
- self.dirport = None
-
- self.is_valid = False
- self.is_guard = False
- self.is_named = False
- self.is_unnamed = False
- self.is_running = False
- self.is_stable = False
- self.is_exit = False
- self.is_fast = False
- self.is_authority = False
- self.supports_v2dir = False
- self.supports_v3dir = False
- self.is_hsdir = False
- self.is_badexit = False
- self.is_baddirectory = False
-
- self.version = None
-
- self.bandwidth = None
- self.measured_bandwidth = None
-
- self.exit_policy = None
-
- self.microdescriptor_hashes = []
-
- self._parse(raw_contents, vote, validate)
-
- def _parse(self, raw_content, vote, validate):
- """
- :param dict raw_content: iptor contents to be applied
- :param bool validate: checks the validity of descriptor content if True
-
- :raises: ValueError if an error occures in validation
- """
-
- parser = stem.descriptor.DescriptorParser(raw_content, validate)
- seen_keywords = set()
- peek_check_kw = lambda keyword: keyword == parser.peek_keyword()
-
- r = parser.read_keyword_line("r")
- # r mauer BD7xbfsCFku3+tgybEZsg8Yjhvw itcuKQ6PuPLJ7m/Oi928WjO2j8g 2012-06-22 13:19:32 80.101.105.103 9001 0
- # "r" SP nickname SP identity SP digest SP publication SP IP SP ORPort SP DirPort NL
- seen_keywords.add("r")
- if r:
- values = r.split(" ")
- self.nickname, self.identity, self.digest = values[0], values[1], values[2]
- self.publication = _strptime(" ".join((values[3], values[4])), validate)
- self.ip, self.orport, self.dirport = values[5], int(values[6]), int(values[7])
- if self.dirport == 0: self.dirport = None
- elif validate: raise ValueError("Invalid router descriptor: empty 'r' line" )
-
- while parser.line:
- if peek_check_kw("s"):
- if "s" in seen_keywords: raise ValueError("Invalid router descriptor: 's' line appears twice")
- line = parser.read_keyword_line("s")
- if not line: continue
- seen_keywords.add("s")
- # s Named Running Stable Valid
- #A series of space-separated status flags, in *lexical order*
- flags = line.split(" ")
- flag_map = {
- "Valid": "is_valid",
- "Guard": "is_guard",
- "Named": "is_named",
- "Unnamed": "is_unnamed",
- "Running": "is_running",
- "Stable": "is_stable",
- "Exit": "is_exit",
- "Fast": "is_fast",
- "Authority": "is_authority",
- "V2Dir": "supports_v2dir",
- "V3Dir": "supports_v3dir",
- "HSDir": "is_hsdir",
- "BadExit": "is_badexit",
- "BadDirectory": "is_baddirectory",
- }
- map(lambda flag: setattr(self, flag_map[flag], True), flags)
-
- if self.is_unnamed: self.is_named = False
- elif self.is_named: self.is_unnamed = False
-
- elif peek_check_kw("v"):
- if "v" in seen_keywords: raise ValueError("Invalid router descriptor: 'v' line appears twice")
- line = parser.read_keyword_line("v", True)
- seen_keywords.add("v")
- # v Tor 0.2.2.35
- if line:
- if line.startswith("Tor "):
- self.version = stem.version.Version(line[4:])
- else:
- self.version = line
- elif validate: raise ValueError("Invalid router descriptor: empty 'v' line" )
-
- elif peek_check_kw("w"):
- if "w" in seen_keywords: raise ValueError("Invalid router descriptor: 'w' line appears twice")
- w = parser.read_keyword_line("w", True)
- # "w" SP "Bandwidth=" INT [SP "Measured=" INT] NL
- seen_keywords.add("w")
- if w:
- values = w.split(" ")
- if len(values) <= 2 and len(values) > 0:
- key, value = values[0].split("=")
- if key == "Bandwidth": self.bandwidth = int(value)
- elif validate: raise ValueError("Router descriptor contains invalid 'w' line: expected Bandwidth, read " + key)
-
- if len(values) == 2:
- key, value = values[1].split("=")
- if key == "Measured": self.measured_bandwidth = int(value)
- elif validate: raise ValueError("Router descriptor contains invalid 'w' line: expected Measured, read " + key)
- elif validate: raise ValueError("Router descriptor contains invalid 'w' line")
- elif validate: raise ValueError("Router descriptor contains empty 'w' line")
-
- elif peek_check_kw("p"):
- if "p" in seen_keywords: raise ValueError("Invalid router descriptor: 'p' line appears twice")
- p = parser.read_keyword_line("p", True)
- seen_keywords.add("p")
- # "p" SP ("accept" / "reject") SP PortList NL
- if p:
- self.exit_policy = stem.exit_policy.MicrodescriptorExitPolicy(p)
- #self.exit_policy = p
-
- elif vote and peek_check_kw("m"):
- # microdescriptor hashes
- m = parser.read_keyword_line("m", True)
- methods, digests = m.split(" ", 1)
- method_list = methods.split(",")
- digest_dict = [digest.split("=", 1) for digest in digests.split(" ")]
- self.microdescriptor_hashes.append((method_list, digest_dict))
-
- elif validate:
- raise ValueError("Router descriptor contains unrecognized trailing lines: %s" % parser.line)
-
- else:
- self._unrecognized_lines.append(parser.read_line()) # ignore unrecognized lines if we aren't validating
-
- def get_unrecognized_lines(self):
- """
- Returns any unrecognized lines.
-
- :returns: a list of unrecognized lines
- """
-
- return self._unrecognized_lines
-
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
new file mode 100644
index 0000000..e65d960
--- /dev/null
+++ b/test/integ/descriptor/networkstatus.py
@@ -0,0 +1,228 @@
+"""
+Integration tests for stem.descriptor.server_descriptor.
+"""
+
+from __future__ import with_statement
+
+import os
+import resource
+import datetime
+import unittest
+
+import stem.exit_policy
+import stem.version
+import stem.descriptor.networkstatus
+import test.integ.descriptor
+
+def _strptime(string):
+ return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
+
+class TestNetworkStatusDocument(unittest.TestCase):
+ def test_metrics_consensus(self):
+ """
+ Checks if consensus documents from Metrics are parsed properly.
+ """
+
+ descriptor_path = test.integ.descriptor.get_resource("metrics_consensus")
+
+ with file(descriptor_path) as descriptor_file:
+ desc = stem.descriptor.parse_file(descriptor_path, descriptor_file)
+
+ router = next(desc)
+ self.assertEquals("sumkledi", router.nickname)
+ self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router.identity)
+ self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router.digest)
+ self.assertEquals(_strptime("2012-07-12 04:01:55"), router.publication)
+ self.assertEquals("178.218.213.229", router.ip)
+ self.assertEquals(80, router.orport)
+ self.assertEquals(None, router.dirport)
+
+ def test_cached_consensus(self):
+ """
+ Parses the cached-consensus file in our data directory.
+ """
+
+ # lengthy test and uneffected by targets, so only run once
+ if test.runner.only_run_once(self, "test_cached_consensus"): return
+
+ descriptor_path = test.runner.get_runner().get_test_dir("cached-consensus")
+
+ if not os.path.exists(descriptor_path):
+ test.runner.skip(self, "(no cached-consensus)")
+
+ if stem.util.system.is_windows():
+ # might hog memory and hang the system
+ # and we aren't checking for memory usage in windows, so, skip.
+ test.runner.skip(self, "(unavailable on windows)")
+
+ count = 0
+ with open(descriptor_path) as descriptor_file:
+ for desc in stem.descriptor.networkstatus.parse_file(descriptor_file):
+ if resource.getrusage(resource.RUSAGE_SELF).ru_maxrss > 100000:
+ # if we're using > 100 MB we should fail
+ self.fail()
+ assert desc.nickname # check that the router has a nickname
+ count += 1
+
+ assert count > 100 # sanity check - assuming atleast 100 relays in the Tor network
+
+ def test_consensus(self):
+ """
+ Checks that consensus documents are properly parsed.
+ """
+
+ descriptor_path = test.integ.descriptor.get_resource("consensus")
+
+ descriptor_file = file(descriptor_path)
+ desc = stem.descriptor.networkstatus.NetworkStatusDocument(descriptor_file.read())
+ descriptor_file.close()
+
+ self.assertEquals(True, desc.validated)
+ self.assertEquals("3", desc.network_status_version)
+ self.assertEquals("consensus", desc.vote_status)
+ self.assertEquals([], desc.consensus_methods)
+ self.assertEquals(None, desc.published)
+ self.assertEquals(12, desc.consensus_method)
+ self.assertEquals(_strptime("2012-07-12 10:00:00"), desc.valid_after)
+ self.assertEquals(_strptime("2012-07-12 11:00:00"), desc.fresh_until)
+ self.assertEquals(_strptime("2012-07-12 13:00:00"), desc.valid_until)
+ self.assertEquals(300, desc.vote_delay)
+ self.assertEquals(300, desc.dist_delay)
+ expected_client_versions = [stem.version.Version(version_string) for version_string in ["0.2.2.35",
+ "0.2.2.36", "0.2.2.37", "0.2.3.10-alpha", "0.2.3.11-alpha", "0.2.3.12-alpha",
+ "0.2.3.13-alpha", "0.2.3.14-alpha", "0.2.3.15-alpha", "0.2.3.16-alpha", "0.2.3.17-beta",
+ "0.2.3.18-rc", "0.2.3.19-rc"]]
+ expected_server_versions = [stem.version.Version(version_string) for version_string in ["0.2.2.35",
+ "0.2.2.36", "0.2.2.37", "0.2.3.10-alpha", "0.2.3.11-alpha", "0.2.3.12-alpha",
+ "0.2.3.13-alpha", "0.2.3.14-alpha", "0.2.3.15-alpha", "0.2.3.16-alpha", "0.2.3.17-beta",
+ "0.2.3.18-rc", "0.2.3.19-rc"]]
+ self.assertEquals(expected_client_versions, desc.client_versions)
+ self.assertEquals(expected_server_versions, desc.server_versions)
+ known_flags = ["Authority", "BadExit", "Exit", "Fast", "Guard", "HSDir", "Named", "Running", "Stable", "Unnamed", "V2Dir", "Valid"]
+ self.assertEquals(known_flags, desc.known_flags)
+ expected_params = {"CircuitPriorityHalflifeMsec": 30000, "bwauthpid": 1}
+ self.assertEquals(expected_params, desc.params)
+ router1 = next(desc.router_descriptors)
+ self.assertEquals("sumkledi", router1.nickname)
+ self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity)
+ self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router1.digest)
+ self.assertEquals(_strptime("2012-07-12 04:01:55"), router1.publication)
+ self.assertEquals("178.218.213.229", router1.ip)
+ self.assertEquals(80, router1.orport)
+ self.assertEquals(None, router1.dirport)
+
+ self.assertEquals(8, len(desc.directory_authorities))
+ self.assertEquals("tor26", desc.directory_authorities[0].nickname)
+ self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_authorities[0].identity)
+ self.assertEquals("86.59.21.38", desc.directory_authorities[0].address)
+ self.assertEquals("86.59.21.38", desc.directory_authorities[0].ip)
+ self.assertEquals(80, desc.directory_authorities[0].dirport)
+ self.assertEquals(443, desc.directory_authorities[0].orport)
+ self.assertEquals("Peter Palfrader", desc.directory_authorities[0].contact)
+ self.assertEquals(None, desc.directory_authorities[0].legacy_dir_key)
+ self.assertEquals(None, desc.directory_authorities[0].key_certificate)
+ self.assertEquals("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", desc.directory_authorities[0].vote_digest)
+ expected_bandwidth_weights = {
+ "Wbd": 3335, "Wbe": 0, "Wbg": 3536, "Wbm": 10000, "Wdb": 10000, "Web": 10000,
+ "Wed": 3329, "Wee": 10000, "Weg": 3329, "Wem": 10000, "Wgb": 10000, "Wgd": 3335,
+ "Wgg": 6464, "Wgm": 6464, "Wmb": 10000, "Wmd": 3335, "Wme": 0, "Wmg": 3536, "Wmm": 10000
+ }
+ self.assertEquals(expected_bandwidth_weights, desc.bandwidth_weights)
+
+ expected_signature = """HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
+mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
+I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY="""
+ self.assertEquals(8, len(desc.directory_signatures))
+ self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_signatures[0].identity)
+ self.assertEquals("BF112F1C6D5543CFD0A32215ACABD4197B5279AD", desc.directory_signatures[0].key_digest)
+ self.assertEquals(expected_signature, desc.directory_signatures[0].signature)
+
+ def test_vote(self):
+ """
+ Checks that vote documents are properly parsed.
+ """
+
+ descriptor_path = test.integ.descriptor.get_resource("vote")
+
+ descriptor_file = file(descriptor_path)
+ desc = stem.descriptor.networkstatus.NetworkStatusDocument(descriptor_file.read())
+ descriptor_file.close()
+
+ self.assertEquals(True, desc.validated)
+ self.assertEquals("3", desc.network_status_version)
+ self.assertEquals("vote", desc.vote_status)
+ self.assertEquals(range(1, 13), desc.consensus_methods)
+ self.assertEquals(_strptime("2012-07-11 23:50:01"), desc.published)
+ self.assertEquals(None, desc.consensus_method)
+ self.assertEquals(_strptime("2012-07-12 00:00:00"), desc.valid_after)
+ self.assertEquals(_strptime("2012-07-12 01:00:00"), desc.fresh_until)
+ self.assertEquals(_strptime("2012-07-12 03:00:00"), desc.valid_until)
+ self.assertEquals(300, desc.vote_delay)
+ self.assertEquals(300, desc.dist_delay)
+ self.assertEquals([], desc.client_versions)
+ self.assertEquals([], desc.server_versions)
+ known_flags = ["Authority", "BadExit", "Exit", "Fast", "Guard", "HSDir", "Running", "Stable", "V2Dir", "Valid"]
+ self.assertEquals(known_flags, desc.known_flags)
+ expected_params = {"CircuitPriorityHalflifeMsec": 30000, "bwauthpid": 1}
+ self.assertEquals(expected_params, desc.params)
+ router1 = next(desc.router_descriptors)
+ self.assertEquals("sumkledi", router1.nickname)
+ self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity)
+ self.assertEquals("B5n4BiALAF8B5AqafxohyYiuj7E", router1.digest)
+ self.assertEquals(_strptime("2012-07-11 04:22:53"), router1.publication)
+ self.assertEquals("178.218.213.229", router1.ip)
+ self.assertEquals(80, router1.orport)
+ self.assertEquals(None, router1.dirport)
+
+ self.assertEquals(1, len(desc.directory_authorities))
+ self.assertEquals("turtles", desc.directory_authorities[0].nickname)
+ self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].identity)
+ self.assertEquals("76.73.17.194", desc.directory_authorities[0].address)
+ self.assertEquals("76.73.17.194", desc.directory_authorities[0].ip)
+ self.assertEquals(9030, desc.directory_authorities[0].dirport)
+ self.assertEquals(9090, desc.directory_authorities[0].orport)
+ self.assertEquals("Mike Perry <email>", desc.directory_authorities[0].contact)
+ self.assertEquals(None, desc.directory_authorities[0].legacy_dir_key)
+
+ expected_identity_key = """MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
+XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
++Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
+pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
+r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
+HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
+Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
+2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
+RGL7jZunMUNvAgMBAAE="""
+ expected_signing_key = """MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
+NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
+GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE="""
+ expected_key_crosscert = """RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
+Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
+GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8="""
+ expected_key_certification = """fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
+AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
+dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
+9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
+tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
+KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
+brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
+emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL"""
+ self.assertEquals("3", desc.directory_authorities[0].key_certificate.key_certificate_version)
+ self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].key_certificate.fingerprint)
+ self.assertEquals(_strptime("2011-11-28 21:51:04"), desc.directory_authorities[0].key_certificate.published)
+ self.assertEquals(_strptime("2012-11-28 21:51:04"), desc.directory_authorities[0].key_certificate.expires)
+ self.assertEquals(expected_identity_key, desc.directory_authorities[0].key_certificate.identity_key)
+ self.assertEquals(expected_signing_key, desc.directory_authorities[0].key_certificate.signing_key)
+ self.assertEquals(expected_key_crosscert, desc.directory_authorities[0].key_certificate.crosscert)
+ self.assertEquals(expected_key_certification, desc.directory_authorities[0].key_certificate.certification)
+ self.assertEquals(None, desc.directory_authorities[0].vote_digest)
+ self.assertEquals({}, desc.bandwidth_weights)
+
+ expected_signature = """fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
+JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
+DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w="""
+ self.assertEquals(1, len(desc.directory_signatures))
+ self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_signatures[0].identity)
+ self.assertEquals("D5C30C15BB3F1DA27669C2D88439939E8F418FCF", desc.directory_signatures[0].key_digest)
+ self.assertEquals(expected_signature, desc.directory_signatures[0].signature)
+
diff --git a/test/integ/descriptor/networkstatus_descriptor.py b/test/integ/descriptor/networkstatus_descriptor.py
deleted file mode 100644
index 46019ea..0000000
--- a/test/integ/descriptor/networkstatus_descriptor.py
+++ /dev/null
@@ -1,228 +0,0 @@
-"""
-Integration tests for stem.descriptor.server_descriptor.
-"""
-
-from __future__ import with_statement
-
-import os
-import resource
-import datetime
-import unittest
-
-import stem.exit_policy
-import stem.version
-import stem.descriptor.networkstatus_descriptor
-import test.integ.descriptor
-
-def _strptime(string):
- return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
-
-class TestNetworkStatusDocument(unittest.TestCase):
- def test_metrics_consensus(self):
- """
- Checks if consensus documents from Metrics are parsed properly.
- """
-
- descriptor_path = test.integ.descriptor.get_resource("metrics_consensus")
-
- with file(descriptor_path) as descriptor_file:
- desc = stem.descriptor.parse_file(descriptor_path, descriptor_file)
-
- router = next(desc)
- self.assertEquals("sumkledi", router.nickname)
- self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router.identity)
- self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router.digest)
- self.assertEquals(_strptime("2012-07-12 04:01:55"), router.publication)
- self.assertEquals("178.218.213.229", router.ip)
- self.assertEquals(80, router.orport)
- self.assertEquals(None, router.dirport)
-
- def test_cached_consensus(self):
- """
- Parses the cached-consensus file in our data directory.
- """
-
- # lengthy test and uneffected by targets, so only run once
- if test.runner.only_run_once(self, "test_cached_consensus"): return
-
- descriptor_path = test.runner.get_runner().get_test_dir("cached-consensus")
-
- if not os.path.exists(descriptor_path):
- test.runner.skip(self, "(no cached-consensus)")
-
- if stem.util.system.is_windows():
- # might hog memory and hang the system
- # and we aren't checking for memory usage in windows, so, skip.
- test.runner.skip(self, "(unavailable on windows)")
-
- count = 0
- with open(descriptor_path) as descriptor_file:
- for desc in stem.descriptor.networkstatus_descriptor.parse_file(descriptor_file):
- if resource.getrusage(resource.RUSAGE_SELF).ru_maxrss > 100000:
- # if we're using > 100 MB we should fail
- self.fail()
- assert desc.nickname # check that the router has a nickname
- count += 1
-
- assert count > 100 # sanity check - assuming atleast 100 relays in the Tor network
-
- def test_consensus(self):
- """
- Checks that consensus documents are properly parsed.
- """
-
- descriptor_path = test.integ.descriptor.get_resource("consensus")
-
- descriptor_file = file(descriptor_path)
- desc = stem.descriptor.networkstatus_descriptor.NetworkStatusDocument(descriptor_file.read())
- descriptor_file.close()
-
- self.assertEquals(True, desc.validated)
- self.assertEquals("3", desc.network_status_version)
- self.assertEquals("consensus", desc.vote_status)
- self.assertEquals([], desc.consensus_methods)
- self.assertEquals(None, desc.published)
- self.assertEquals(12, desc.consensus_method)
- self.assertEquals(_strptime("2012-07-12 10:00:00"), desc.valid_after)
- self.assertEquals(_strptime("2012-07-12 11:00:00"), desc.fresh_until)
- self.assertEquals(_strptime("2012-07-12 13:00:00"), desc.valid_until)
- self.assertEquals(300, desc.vote_delay)
- self.assertEquals(300, desc.dist_delay)
- expected_client_versions = [stem.version.Version(version_string) for version_string in ["0.2.2.35",
- "0.2.2.36", "0.2.2.37", "0.2.3.10-alpha", "0.2.3.11-alpha", "0.2.3.12-alpha",
- "0.2.3.13-alpha", "0.2.3.14-alpha", "0.2.3.15-alpha", "0.2.3.16-alpha", "0.2.3.17-beta",
- "0.2.3.18-rc", "0.2.3.19-rc"]]
- expected_server_versions = [stem.version.Version(version_string) for version_string in ["0.2.2.35",
- "0.2.2.36", "0.2.2.37", "0.2.3.10-alpha", "0.2.3.11-alpha", "0.2.3.12-alpha",
- "0.2.3.13-alpha", "0.2.3.14-alpha", "0.2.3.15-alpha", "0.2.3.16-alpha", "0.2.3.17-beta",
- "0.2.3.18-rc", "0.2.3.19-rc"]]
- self.assertEquals(expected_client_versions, desc.client_versions)
- self.assertEquals(expected_server_versions, desc.server_versions)
- known_flags = ["Authority", "BadExit", "Exit", "Fast", "Guard", "HSDir", "Named", "Running", "Stable", "Unnamed", "V2Dir", "Valid"]
- self.assertEquals(known_flags, desc.known_flags)
- expected_params = {"CircuitPriorityHalflifeMsec": 30000, "bwauthpid": 1}
- self.assertEquals(expected_params, desc.params)
- router1 = next(desc.router_descriptors)
- self.assertEquals("sumkledi", router1.nickname)
- self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity)
- self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router1.digest)
- self.assertEquals(_strptime("2012-07-12 04:01:55"), router1.publication)
- self.assertEquals("178.218.213.229", router1.ip)
- self.assertEquals(80, router1.orport)
- self.assertEquals(None, router1.dirport)
-
- self.assertEquals(8, len(desc.directory_authorities))
- self.assertEquals("tor26", desc.directory_authorities[0].nickname)
- self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_authorities[0].identity)
- self.assertEquals("86.59.21.38", desc.directory_authorities[0].address)
- self.assertEquals("86.59.21.38", desc.directory_authorities[0].ip)
- self.assertEquals(80, desc.directory_authorities[0].dirport)
- self.assertEquals(443, desc.directory_authorities[0].orport)
- self.assertEquals("Peter Palfrader", desc.directory_authorities[0].contact)
- self.assertEquals(None, desc.directory_authorities[0].legacy_dir_key)
- self.assertEquals(None, desc.directory_authorities[0].key_certificate)
- self.assertEquals("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", desc.directory_authorities[0].vote_digest)
- expected_bandwidth_weights = {
- "Wbd": 3335, "Wbe": 0, "Wbg": 3536, "Wbm": 10000, "Wdb": 10000, "Web": 10000,
- "Wed": 3329, "Wee": 10000, "Weg": 3329, "Wem": 10000, "Wgb": 10000, "Wgd": 3335,
- "Wgg": 6464, "Wgm": 6464, "Wmb": 10000, "Wmd": 3335, "Wme": 0, "Wmg": 3536, "Wmm": 10000
- }
- self.assertEquals(expected_bandwidth_weights, desc.bandwidth_weights)
-
- expected_signature = """HFXB4497LzESysYJ/4jJY83E5vLjhv+igIxD9LU6lf6ftkGeF+lNmIAIEKaMts8H
-mfWcW0b+jsrXcJoCxV5IrwCDF3u1aC3diwZY6yiG186pwWbOwE41188XI2DeYPwE
-I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY="""
- self.assertEquals(8, len(desc.directory_signatures))
- self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_signatures[0].identity)
- self.assertEquals("BF112F1C6D5543CFD0A32215ACABD4197B5279AD", desc.directory_signatures[0].key_digest)
- self.assertEquals(expected_signature, desc.directory_signatures[0].signature)
-
- def test_vote(self):
- """
- Checks that vote documents are properly parsed.
- """
-
- descriptor_path = test.integ.descriptor.get_resource("vote")
-
- descriptor_file = file(descriptor_path)
- desc = stem.descriptor.networkstatus_descriptor.NetworkStatusDocument(descriptor_file.read())
- descriptor_file.close()
-
- self.assertEquals(True, desc.validated)
- self.assertEquals("3", desc.network_status_version)
- self.assertEquals("vote", desc.vote_status)
- self.assertEquals(range(1, 13), desc.consensus_methods)
- self.assertEquals(_strptime("2012-07-11 23:50:01"), desc.published)
- self.assertEquals(None, desc.consensus_method)
- self.assertEquals(_strptime("2012-07-12 00:00:00"), desc.valid_after)
- self.assertEquals(_strptime("2012-07-12 01:00:00"), desc.fresh_until)
- self.assertEquals(_strptime("2012-07-12 03:00:00"), desc.valid_until)
- self.assertEquals(300, desc.vote_delay)
- self.assertEquals(300, desc.dist_delay)
- self.assertEquals([], desc.client_versions)
- self.assertEquals([], desc.server_versions)
- known_flags = ["Authority", "BadExit", "Exit", "Fast", "Guard", "HSDir", "Running", "Stable", "V2Dir", "Valid"]
- self.assertEquals(known_flags, desc.known_flags)
- expected_params = {"CircuitPriorityHalflifeMsec": 30000, "bwauthpid": 1}
- self.assertEquals(expected_params, desc.params)
- router1 = next(desc.router_descriptors)
- self.assertEquals("sumkledi", router1.nickname)
- self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity)
- self.assertEquals("B5n4BiALAF8B5AqafxohyYiuj7E", router1.digest)
- self.assertEquals(_strptime("2012-07-11 04:22:53"), router1.publication)
- self.assertEquals("178.218.213.229", router1.ip)
- self.assertEquals(80, router1.orport)
- self.assertEquals(None, router1.dirport)
-
- self.assertEquals(1, len(desc.directory_authorities))
- self.assertEquals("turtles", desc.directory_authorities[0].nickname)
- self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].identity)
- self.assertEquals("76.73.17.194", desc.directory_authorities[0].address)
- self.assertEquals("76.73.17.194", desc.directory_authorities[0].ip)
- self.assertEquals(9030, desc.directory_authorities[0].dirport)
- self.assertEquals(9090, desc.directory_authorities[0].orport)
- self.assertEquals("Mike Perry <email>", desc.directory_authorities[0].contact)
- self.assertEquals(None, desc.directory_authorities[0].legacy_dir_key)
-
- expected_identity_key = """MIIBigKCAYEA6uSmsoxj2MiJ3qyZq0qYXlRoG8o82SNqg+22m+t1c7MlQOZWPJYn
-XeMcBCt8xrTeIt2ZI+Q/Kt2QJSeD9WZRevTKk/kn5Tg2+xXPogalUU47y5tUohGz
-+Q8+CxtRSXpDxBHL2P8rLHvGrI69wbNHGoQkce/7gJy9vw5Ie2qzbyXk1NG6V8Fb
-pr6A885vHo6TbhUnolz2Wqt/kN+UorjLkN2H3fV+iGcQFv42SyHYGDLa0WwL3PJJ
-r/veu36S3VaHBrfhutfioi+d3d4Ya0bKwiWi5Lm2CHuuRTgMpHLU9vlci8Hunuxq
-HsULe2oMsr4VEic7sW5SPC5Obpx6hStHdNv1GxoSEm3/vIuPM8pINpU5ZYAyH9yO
-Ef22ZHeiVMMKmpV9TtFyiFqvlI6GpQn3mNbsQqF1y3XCA3Q4vlRAkpgJVUSvTxFP
-2bNDobOyVCpCM/rwxU1+RCNY5MFJ/+oktUY+0ydvTen3gFdZdgNqCYjKPLfBNm9m
-RGL7jZunMUNvAgMBAAE="""
- expected_signing_key = """MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0
-NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic
-GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE="""
- expected_key_crosscert = """RHYImGTwg36wmEdAn7qaRg2sAfql7ZCtPIL/O3lU5OIdXXp0tNn/K00Bamqohjk+
-Tz4FKsKXGDlbGv67PQcZPOK6NF0GRkNh4pk89prrDO4XwtEn7rkHHdBH6/qQ7IRG
-GdDZHtZ1a69oFZvPWD3hUaB50xeIe7GoKdKIfdNNJ+8="""
- expected_key_certification = """fasWOGyUZ3iMCYpDfJ+0JcMiTH25sXPWzvlHorEOyOMbaMqRYpZU4GHzt1jLgdl6
-AAoR6KdamsLg5VE8xzst48a4UFuzHFlklZ5O8om2rcvDd5DhSnWWYZnYJecqB+bo
-dNisPmaIVSAWb29U8BpNRj4GMC9KAgGYUj8aE/KtutAeEekFfFEHTfWZ2fFp4j3m
-9rY8FWraqyiF+Emq1T8pAAgMQ+79R3oZxq0TXS42Z4Anhms735ccauKhI3pDKjbl
-tD5vAzIHOyjAOXj7a6jY/GrnaBNuJ4qe/4Hf9UmzK/jKKwG95BPJtPTT4LoFwEB0
-KG2OUeQUNoCck4nDpsZwFqPlrWCHcHfTV2iDYFV1HQWDTtZz/qf+GtB8NXsq+I1w
-brADmvReM2BD6p/13h0QURCI5hq7ZYlIKcKrBa0jn1d9cduULl7vgKsRCJDls/ID
-emBZ6pUxMpBmV0v+PrA3v9w4DlE7GHAq61FF/zju2kpqj6MInbEvI/E+e438sWsL"""
- self.assertEquals("3", desc.directory_authorities[0].key_certificate.key_certificate_version)
- self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].key_certificate.fingerprint)
- self.assertEquals(_strptime("2011-11-28 21:51:04"), desc.directory_authorities[0].key_certificate.published)
- self.assertEquals(_strptime("2012-11-28 21:51:04"), desc.directory_authorities[0].key_certificate.expires)
- self.assertEquals(expected_identity_key, desc.directory_authorities[0].key_certificate.identity_key)
- self.assertEquals(expected_signing_key, desc.directory_authorities[0].key_certificate.signing_key)
- self.assertEquals(expected_key_crosscert, desc.directory_authorities[0].key_certificate.crosscert)
- self.assertEquals(expected_key_certification, desc.directory_authorities[0].key_certificate.certification)
- self.assertEquals(None, desc.directory_authorities[0].vote_digest)
- self.assertEquals({}, desc.bandwidth_weights)
-
- expected_signature = """fskXN84wB3mXfo+yKGSt0AcDaaPuU3NwMR3ROxWgLN0KjAaVi2eV9PkPCsQkcgw3
-JZ/1HL9sHyZfo6bwaC6YSM9PNiiY6L7rnGpS7UkHiFI+M96VCMorvjm5YPs3FioJ
-DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w="""
- self.assertEquals(1, len(desc.directory_signatures))
- self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_signatures[0].identity)
- self.assertEquals("D5C30C15BB3F1DA27669C2D88439939E8F418FCF", desc.directory_signatures[0].key_digest)
- self.assertEquals(expected_signature, desc.directory_signatures[0].signature)
-
More information about the tor-commits
mailing list