[tor-commits] [bridgedb/develop] Add support for respecting "bridge-distribution-request" lines.
isis at torproject.org
isis at torproject.org
Sat Oct 28 00:29:39 UTC 2017
commit 75206116fe6f39538f522776a158ca79959a6e79
Author: Isis Lovecruft <isis at torproject.org>
Date: Fri Oct 27 02:16:30 2017 +0000
Add support for respecting "bridge-distribution-request" lines.
* CLOSES #23957: https://bugs.torproject.org/23957
---
bridgedb/Bridges.py | 65 +++++++++++++++++++++++++++++++++++++++--------------
bridgedb/Storage.py | 24 ++++++++++++++++++++
bridgedb/bridges.py | 10 +++++++++
3 files changed, 82 insertions(+), 17 deletions(-)
diff --git a/bridgedb/Bridges.py b/bridgedb/Bridges.py
index ddfb447..ac403b6 100644
--- a/bridgedb/Bridges.py
+++ b/bridgedb/Bridges.py
@@ -454,29 +454,60 @@ class BridgeSplitter(object):
if not bridge.flags.running:
return
- # Determine which ring to put this bridge in if we haven't seen it
- # before.
- pos = self.hmac(bridge.identity)
- n = int(pos[:8], 16) % self.totalP
- pos = bisect.bisect_right(self.pValues, n) - 1
- assert 0 <= pos < len(self.rings)
- ringname = self.rings[pos]
- logging.info("%s placing bridge %s into hashring %s (via n=%s, pos=%s)."
- % (self.__class__.__name__, bridge, ringname, n, pos))
-
validRings = self.rings + self.pseudoRings
+ distribution_method = None
+ # If the bridge already has a distributor, use that.
with bridgedb.Storage.getDB() as db:
- ringname = db.insertBridgeAndGetRing(bridge, ringname, time.time(),
- validRings)
+ distribution_method = db.getBridgeDistributor(bridge, validRings)
+
+ if distribution_method:
+ logging.info("%s bridge %s was already in hashring %s" %
+ (self.__class__.__name__, bridge, distribution_method))
+ else:
+ # Check if the bridge requested a specific distribution method.
+ if bridge.distribution_request:
+ distribution_method = bridge.distribution_request
+ logging.info("%s bridge %s requested placement in hashring %s"
+ % (self.__class__.__name__, bridge,
+ distribution_method))
+
+ # If they requested not to be distributed, honor the request:
+ if distribution_method == "none":
+ logging.info("%s bridge %s requested to not be distributed."
+ % (self.__class__.__name__, bridge))
+ return
+
+ # If we didn't know what they are talking about, or they requested
+ # "any" distribution method, and we've never seen this bridge
+ # before, then determine where to place it.
+ if ((distribution_method not in validRings) or
+ (distribution_method == "any")):
+
+ pos = self.hmac(bridge.identity)
+ n = int(pos[:8], 16) % self.totalP
+ pos = bisect.bisect_right(self.pValues, n) - 1
+ assert 0 <= pos < len(self.rings)
+ distribution_method = self.rings[pos]
+ logging.info(("%s placing bridge %s into hashring %s (via n=%s,"
+ " pos=%s).") % (self.__class__.__name__, bridge,
+ distribution_method, n, pos))
+
+ with bridgedb.Storage.getDB() as db:
+ ringname = db.insertBridgeAndGetRing(bridge, distribution_method,
+ time.time(), validRings)
db.commit()
- # Pseudo distributors are always held in the "unallocated" ring
- if ringname in self.pseudoRings:
- ringname = "unallocated"
+ # Pseudo distributors are always held in the "unallocated" ring
+ if ringname in self.pseudoRings:
+ ringname = "unallocated"
+
+ ring = self.ringsByName.get(ringname)
+ ring.insert(bridge)
- ring = self.ringsByName.get(ringname)
- ring.insert(bridge)
+ if ring is None:
+ logging.warn("Couldn't recognise ring named: '%s'" % ringname)
+ logging.info("Current rings: %s" % " ".join(self.ringsByName))
def dumpAssignments(self, f, description=""):
for name,ring in self.ringsByName.iteritems():
diff --git a/bridgedb/Storage.py b/bridgedb/Storage.py
index ea9d26b..4182c4b 100644
--- a/bridgedb/Storage.py
+++ b/bridgedb/Storage.py
@@ -145,6 +145,26 @@ class Database(object):
self._cur.close()
self._conn.close()
+ def getBridgeDistributor(self, bridge, validRings):
+ """If a ``bridge`` is already in the database, get its distributor.
+
+ :rtype: None or str
+ :returns: The ``bridge`` distribution method, if one was
+ already assigned, otherwise, returns None.
+ """
+ distribution_method = None
+ cur = self._cur
+
+ cur.execute("SELECT id, distributor FROM Bridges WHERE hex_key = ?",
+ (bridge.fingerprint,))
+ result = cur.fetchone()
+
+ if result:
+ if result[1] in validRings:
+ distribution_method = result[1]
+
+ return distribution_method
+
def insertBridgeAndGetRing(self, bridge, setRing, seenAt, validRings,
defaultPool="unallocated"):
'''Updates info about bridge, setting ring to setRing if none was set.
@@ -175,6 +195,10 @@ class Database(object):
timeToStr(seenAt), i))
return ring
else:
+ # Check if this is currently a valid ring name. If not, move back
+ # into default pool.
+ if setRing not in validRings:
+ setRing = defaultPool
# Insert it.
cur.execute("INSERT INTO Bridges (hex_key, address, or_port, "
"distributor, first_seen, last_seen) "
diff --git a/bridgedb/bridges.py b/bridgedb/bridges.py
index 4386d4e..8b4bc1b 100644
--- a/bridgedb/bridges.py
+++ b/bridgedb/bridges.py
@@ -914,6 +914,12 @@ class Bridge(BridgeBackwardsCompatibility):
currently serving clients (e.g. if the Bridge hit its configured
``RelayBandwidthLimit``); ``False`` otherwise.
+ :vartype distribution_request: str
+ :ivar distribution_request: If the bridge specified a
+ "bridgedb-distribution-request" line in its ``@type
+ bridge-server-descriptor``, the requested distribution method will be
+ stored here. If the line was absent, this will be set to ``"any"``.
+
:vartype _blockedIn: dict
:ivar _blockedIn: A dictionary of ``ADDRESS:PORT`` pairs to lists of
lowercased, two-letter country codes (e.g. ``"us"``, ``"gb"``,
@@ -963,6 +969,7 @@ class Bridge(BridgeBackwardsCompatibility):
self.flags = Flags()
self.hibernating = False
self._blockedIn = {}
+ self.distribution_request = "any"
self.bandwidth = None
self.bandwidthAverage = None
@@ -1595,6 +1602,9 @@ class Bridge(BridgeBackwardsCompatibility):
self._updateORAddresses(descriptor.or_addresses)
self.hibernating = descriptor.hibernating
+ if descriptor.bridge_distribution:
+ self.distribution_request = descriptor.bridge_distribution
+
self.onionKey = descriptor.onion_key
self.ntorOnionKey = descriptor.ntor_onion_key
self.signingKey = descriptor.signing_key
More information about the tor-commits
mailing list