[tor-commits] [stem/master] Unit tests for bridge descriptors and related fixes

atagar at torproject.org atagar at torproject.org
Sun Apr 15 02:50:21 UTC 2012


commit 850b1cbbc07554ea369b44c89b3bbfdd1b99fa69
Author: Damian Johnson <atagar at torproject.org>
Date:   Thu Apr 12 10:53:28 2012 -0700

    Unit tests for bridge descriptors and related fixes
    
    Unit testing the new bridge descriptors. This mostly focused on parsing
    or-address entries and the validation that the class does for scrubbed values.
    Fixes included having string port values and not checking that or-address
    addresses were scrubbed.
---
 stem/descriptor/server_descriptor.py      |   10 ++-
 test/unit/descriptor/server_descriptor.py |  121 ++++++++++++++++++++++++++--
 2 files changed, 121 insertions(+), 10 deletions(-)

diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 52bca94..dfdde30 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -645,7 +645,7 @@ class BridgeDescriptorV3(ServerDescriptorV3):
               if not validate: break
               else: raise ValueError("or-address line has malformed ports: %s" % line)
             
-            self.address_alt.append((address, port, is_ipv6))
+            self.address_alt.append((address, int(port), is_ipv6))
         
         del entries["or-address"]
     
@@ -665,6 +665,14 @@ class BridgeDescriptorV3(ServerDescriptorV3):
     elif self.contact and self.contact != "somebody":
       raise ValueError("Contact line should be scrubbed to be 'somebody', but instead had '%s'" % self.contact)
     
+    for address, _, is_ipv6 in self.address_alt:
+      if not is_ipv6 and not address.startswith("10."):
+        raise ValueError("or-address line's address should be scrubbed to be '10.x.x.x': %s" % address)
+      elif is_ipv6 and not address.startswith("fd9f:2e19:3bcf::"):
+        # TODO: this check isn't quite right because we aren't checking that
+        # the next grouping of hex digits contains 1-2 digits
+        raise ValueError("or-address line's address should be scrubbed to be 'fd9f:2e19:3bcf::xx:xxxx': %s" % address)
+    
     for line in self.get_unrecognized_lines():
       if line.startswith("onion-key "):
         raise ValueError("Bridge descriptors should have their onion-key scrubbed: %s" % line)
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 42325ff..89a28c6 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -7,7 +7,7 @@ import StringIO
 import unittest
 
 import stem.descriptor.server_descriptor
-from stem.descriptor.server_descriptor import RelayDescriptorV3
+from stem.descriptor.server_descriptor import RelayDescriptorV3, BridgeDescriptorV3
 
 CRYPTO_BLOB = """
 MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
@@ -15,7 +15,7 @@ skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
 WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
 """
 
-SAMPLE_DESCRIPTOR_ATTR = (
+RELAY_DESCRIPTOR_ATTR = (
   ("router", "caerSidi 71.35.133.197 9001 0 0"),
   ("published", "2012-03-01 17:15:27"),
   ("bandwidth", "153600 256000 104590"),
@@ -25,13 +25,21 @@ SAMPLE_DESCRIPTOR_ATTR = (
   ("router-signature", "\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----" % CRYPTO_BLOB),
 )
 
-def _make_descriptor(attr = None, exclude = None):
+BRIDGE_DESCRIPTOR_ATTR = (
+  ("router", "Unnamed 10.45.227.253 9001 0 0"),
+  ("published", "2012-03-22 17:34:38"),
+  ("bandwidth", "409600 819200 5120"),
+  ("reject", "*:*"),
+)
+
+def _make_descriptor(attr = None, exclude = None, is_bridge = False):
   """
   Constructs a minimal server descriptor with the given attributes.
   
   Arguments:
-    attr (dict)    - keyword/value mappings to be included in the descriptor
-    exclude (list) - mandatory keywords to exclude from the descriptor
+    attr (dict)     - keyword/value mappings to be included in the descriptor
+    exclude (list)  - mandatory keywords to exclude from the descriptor
+    is_bridge (bool) - minimal descriptor is for a bridge if True, relay otherwise
   
   Returns:
     str with customized descriptor content
@@ -40,26 +48,35 @@ def _make_descriptor(attr = None, exclude = None):
   descriptor_lines = []
   if attr == None: attr = {}
   if exclude == None: exclude = []
+  desc_attr = BRIDGE_DESCRIPTOR_ATTR if is_bridge else RELAY_DESCRIPTOR_ATTR
+  attr = dict(attr) # shallow copy since we're destructive
   
-  for keyword, value in SAMPLE_DESCRIPTOR_ATTR:
+  for keyword, value in desc_attr:
     if keyword in exclude: continue
     elif keyword in attr:
       value = attr[keyword]
       del attr[keyword]
     
     # if this is the last entry then we should dump in any unused attributes
-    if keyword == "router-signature":
+    if not is_bridge and keyword == "router-signature":
       for attr_keyword, attr_value in attr.items():
         descriptor_lines.append("%s %s" % (attr_keyword, attr_value))
     
     descriptor_lines.append("%s %s" % (keyword, value))
   
+  # bridges don't have a router-signature so simply append any extra attributes
+  # to the end
+  if is_bridge:
+    for attr_keyword, attr_value in attr.items():
+      descriptor_lines.append("%s %s" % (attr_keyword, attr_value))
+  
   return "\n".join(descriptor_lines)
 
 class TestServerDescriptor(unittest.TestCase):
-  def test_minimal_descriptor(self):
+  def test_minimal_relay_descriptor(self):
     """
-    Basic sanity check that we can parse a descriptor with minimal attributes.
+    Basic sanity check that we can parse a relay server descriptor with minimal
+    attributes.
     """
     
     desc_text = _make_descriptor()
@@ -255,6 +272,92 @@ class TestServerDescriptor(unittest.TestCase):
         self.assertEquals(None, desc.socks_port)
         self.assertEquals(None, desc.dir_port)
   
+  def test_minimal_bridge_descriptor(self):
+    """
+    Basic sanity check that we can parse a descriptor with minimal attributes.
+    """
+    
+    desc_text = _make_descriptor(is_bridge = True)
+    desc = BridgeDescriptorV3(desc_text)
+    
+    self.assertEquals("Unnamed", desc.nickname)
+    self.assertEquals("10.45.227.253", desc.address)
+    self.assertEquals(None, desc.fingerprint)
+    
+    # check that we don't have crypto fields
+    self.assertRaises(AttributeError, getattr, desc, "onion_key")
+    self.assertRaises(AttributeError, getattr, desc, "signing_key")
+    self.assertRaises(AttributeError, getattr, desc, "signature")
+  
+  def test_bridge_unsanitized(self):
+    """
+    Targeted check that individual unsanitized attributes will be detected. 
+    """
+    
+    unsanitized_attr = [
+      {"router": "caerSidi 10.45.227.253 9001 0 0"},
+      {"router": "Unnamed 75.45.227.253 9001 0 0"},
+      {"contact": "Damian"},
+      {"or-address": "71.35.133.197:9001"},
+      {"or-address": "[12ab:2e19:3bcf::02:9970]:9001"},
+      {"onion-key": "\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----" % CRYPTO_BLOB},
+      {"signing-key": "\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----" % CRYPTO_BLOB},
+      {"router-signature": "\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----" % CRYPTO_BLOB},
+    ]
+    
+    for attr in unsanitized_attr:
+      try:
+        desc_text = _make_descriptor(attr, is_bridge = True)
+        BridgeDescriptorV3(desc_text)
+        self.fail("Unsanitized attribute wasn't detected: %s %s" % attr.items()[0])
+      except ValueError: pass
+  
+  def test_bridge_unsanitized_relay(self):
+    """
+    Checks that parsing a normal relay descriptor as a bridge will fail due to
+    its unsanatized content.
+    """
+    
+    desc_text = _make_descriptor()
+    self.assertRaises(ValueError, BridgeDescriptorV3, desc_text)
+  
+  def test_or_address_v4(self):
+    """
+    Constructs a bridge descriptor with a sanatized IPv4 or-address entry.
+    """
+    
+    desc_text = _make_descriptor({"or-address": "10.45.227.253:9001"}, is_bridge = True)
+    desc = BridgeDescriptorV3(desc_text)
+    self.assertEquals([("10.45.227.253", 9001, False)], desc.address_alt)
+  
+  def test_or_address_v6(self):
+    """
+    Constructs a bridge descriptor with a sanatized IPv6 or-address entry.
+    """
+    
+    desc_text = _make_descriptor({"or-address": "[fd9f:2e19:3bcf::02:9970]:9001"}, is_bridge = True)
+    desc = BridgeDescriptorV3(desc_text)
+    self.assertEquals([("fd9f:2e19:3bcf::02:9970", 9001, True)], desc.address_alt)
+  
+  def test_or_address_multiple(self):
+    """
+    Constructs a bridge descriptor with multiple or-address entries and multiple ports.
+    """
+    
+    desc_text = "\n".join((_make_descriptor(is_bridge = True),
+                          "or-address 10.45.227.253:9001,9005,80",
+                          "or-address [fd9f:2e19:3bcf::02:9970]:443"))
+    
+    expected_address_alt = [
+      ("10.45.227.253", 9001, False),
+      ("10.45.227.253", 9005, False),
+      ("10.45.227.253", 80, False),
+      ("fd9f:2e19:3bcf::02:9970", 443, True),
+    ]
+    
+    desc = BridgeDescriptorV3(desc_text)
+    self.assertEquals(expected_address_alt, desc.address_alt)
+  
   def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
     """
     Asserts that construction will fail due to desc_text having a malformed





More information about the tor-commits mailing list