[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [bridgedb/develop] Filter returned bridges to avoid multiples from the same /16 or /32.
commit ad269657fe16f2603f207bdb2a979266c3b918e8
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date: Fri Dec 22 02:34:32 2017 +0000
Filter returned bridges to avoid multiples from the same /16 or /32.
* FIXES #24704 https://bugs.torproject.org/24704
---
bridgedb/Bridges.py | 52 ++++++++++++++++++++----
bridgedb/distributors/email/distributor.py | 2 +-
bridgedb/distributors/https/distributor.py | 2 +-
bridgedb/test/test_Bridges.py | 64 ++++++++++++++++++++++++++++++
4 files changed, 111 insertions(+), 9 deletions(-)
diff --git a/bridgedb/Bridges.py b/bridgedb/Bridges.py
index 13bffd2..dfc6ac5 100644
--- a/bridgedb/Bridges.py
+++ b/bridgedb/Bridges.py
@@ -268,7 +268,39 @@ class BridgeRing(object):
assert len(r) == N
return r
- def getBridges(self, pos, N=1):
+ def filterDistinctSubnets(self, fingerprints):
+ """Given a chosen set of ``fingerprints`` of bridges to distribute,
+ filter the bridges such that they are in distinct subnets.
+ """
+ logging.debug("Got %d possible bridges to filter" % len(fingerprints))
+
+ bridges = []
+ subnets = []
+
+ for fingerprint in fingerprints:
+ bridge = self.bridges[fingerprint]
+ jump = False
+ for subnet in subnets:
+ if bridge.address in subnet:
+ jump = True
+ logging.debug(
+ ("Skipping distribution of bridge %s in a subnet which "
+ "contains another bridge we're already distributing")
+ % bridge)
+ break
+ if jump:
+ continue
+
+ bridges.append(bridge)
+ if bridge.address.version == 4:
+ cidr = str(bridge.address) + "/16"
+ else:
+ cidr = str(bridge.address) + "/32"
+ subnets.append(ipaddr.IPNetwork(cidr))
+
+ return bridges
+
+ def getBridges(self, pos, N=1, filterBySubnet=False):
"""Return **N** bridges appearing in this hashring after a position.
:param bytes pos: The position to jump to. Any bridges returned will
@@ -285,19 +317,25 @@ class BridgeRing(object):
count = len(subring)
forced.extend(subring._getBridgeKeysAt(pos, count))
- keys = [ ]
- for k in forced + self._getBridgeKeysAt(pos, N):
+ keys = []
+
+ # Oversample double the number we need, in case we need to
+ # filter them and some are within the same subnet.
+ for k in forced + self._getBridgeKeysAt(pos, N + N):
if k not in keys:
keys.append(k)
else:
logging.debug(
"Got duplicate bridge %r in main hashring for position %r."
% (logSafely(k.encode('hex')), pos.encode('hex')))
- keys = keys[:N]
keys.sort()
- #Do not return bridges from the same /16
- bridges = [ self.bridges[k] for k in keys ]
+ if filterBySubnet:
+ bridges = self.filterDistinctSubnets(keys)
+ else:
+ bridges = [self.bridges[k] for k in keys]
+
+ bridges = bridges[:N]
return bridges
@@ -551,7 +589,7 @@ class FilteredBridgeSplitter(object):
For all sub-hashrings, the ``bridge`` will only be added iff it passes
the filter functions for that sub-hashring.
- :type bridge: :class:`~bridgedb.Bridges.Bridge`
+ :type bridge: :class:`~bridgedb.bridges.Bridge`
:param bridge: The bridge to add.
"""
# The bridge must be running to insert it:
diff --git a/bridgedb/distributors/email/distributor.py b/bridgedb/distributors/email/distributor.py
index b76c26c..fbf1a50 100644
--- a/bridgedb/distributors/email/distributor.py
+++ b/bridgedb/distributors/email/distributor.py
@@ -191,7 +191,7 @@ class EmailDistributor(Distributor):
populate_from=self.hashring.bridges)
returnNum = self.bridgesPerResponse(ring)
- result = ring.getBridges(pos, returnNum)
+ result = ring.getBridges(pos, returnNum, filterBySubnet=False)
db.setEmailTime(bridgeRequest.client, now)
db.commit()
diff --git a/bridgedb/distributors/https/distributor.py b/bridgedb/distributors/https/distributor.py
index 5ff9d83..7bce8d9 100644
--- a/bridgedb/distributors/https/distributor.py
+++ b/bridgedb/distributors/https/distributor.py
@@ -342,6 +342,6 @@ class HTTPSDistributor(Distributor):
# Determine the appropriate number of bridges to give to the client:
returnNum = self.bridgesPerResponse(ring)
- answer = ring.getBridges(position, returnNum)
+ answer = ring.getBridges(position, returnNum, filterBySubnet=True)
return answer
diff --git a/bridgedb/test/test_Bridges.py b/bridgedb/test/test_Bridges.py
new file mode 100644
index 0000000..092b033
--- /dev/null
+++ b/bridgedb/test/test_Bridges.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+#_____________________________________________________________________________
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@xxxxxxxxxxxxxx>
+# :copyright: (c) 2017, Isis Lovecruft
+# (c) 2017, The Tor Project, Inc.
+# :license: see LICENSE for licensing information
+#_____________________________________________________________________________
+
+"""Unittests for :mod:`bridgedb.Bridges`."""
+
+from __future__ import print_function
+
+import copy
+import ipaddr
+import logging
+
+from twisted.trial import unittest
+
+from bridgedb import Bridges
+from bridgedb.test import util
+
+# For additional logger output for debugging, comment out the following:
+#logging.disable(50)
+# and then uncomment the following line:
+Bridges.logging.getLogger().setLevel(10)
+
+
+class BridgeRingTests(unittest.TestCase):
+ """Unittests for :class:`bridgedb.Bridges.BridgeRing`."""
+
+ def setUp(self):
+ self.ring = Bridges.BridgeRing('fake-hmac-key')
+
+ def addBridgesFromSameSubnet(self):
+ bridges = copy.deepcopy(util.generateFakeBridges())
+ subnet = "5.5.%d.%d"
+ i = 1
+ j = 1
+
+ for bridge in bridges:
+ bridge.address = ipaddr.IPAddress(subnet % (i, j))
+ self.ring.insert(bridge)
+
+ if j == 255:
+ j = 1
+ i += 1
+ else:
+ j += 1
+
+ def test_filterDistinctSubnets(self):
+ """If there are bridges in the same subnet then they should be
+ filtered out of the results.
+ """
+ self.addBridgesFromSameSubnet()
+
+ chosen = self.ring.bridges.keys()[:10]
+ bridges = self.ring.filterDistinctSubnets(chosen)
+
+ # Since they're all in the same /16, we should only get one
+ # bridge back:
+ self.assertEqual(len(bridges), 1)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits