[tor-commits] [stem/master] Making relay-signature field only apply to relay extra-info

atagar at torproject.org atagar at torproject.org
Sun Jun 10 21:30:22 UTC 2012


commit 737018b7ee3da5b457689404376a7937de5a016f
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jun 10 13:33:21 2012 -0700

    Making relay-signature field only apply to relay extra-info
    
    Parsing and handling the relay-signature field as only an attribute of
    extra-info descriptors for relays.
---
 stem/descriptor/__init__.py                   |    4 +-
 stem/descriptor/extrainfo_descriptor.py       |   78 ++++++++++++++++++------
 stem/descriptor/server_descriptor.py          |   31 ++++------
 test/integ/descriptor/extrainfo_descriptor.py |    2 +-
 test/unit/descriptor/extrainfo_descriptor.py  |   34 +++++-----
 5 files changed, 91 insertions(+), 58 deletions(-)

diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index e4f23e5..929396c 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -70,9 +70,9 @@ def parse_file(path, descriptor_file):
     desc = stem.descriptor.server_descriptor.RelayDescriptor(descriptor_file.read())
   elif first_line == "@type bridge-server-descriptor 1.0":
     desc = stem.descriptor.server_descriptor.BridgeDescriptor(descriptor_file.read())
-  elif first_line in ("@type bridge-extra-info 1.0"):
-    desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_file.read())
   elif first_line in ("@type extra-info 1.0"):
+    desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_file.read())
+  elif first_line in ("@type bridge-extra-info 1.0"):
     desc = stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor(descriptor_file.read())
   
   if desc:
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 03c7a37..0515099 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -114,9 +114,6 @@ SINGLE_FIELDS = (
   "exit-streams-opened",
 )
 
-FIRST_FIELD = "extra-info"
-LAST_FIELD = "router-signature"
-
 def parse_file(descriptor_file, validate = True):
   """
   Iterates over the extra-info descriptors in a file.
@@ -139,7 +136,7 @@ def parse_file(descriptor_file, validate = True):
     extrainfo_content += stem.descriptor._read_until_keyword(block_end_prefix, descriptor_file, True)
     
     if extrainfo_content:
-      yield ExtraInfoDescriptor("".join(extrainfo_content), validate)
+      yield RelayExtraInfoDescriptor("".join(extrainfo_content), validate)
     else: break # done parsing file
 
 def _parse_timestamp_and_interval(keyword, content):
@@ -180,7 +177,6 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
   :var str fingerprint: **\*** identity key fingerprint
   :var datetime published: **\*** time in GMT when this descriptor was made
   :var str geoip_db_digest: sha1 of geoIP database file
-  :var str signature: **\*** signature for this extrainfo descriptor
   
   **Bi-directional connection usage:**
   
@@ -289,7 +285,6 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
     self.fingerprint = None
     self.published = None
     self.geoip_db_digest = None
-    self.signature = None
     
     self.conn_bi_direct_end = None
     self.conn_bi_direct_interval = None
@@ -364,18 +359,21 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
       stem.descriptor._get_descriptor_components(raw_contents, validate, ())
     
     if validate:
-      for keyword in REQUIRED_FIELDS:
+      for keyword in self._required_fields():
         if not keyword in entries:
           raise ValueError("Extra-info descriptor must have a '%s' entry" % keyword)
       
-      for keyword in REQUIRED_FIELDS + SINGLE_FIELDS:
+      for keyword in self._required_fields() + SINGLE_FIELDS:
         if keyword in entries and len(entries[keyword]) > 1:
           raise ValueError("The '%s' entry can only appear once in an extra-info descriptor" % keyword)
-      if not first_keyword == FIRST_FIELD:
-        raise ValueError("Extra-info descriptor must start with a '%s' entry" % FIRST_FIELD)
       
-      if not last_keyword == LAST_FIELD:
-        raise ValueError("Descriptor must end with a '%s' entry" % LAST_FIELD)
+      expected_first_keyword = self._first_keyword()
+      if expected_first_keyword and not first_keyword == expected_first_keyword:
+        raise ValueError("Extra-info descriptor must start with a '%s' entry" % expected_first_keyword)
+      
+      expected_last_keyword = self._last_keyword()
+      if expected_last_keyword and not last_keyword == expected_last_keyword:
+        raise ValueError("Descriptor must end with a '%s' entry" % expected_last_keyword)
     
     self._parse(entries, validate)
   
@@ -395,10 +393,8 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
     
     for keyword, values in entries.items():
       # most just work with the first (and only) value
-      value, block_contents = values[0]
-      
+      value, _ = values[0]
       line = "%s %s" % (keyword, value) # original line
-      if block_contents: line += "\n%s" % block_contents
       
       if keyword == "extra-info":
         # "extra-info" Nickname Fingerprint
@@ -674,23 +670,65 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
           self.entry_ips = locale_usage
         elif keyword == "bridge-ips":
           self.bridge_ips = locale_usage
-      elif keyword == "router-signature":
-        if validate and not block_contents:
-          raise ValueError("Router signature line must be followed by a signature block: %s" % line)
-        
-        self.signature = block_contents
       else:
         self._unrecognized_lines.append(line)
+  
+  def _required_fields(self):
+    return REQUIRED_FIELDS
+  
+  def _first_keyword(self):
+    return "extra-info"
+  
+  def _last_keyword(self):
+    return "router-signature"
 
 class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
   """
   Relay extra-info descriptor, constructed from data such as that provided by
   "GETINFO extra-info/digest/*", cached descriptors, and metrics
   (`specification <https://gitweb.torproject.org/torspec.git/blob/HEAD:/dir-spec.txt>`_).
+  
+  :var str signature: **\*** signature for this extrainfo descriptor
+  
+  **\*** 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, validate = True):
+    self.signature = None
+    
+    ExtraInfoDescriptor.__init__(self, raw_contents, validate)
+  
+  def _parse(self, entries, validate):
+    entries = dict(entries) # shallow copy since we're destructive
+    
+    # handles fields only in server descriptors
+    for keyword, values in entries.items():
+      value, block_contents = values[0]
+      
+      line = "%s %s" % (keyword, value) # original line
+      if block_contents: line += "\n%s" % block_contents
+      
+      if keyword == "router-signature":
+        if validate and not block_contents:
+          raise ValueError("Router signature line must be followed by a signature block: %s" % line)
+        
+        self.signature = block_contents
+        del entries["router-signature"]
+    
+    ExtraInfoDescriptor._parse(self, entries, validate)
 
 class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
   """
   Bridge extra-info descriptor (`specification <https://metrics.torproject.org/formats.html#bridgedesc>`_)
   """
+  
+  def _required_fields(self):
+    excluded_fields = (
+      "router-signature",
+    )
+    
+    filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS)
+  
+  def _last_keyword(self):
+    return None
 
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 6f8079b..af31f03 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -542,10 +542,17 @@ class ServerDescriptor(stem.descriptor.Descriptor):
   # Constraints that the descriptor must meet to be valid. These can be None if
   # not applicable.
   
-  def _required_fields(self): return None
-  def _single_fields(self): return None
-  def _first_keyword(self): return None
-  def _last_keyword(self): return None
+  def _required_fields(self):
+    return REQUIRED_FIELDS
+  
+  def _single_fields(self):
+    return REQUIRED_FIELDS + SINGLE_FIELDS
+  
+  def _first_keyword(self):
+    return "router"
+  
+  def _last_keyword(self):
+    return "router-signature"
 
 class RelayDescriptor(ServerDescriptor):
   """
@@ -640,18 +647,6 @@ class RelayDescriptor(ServerDescriptor):
         del entries["router-signature"]
     
     ServerDescriptor._parse(self, entries, validate)
-  
-  def _required_fields(self):
-    return REQUIRED_FIELDS
-  
-  def _single_fields(self):
-    return REQUIRED_FIELDS + SINGLE_FIELDS
-  
-  def _first_keyword(self):
-    return "router"
-  
-  def _last_keyword(self):
-    return "router-signature"
 
 class BridgeDescriptor(ServerDescriptor):
   """
@@ -780,6 +775,6 @@ class BridgeDescriptor(ServerDescriptor):
   def _single_fields(self):
     return self._required_fields() + SINGLE_FIELDS
   
-  def _first_keyword(self):
-    return "router"
+  def _last_keyword(self):
+    return None
 
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index a753d27..2739985 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -29,7 +29,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 7LZqklu+gVvhMKREpchVqlAwXkWR44VENm24Hs+mT3M=
 -----END SIGNATURE-----"""
     
-    desc = stem.descriptor.extrainfo_descriptor.ExtraInfoDescriptor(descriptor_contents)
+    desc = stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(descriptor_contents)
     self.assertEquals("NINJA", desc.nickname)
     self.assertEquals("B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48", desc.fingerprint)
     self.assertEquals(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 5d4c910..3bfff70 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -4,7 +4,7 @@ Unit tests for stem.descriptor.extrainfo_descriptor.
 
 import datetime
 import unittest
-from stem.descriptor.extrainfo_descriptor import ExtraInfoDescriptor, DirResponses, DirStats
+from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponses, DirStats
 
 CRYPTO_BLOB = """
 K5FSywk7qvw/boA4DQcqkls6Ize5vcBYfhQ8JnOeRQC9+uDxbnpm3qaYN9jZ8myj
@@ -58,7 +58,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     """
     
     desc_text = _make_descriptor()
-    desc = ExtraInfoDescriptor(desc_text)
+    desc = RelayExtraInfoDescriptor(desc_text)
     
     self.assertEquals("ninja", desc.nickname)
     self.assertEquals("B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48", desc.fingerprint)
@@ -70,7 +70,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     """
     
     desc_text = _make_descriptor({"pepperjack": "is oh so tasty!"})
-    desc = ExtraInfoDescriptor(desc_text)
+    desc = RelayExtraInfoDescriptor(desc_text)
     self.assertEquals(["pepperjack is oh so tasty!"], desc.get_unrecognized_lines())
   
   def test_proceeding_line(self):
@@ -116,7 +116,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     
     geoip_db_digest = "916A3CA8B7DF61473D5AE5B21711F35F301CE9E8"
     desc_text = _make_descriptor({"geoip-db-digest": geoip_db_digest})
-    desc = ExtraInfoDescriptor(desc_text)
+    desc = RelayExtraInfoDescriptor(desc_text)
     self.assertEquals(geoip_db_digest, desc.geoip_db_digest)
     
     test_entries = (
@@ -143,7 +143,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     
     for entry in ("0", "11", "25"):
       desc_text = _make_descriptor({"cell-circuits-per-decile": entry})
-      desc = ExtraInfoDescriptor(desc_text)
+      desc = RelayExtraInfoDescriptor(desc_text)
       self.assertEquals(int(entry), desc.cell_circuits_per_decile)
     
     test_entries = (
@@ -169,7 +169,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       test_value = "ok=0,unavailable=0,not-found=984,not-modified=0,something-new=7"
       desc_text = _make_descriptor({keyword: test_value})
-      desc = ExtraInfoDescriptor(desc_text)
+      desc = RelayExtraInfoDescriptor(desc_text)
       self.assertEquals(0, getattr(desc, attr)[DirResponses.OK])
       self.assertEquals(0, getattr(desc, attr)[DirResponses.UNAVAILABLE])
       self.assertEquals(984, getattr(desc, attr)[DirResponses.NOT_FOUND])
@@ -200,7 +200,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       test_value = "complete=2712,timeout=32,running=4,min=741,d1=14507,d2=22702,q1=28881,d3=38277,d4=73729,md=111455,d6=168231,d7=257218,q3=319833,d8=390507,d9=616301,something-new=11,max=29917857"
       desc_text = _make_descriptor({keyword: test_value})
-      desc = ExtraInfoDescriptor(desc_text)
+      desc = RelayExtraInfoDescriptor(desc_text)
       self.assertEquals(2712, getattr(desc, attr)[DirStats.COMPLETE])
       self.assertEquals(32, getattr(desc, attr)[DirStats.TIMEOUT])
       self.assertEquals(4, getattr(desc, attr)[DirStats.RUNNING])
@@ -237,7 +237,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     """
     
     desc_text = _make_descriptor({"conn-bi-direct": "2012-05-03 12:07:50 (500 s) 277431,12089,0,2134"})
-    desc = ExtraInfoDescriptor(desc_text)
+    desc = RelayExtraInfoDescriptor(desc_text)
     self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.conn_bi_direct_end)
     self.assertEquals(500, desc.conn_bi_direct_interval)
     self.assertEquals(277431, desc.conn_bi_direct_below)
@@ -287,7 +287,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       for test_value, expected_value in test_entries:
         desc_text = _make_descriptor({keyword: test_value})
-        desc = ExtraInfoDescriptor(desc_text)
+        desc = RelayExtraInfoDescriptor(desc_text)
         self.assertEquals(expected_value, getattr(desc, attr))
       
       test_entries = (
@@ -320,7 +320,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       for test_value, expected_value in test_entries:
         desc_text = _make_descriptor({keyword: test_value})
-        desc = ExtraInfoDescriptor(desc_text)
+        desc = RelayExtraInfoDescriptor(desc_text)
         self.assertEquals(expected_value, getattr(desc, attr))
       
       test_entries = (
@@ -343,7 +343,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       attr = keyword.replace('-', '_')
       
       desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50"})
-      desc = ExtraInfoDescriptor(desc_text)
+      desc = RelayExtraInfoDescriptor(desc_text)
       self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, attr))
       
       test_entries = (
@@ -368,7 +368,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       interval_attr = end_attr[:-4] + "_interval"
       
       desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50 (500 s)"})
-      desc = ExtraInfoDescriptor(desc_text)
+      desc = RelayExtraInfoDescriptor(desc_text)
       self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
       self.assertEquals(500, getattr(desc, interval_attr))
       
@@ -408,7 +408,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       for test_values, expected_values in test_entries:
         desc_text = _make_descriptor({keyword: "2012-05-03 12:07:50 (500 s)%s" % test_values})
-        desc = ExtraInfoDescriptor(desc_text)
+        desc = RelayExtraInfoDescriptor(desc_text)
         self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
         self.assertEquals(500, getattr(desc, interval_attr))
         self.assertEquals(expected_values, getattr(desc, values_attr))
@@ -448,7 +448,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       for test_value, expected_value in test_entries:
         desc_text = _make_descriptor({keyword: test_value})
-        desc = ExtraInfoDescriptor(desc_text)
+        desc = RelayExtraInfoDescriptor(desc_text)
         self.assertEquals(expected_value, getattr(desc, attr))
       
       test_entries = (
@@ -480,7 +480,7 @@ class TestExtraInfoDescriptor(unittest.TestCase):
       
       for test_value, expected_value in test_entries:
         desc_text = _make_descriptor({keyword: test_value})
-        desc = ExtraInfoDescriptor(desc_text)
+        desc = RelayExtraInfoDescriptor(desc_text)
         self.assertEquals(expected_value, getattr(desc, attr))
       
       test_entries = (
@@ -501,8 +501,8 @@ class TestExtraInfoDescriptor(unittest.TestCase):
     value when we're constructed without validation.
     """
     
-    self.assertRaises(ValueError, ExtraInfoDescriptor, desc_text)
-    desc = ExtraInfoDescriptor(desc_text, validate = False)
+    self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text)
+    desc = RelayExtraInfoDescriptor(desc_text, validate = False)
     
     if attr:
       # check that the invalid attribute matches the expected value when





More information about the tor-commits mailing list