[tor-commits] [bridgedb/master] Add support for bridge descriptors from multiple authorities.

isis at torproject.org isis at torproject.org
Thu Sep 22 13:20:24 UTC 2016


commit ae45f903f260fcc98997793c52919b2d9b289295
Author: Isis Lovecruft <isis at torproject.org>
Date:   Tue Sep 6 18:33:33 2016 +0000

    Add support for bridge descriptors from multiple authorities.
    
     * FIXES #20088: https://bugs.torproject.org/20088
---
 bridgedb.conf         |   4 ++
 bridgedb/Main.py      | 172 +++++++++++++++++++++++++++-----------------------
 bridgedb/configure.py |   4 +-
 requirements.txt      |   2 +-
 4 files changed, 99 insertions(+), 83 deletions(-)

diff --git a/bridgedb.conf b/bridgedb.conf
index 52a8ca7..2d73a2b 100644
--- a/bridgedb.conf
+++ b/bridgedb.conf
@@ -120,6 +120,10 @@
 # can read it.
 #------------------------------------------------------------------------------
 
+# List of directories which contain versions of the files specified in
+# BRIDGE_FILES, EXTRA_INFO_FILES, and STATUS_FILE.
+BRIDGE_AUTHORITY_DIRECTORIES = ["from-authority", "from-bifroest"]
+
 # List of filenames from which we read ``@type bridge-server-descriptor``s, on
 # startup and on SIGHUP.
 BRIDGE_FILES = ["bridge-descriptors"]
diff --git a/bridgedb/Main.py b/bridgedb/Main.py
index bf4c213..dc618cb 100644
--- a/bridgedb/Main.py
+++ b/bridgedb/Main.py
@@ -41,6 +41,12 @@ from bridgedb import Bridges
 from bridgedb.Stability import updateBridgeHistory
 
 
+def expandBridgeAuthDir(authdir, filename):
+    """Expands a descriptor ``filename`` relative to which of the
+    BRIDGE_AUTHORITY_DIRECTORIES, ``authdir`` it resides within.
+    """
+    return os.path.abspath(os.path.expanduser(os.sep.join([authdir, filename])))
+
 def load(state, hashring, clear=False):
     """Read and parse all descriptors, and load into a bridge hashring.
 
@@ -69,95 +75,101 @@ def load(state, hashring, clear=False):
     if ignoreNetworkstatus:
         logging.info("Ignoring BridgeAuthority networkstatus documents.")
 
-    bridges = {}
-    timestamps = {}
-
-    logging.info("Opening networkstatus file: %s" % state.STATUS_FILE)
-    networkstatuses = descriptors.parseNetworkStatusFile(state.STATUS_FILE)
-    logging.debug("Closing networkstatus file: %s" % state.STATUS_FILE)
+    for auth in state.BRIDGE_AUTHORITY_DIRECTORIES:
+        logging.info("Processing descriptors in %s directory..." % auth)
 
-    logging.info("Processing networkstatus descriptors...")
-    for router in networkstatuses:
-        bridge = Bridge()
-        bridge.updateFromNetworkStatus(router, ignoreNetworkstatus)
-        try:
-            bridge.assertOK()
-        except MalformedBridgeInfo as error:
-            logging.warn(str(error))
-        else:
-            bridges[bridge.fingerprint] = bridge
+        bridges = {}
+        timestamps = {}
 
-    for filename in state.BRIDGE_FILES:
-        logging.info("Opening bridge-server-descriptor file: '%s'" % filename)
-        serverdescriptors = descriptors.parseServerDescriptorsFile(filename)
-        logging.debug("Closing bridge-server-descriptor file: '%s'" % filename)
+        fn = expandBridgeAuthDir(auth, state.STATUS_FILE)
+        logging.info("Opening networkstatus file: %s" % fn)
+        networkstatuses = descriptors.parseNetworkStatusFile(fn)
+        logging.debug("Closing networkstatus file: %s" % fn)
 
-        for router in serverdescriptors:
+        logging.info("Processing networkstatus descriptors...")
+        for router in networkstatuses:
+            bridge = Bridge()
+            bridge.updateFromNetworkStatus(router, ignoreNetworkstatus)
             try:
-                bridge = bridges[router.fingerprint]
-            except KeyError:
-                logging.warn(
-                    ("Received server descriptor for bridge '%s' which wasn't "
-                     "in the networkstatus!") % router.fingerprint)
-                if ignoreNetworkstatus:
-                    bridge = Bridge()
-                else:
+                bridge.assertOK()
+            except MalformedBridgeInfo as error:
+                logging.warn(str(error))
+            else:
+                bridges[bridge.fingerprint] = bridge
+
+        for filename in state.BRIDGE_FILES:
+            fn = expandBridgeAuthDir(auth, filename)
+            logging.info("Opening bridge-server-descriptor file: '%s'" % fn)
+            serverdescriptors = descriptors.parseServerDescriptorsFile(fn)
+            logging.debug("Closing bridge-server-descriptor file: '%s'" % fn)
+
+            for router in serverdescriptors:
+                try:
+                    bridge = bridges[router.fingerprint]
+                except KeyError:
+                    logging.warn(
+                        ("Received server descriptor for bridge '%s' which wasn't "
+                         "in the networkstatus!") % router.fingerprint)
+                    if ignoreNetworkstatus:
+                        bridge = Bridge()
+                    else:
+                        continue
+
+                try:
+                    bridge.updateFromServerDescriptor(router, ignoreNetworkstatus)
+                except (ServerDescriptorWithoutNetworkstatus,
+                        MissingServerDescriptorDigest,
+                        ServerDescriptorDigestMismatch) as error:
+                    logging.warn(str(error))
+                    # Reject any routers whose server descriptors didn't pass
+                    # :meth:`~bridges.Bridge._checkServerDescriptor`, i.e. those
+                    # bridges who don't have corresponding networkstatus
+                    # documents, or whose server descriptor digests don't check
+                    # out:
+                    bridges.pop(router.fingerprint)
                     continue
 
+                if state.COLLECT_TIMESTAMPS:
+                    # Update timestamps from server descriptors, not from network
+                    # status descriptors (because networkstatus documents and
+                    # descriptors aren't authenticated in any way):
+                    if bridge.fingerprint in timestamps.keys():
+                        timestamps[bridge.fingerprint].append(router.published)
+                    else:
+                        timestamps[bridge.fingerprint] = [router.published]
+
+        eifiles = [expandBridgeAuthDir(auth, fn) for fn in state.EXTRA_INFO_FILES]
+        extrainfos = descriptors.parseExtraInfoFiles(*eifiles)
+        for fingerprint, router in extrainfos.items():
             try:
-                bridge.updateFromServerDescriptor(router, ignoreNetworkstatus)
-            except (ServerDescriptorWithoutNetworkstatus,
-                    MissingServerDescriptorDigest,
-                    ServerDescriptorDigestMismatch) as error:
+                bridges[fingerprint].updateFromExtraInfoDescriptor(router)
+            except MalformedBridgeInfo as error:
                 logging.warn(str(error))
-                # Reject any routers whose server descriptors didn't pass
-                # :meth:`~bridges.Bridge._checkServerDescriptor`, i.e. those
-                # bridges who don't have corresponding networkstatus
-                # documents, or whose server descriptor digests don't check
-                # out:
-                bridges.pop(router.fingerprint)
-                continue
-
-            if state.COLLECT_TIMESTAMPS:
-                # Update timestamps from server descriptors, not from network
-                # status descriptors (because networkstatus documents and
-                # descriptors aren't authenticated in any way):
-                if bridge.fingerprint in timestamps.keys():
-                    timestamps[bridge.fingerprint].append(router.published)
-                else:
-                    timestamps[bridge.fingerprint] = [router.published]
-
-    extrainfos = descriptors.parseExtraInfoFiles(*state.EXTRA_INFO_FILES)
-    for fingerprint, router in extrainfos.items():
-        try:
-            bridges[fingerprint].updateFromExtraInfoDescriptor(router)
-        except MalformedBridgeInfo as error:
-            logging.warn(str(error))
-        except KeyError as error:
-            logging.warn(("Received extrainfo descriptor for bridge '%s', "
-                          "but could not find bridge with that fingerprint.")
-                         % router.fingerprint)
-
-    inserted = 0
-    logging.info("Inserting %d bridges into hashring..." % len(bridges))
-    for fingerprint, bridge in bridges.items():
-        # Skip insertion of bridges which are geolocated to be in one of the
-        # NO_DISTRIBUTION_COUNTRIES, a.k.a. the countries we don't distribute
-        # bridges from:
-        if bridge.country in state.NO_DISTRIBUTION_COUNTRIES:
-            logging.warn("Not distributing Bridge %s %s:%s in country %s!" %
-                         (bridge, bridge.address, bridge.orPort, bridge.country))
-        else:
-            # If the bridge is not running, then it is skipped during the
-            # insertion process.
-            hashring.insert(bridge)
-            inserted += 1
-    logging.info("Done inserting %d bridges into hashring." % inserted)
-
-    if state.COLLECT_TIMESTAMPS:
-        reactor.callInThread(updateBridgeHistory, bridges, timestamps)
+            except KeyError as error:
+                logging.warn(("Received extrainfo descriptor for bridge '%s', "
+                              "but could not find bridge with that fingerprint.")
+                             % router.fingerprint)
+
+        inserted = 0
+        logging.info("Inserting %d bridges into hashring..." % len(bridges))
+        for fingerprint, bridge in bridges.items():
+            # Skip insertion of bridges which are geolocated to be in one of the
+            # NO_DISTRIBUTION_COUNTRIES, a.k.a. the countries we don't distribute
+            # bridges from:
+            if bridge.country in state.NO_DISTRIBUTION_COUNTRIES:
+                logging.warn("Not distributing Bridge %s %s:%s in country %s!" %
+                             (bridge, bridge.address, bridge.orPort, bridge.country))
+            else:
+                # If the bridge is not running, then it is skipped during the
+                # insertion process.
+                hashring.insert(bridge)
+                inserted += 1
+        logging.info("Done inserting %d bridges into hashring." % inserted)
+
+        if state.COLLECT_TIMESTAMPS:
+            reactor.callInThread(updateBridgeHistory, bridges, timestamps)
 
-    state.save()
+        state.save()
 
 def _reloadFn(*args):
     """Placeholder callback function for :func:`_handleSIGHUP`."""
diff --git a/bridgedb/configure.py b/bridgedb/configure.py
index e107a57..6c13595 100644
--- a/bridgedb/configure.py
+++ b/bridgedb/configure.py
@@ -100,7 +100,7 @@ def loadConfig(configFile=None, configCls=None):
     #
     # See :meth:`bridgedb.persistent.State.useUpdatedSettings`.
 
-    for attr in ["PROXY_LIST_FILES", "BRIDGE_FILES", "EXTRA_INFO_FILES"]:
+    for attr in ["PROXY_LIST_FILES"]:
         setting = getattr(config, attr, None)
         if setting is None:  # pragma: no cover
             setattr(config, attr, []) # If they weren't set, make them lists
@@ -110,7 +110,7 @@ def loadConfig(configFile=None, configCls=None):
 
     for attr in ["DB_FILE", "DB_LOG_FILE", "MASTER_KEY_FILE", "PIDFILE",
                  "ASSIGNMENTS_FILE", "HTTPS_CERT_FILE", "HTTPS_KEY_FILE",
-                 "LOG_FILE", "STATUS_FILE", "COUNTRY_BLOCK_FILE",
+                 "LOG_FILE", "COUNTRY_BLOCK_FILE",
                  "GIMP_CAPTCHA_DIR", "GIMP_CAPTCHA_HMAC_KEYFILE",
                  "GIMP_CAPTCHA_RSA_KEYFILE", "EMAIL_GPG_HOMEDIR",
                  "EMAIL_GPG_PASSPHRASE_FILE"]:
diff --git a/requirements.txt b/requirements.txt
index bd25ae4..26c2c1a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,5 +10,5 @@ gnupg==2.0.1
 pillow>=2.6.1
 qrcode==5.0.1
 service_identity==14.0.0
-git+https://git.torproject.org/stem.git@ba8cee36a0348c1509ad3562051723b7948e19ce#egg=stem-1.4.1.dev0-py2.7
+git+https://git.torproject.org/stem.git@152fa89c33114619913d2be13e67adb4e55ce7ab#egg=stem-1.4.1.dev1-py2.7
 zope.interface==3.6.1





More information about the tor-commits mailing list