[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [bridgedb/master] Refactor getBridgesForIP() to use b.https.request.HTTPSBridgeRequest.
commit 7b880a1fe0f346c85364b255eee93995a6b31b94
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date: Wed Apr 1 08:40:50 2015 +0000
Refactor getBridgesForIP() to use b.https.request.HTTPSBridgeRequest.
* RENAME b.D.IPBasedDistributor.getBridgesForIP() â??
b.D.IPBasedDistributor.getBridges().
* CREATE new bridgedb.https package.
* CREATE new bridgedb.https.request module.
* CHANGE b.D.IPBasedDistributor.getBridges() to use
bridgedb.https.request.HTTPSBridgeRequest.
---
doc/sphinx/source/bridgedb.https.rst | 10 +++
doc/sphinx/source/bridgedb.rst | 1 +
lib/bridgedb/Dist.py | 143 ++++++++++++++--------------------
lib/bridgedb/HTTPServer.py | 142 ++++++++++++++-------------------
lib/bridgedb/https/__init__.py | 1 +
lib/bridgedb/https/request.py | 143 ++++++++++++++++++++++++++++++++++
lib/bridgedb/test/legacy_Tests.py | 26 +++----
lib/bridgedb/test/test_HTTPServer.py | 27 ++++---
setup.py | 1 +
9 files changed, 301 insertions(+), 193 deletions(-)
diff --git a/doc/sphinx/source/bridgedb.https.rst b/doc/sphinx/source/bridgedb.https.rst
new file mode 100644
index 0000000..b0ee95c
--- /dev/null
+++ b/doc/sphinx/source/bridgedb.https.rst
@@ -0,0 +1,10 @@
+.. _https-pkg:
+
+bridgedb.https
+---------------
+
+.. contents:: bridgedb.https
+ :depth: 3
+
+.. automodule:: bridgedb.https.__init__
+.. automodule:: bridgedb.https.request
diff --git a/doc/sphinx/source/bridgedb.rst b/doc/sphinx/source/bridgedb.rst
index 3f87647..61ab07f 100644
--- a/doc/sphinx/source/bridgedb.rst
+++ b/doc/sphinx/source/bridgedb.rst
@@ -17,6 +17,7 @@ BridgeDB Package and Module Documentation
bridgedb.email
bridgedb.Filters
bridgedb.geo
+ bridgedb.https
bridgedb.HTTPServer
bridgedb.interfaces
bridgedb.Main
diff --git a/lib/bridgedb/Dist.py b/lib/bridgedb/Dist.py
index 26e952e..3f7835b 100644
--- a/lib/bridgedb/Dist.py
+++ b/lib/bridgedb/Dist.py
@@ -305,118 +305,92 @@ class IPBasedDistributor(Distributor):
"""Assign a bridge to this distributor."""
self.splitter.insert(bridge)
- def getBridgesForIP(self, ip, epoch, N=1, countryCode=None,
- bridgeFilterRules=None):
+ def _buildHashringFilters(self, previousFilters, clientCluster):
+ totalRings = self.nClusters + len(self.categories)
+ g = filterAssignBridgesToRing(self.splitter.hmac, totalRings, clientCluster)
+ previousFilters.append(g)
+ return frozenset(previousFilters)
+
+ def getBridges(self, bridgeRequest, interval, N=1):
"""Return a list of bridges to give to a user.
- :param str ip: The user's IP address, as a dotted quad.
- :param str epoch: The time period when we got this request. This can
- be any string, so long as it changes with every
- period.
+ :type bridgeRequest: :class:`bridgedb.https.request.HTTPSBridgeRequest`
+ :param bridgeRequest: A :class:`~bridgedb.bridgerequest.BridgeRequestBase`
+ with the :data:`~bridgedb.bridgerequest.BridgeRequestBase.client`
+ attribute set to a string containing the client's IP address.
+ :param str interval: The time period when we got this request. This
+ can be any string, so long as it changes with every period.
:param int N: The number of bridges to try to give back. (default: 1)
- :param str countryCode: The two-letter geoip country code of the
- client's IP address. If given, it is assumed that any bridges
- distributed to that client should not be blocked in that
- country. (default: None)
- :param list bridgeFilterRules: A list of callables used filter the
- bridges returned in the response to the
- client. See :mod:`~bridgedb.Filters`.
:rtype: list
:return: A list of :class:`~bridgedb.Bridges.Bridge`s to include in
- the response. See
- :meth:`bridgedb.HTTPServer.WebResource.getBridgeRequestAnswer`
- for an example of how this is used.
+ the response. See
+ :meth:`bridgedb.HTTPServer.WebResourceBridges.getBridgeRequestAnswer`
+ for an example of how this is used.
"""
logging.info("Attempting to return %d bridges to client %s..."
- % (N, ip))
-
- if not bridgeFilterRules:
- bridgeFilterRules=[]
+ % (N, bridgeRequest.client))
if not len(self.splitter):
logging.warn("Bailing! Splitter has zero bridges!")
return []
- logging.debug("Bridges in splitter:\t%d" % len(self.splitter))
- logging.debug("Client request epoch:\t%s" % epoch)
- logging.debug("Active bridge filters:\t%s"
- % ' '.join([x.func_name for x in bridgeFilterRules]))
-
- area = self.areaMapper(ip)
- logging.debug("IP mapped to area:\t%s" % area)
-
- key1 = ''
- pos = 0
- n = self.nClusters
-
- # only one of ip categories or area clustering is active
- # try to match the request to an ip category
+ # The cluster the client should draw bridges from:
+ clientCluster = self.nClusters
+ # First, check if the client's IP is one of the known proxies in one
+ # of our :data:`catagories`:
for category in self.categories:
- # IP Categories
- if ip in category:
+ if bridgeRequest.client in category:
# The tag is a tag applied to a proxy IP address when it is
# added to the bridgedb.proxy.ProxySet. For Tor Exit relays,
# the default is 'exit_relay'. For other proxies loaded from
# the PROXY_LIST_FILES config option, the default tag is the
# full filename that the IP address originally came from.
- tag = category.getTag(ip)
- logging.info("Client was from known proxy (tag: %s): %s" % (tag, ip))
- g = filterAssignBridgesToRing(self.splitter.hmac,
- self.nClusters +
- len(self.categories),
- n)
- bridgeFilterRules.append(g)
+ tag = category.getTag(bridgeRequest.client)
+ logging.info("Client was from known proxy (tag: %s): %s" %
+ (tag, bridgeRequest.client))
# Cluster Tor/proxy users into four groups. This means that
# no matter how many different Tor Exits or proxies a client
# uses, the most they can ever get is four different sets of
# bridge lines (per period).
- group = (int(ipaddr.IPAddress(ip)) % 4) + 1
- logging.debug(("Assigning client hashring position based on: "
- "known-proxy<%s>%s") % (epoch, group))
- pos = self.areaOrderHmac("known-proxy<%s>%s" % (epoch, group))
- key1 = getHMAC(self.splitter.key,
- "Order-Bridges-In-Ring-%d" % n)
+ group = (int(ipaddr.IPAddress(bridgeRequest.client)) % 4) + 1
+ area = "known-proxy-group-%d" % group
break
- n += 1
-
- # if no category matches, use area clustering
+ clientCluster += 1
+ # If the client wasn't using Tor or any other known proxy, select the
+ # client's cluster number based upon the /16 of the client's IP
+ # address:
else:
- # IP clustering
- h = int( self.areaClusterHmac(area)[:8], 16)
- # length of numClusters
- clusterNum = h % self.nClusters
-
- g = filterAssignBridgesToRing(self.splitter.hmac,
- self.nClusters +
- len(self.categories),
- clusterNum)
- bridgeFilterRules.append(g)
- pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
- key1 = getHMAC(self.splitter.key,
- "Order-Bridges-In-Ring-%d" % clusterNum)
-
- # try to find a cached copy
- ruleset = frozenset(bridgeFilterRules)
-
- # See if we have a cached copy of the ring,
- # otherwise, add a new ring and populate it
- if ruleset in self.splitter.filterRings.keys():
- logging.debug("Cache hit %s" % ruleset)
- _, ring = self.splitter.filterRings[ruleset]
-
- # else create the ring and populate it
+ # Areas (i.e. /16s) are grouped into the number of rings specified
+ # by the N_IP_CLUSTERS configuration option.
+ area = self.areaMapper(bridgeRequest.client)
+ logging.debug("IP mapped to area:\t%s" % area)
+ clientCluster = int(self.areaClusterHmac(area)[:8], 16) % self.nClusters
+
+ pos = self.areaOrderHmac("<%s>%s" % (interval, area))
+ filters = self._buildHashringFilters(bridgeRequest.filters, clientCluster)
+
+ logging.debug("Assigned client hashring position based on: <%s>%s" %
+ (interval, area))
+ logging.debug("Bridges in splitter:\t%d" % len(self.splitter))
+ logging.debug("Active bridge filters:\t%s" %
+ ' '.join([x.func_name for x in filters]))
+
+ # Check wheth we have a cached copy of the hashring:
+ if filters in self.splitter.filterRings.keys():
+ logging.debug("Cache hit %s" % filters)
+ _, ring = self.splitter.filterRings[filters]
+ # Otherwise, construct a new hashring and populate it:
else:
- logging.debug("Cache miss %s" % ruleset)
+ logging.debug("Cache miss %s" % filters)
+ key1 = getHMAC(self.splitter.key, "Order-Bridges-In-Ring-%d" % clientCluster)
ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
- self.splitter.addRing(ring,
- ruleset,
- filterBridgesByRules(bridgeFilterRules),
+ self.splitter.addRing(ring, filters, filterBridgesByRules(filters),
populate_from=self.splitter.bridges)
- # get an appropriate number of bridges
- numBridgesToReturn = getNumBridgesPerAnswer(ring,
- max_bridges_per_answer=N)
- answer = ring.getBridges(pos, numBridgesToReturn)
+ # Determine the appropriate number of bridges to give to the client:
+ returnNum = getNumBridgesPerAnswer(ring, max_bridges_per_answer=N)
+ answer = ring.getBridges(pos, returnNum)
+
return answer
def __len__(self):
@@ -425,6 +399,7 @@ class IPBasedDistributor(Distributor):
def dumpAssignments(self, f, description=""):
self.splitter.dumpAssignments(f, description)
+
class EmailBasedDistributor(Distributor):
"""Object that hands out bridges based on the email address of an incoming
request and the current time period.
diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index 8c58380..179c061 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -23,8 +23,6 @@ import os
from functools import partial
from ipaddr import IPv4Address
-from ipaddr import IPv6Address
-from ipaddr import IPAddress
import mako.exceptions
from mako.template import Template
@@ -32,15 +30,11 @@ from mako.lookup import TemplateLookup
from twisted.internet import reactor
from twisted.internet.error import CannotListenError
-from twisted.python import filepath
from twisted.web import resource
from twisted.web import server
from twisted.web import static
from twisted.web.util import redirectTo
-import bridgedb.Dist
-import bridgedb.geo
-
from bridgedb import captcha
from bridgedb import crypto
from bridgedb import strings
@@ -50,6 +44,7 @@ from bridgedb.Filters import filterBridgesByIP4
from bridgedb.Filters import filterBridgesByIP6
from bridgedb.Filters import filterBridgesByTransport
from bridgedb.Filters import filterBridgesByNotBlockedIn
+from bridgedb.https.request import HTTPSBridgeRequest
from bridgedb.parse import headers
from bridgedb.parse.addr import isIPAddress
from bridgedb.qrcodes import generateQR
@@ -671,6 +666,18 @@ class WebResourceBridges(resource.Resource):
return response
+ def getClientIP(self, request):
+ """Get the client's IP address from the :header:`X-Forwarded-For`
+ header, or from the :api:`request <twisted.web.server.Request>`.
+
+ :type request: :api:`twisted.web.http.Request`
+ :param request: A ``Request`` object for a
+ :api:`twisted.web.resource.Resource`.
+ :rtype: None or str
+ :returns: The client's IP address, if it was obtainable.
+ """
+ return getClientIP(request, self.useForwardedHeader)
+
def getBridgeRequestAnswer(self, request):
"""Respond to a client HTTP request for bridges.
@@ -680,104 +687,75 @@ class WebResourceBridges(resource.Resource):
:rtype: str
:returns: A plaintext or HTML response to serve.
"""
- # XXX why are we getting the interval if our distributor might be
- # using bridgedb.schedule.Unscheduled?
+ bridgeLines = None
interval = self.schedule.intervalStart(time.time())
- bridges = ( )
- ip = None
- countryCode = None
-
- ip = getClientIP(request, self.useForwardedForHeader)
-
- # Look up the country code of the input IP
- if isIPAddress(ip):
- countryCode = bridgedb.geo.getCountryCode(IPAddress(ip))
- else:
- logging.warn("Invalid IP detected; skipping country lookup.")
- countryCode = None
-
- # XXX separate function again
- format = request.args.get("format", None)
- if format and len(format): format = format[0] # choose the first arg
-
- # do want any options?
- transport = ipv6 = unblocked = False
-
- ipv6 = request.args.get("ipv6", False)
- if ipv6: ipv6 = True # if anything after ?ipv6=
-
- # XXX oh dear hell. why not check for the '?transport=' arg before
- # regex'ing? And why not compile the regex once, somewhere outside
- # this function and class?
- try:
- # validate method name
- transport = re.match('[_a-zA-Z][_a-zA-Z0-9]*',
- request.args.get("transport")[0]).group()
- except (TypeError, IndexError, AttributeError):
- transport = None
-
- try:
- unblocked = re.match('[a-zA-Z]{2,4}',
- request.args.get("unblocked")[0]).group()
- except (TypeError, IndexError, AttributeError):
- unblocked = False
+ ip = self.getClientIP(request)
logging.info("Replying to web request from %s. Parameters were %r"
% (ip, request.args))
- rules = []
- bridgeLines = None
-
if ip:
- if ipv6:
- rules.append(filterBridgesByIP6)
- addressClass = IPv6Address
- else:
- rules.append(filterBridgesByIP4)
- addressClass = IPv4Address
+ bridgeRequest = HTTPSBridgeRequest()
+ bridgeRequest.client = ip
+ bridgeRequest.isValid(True)
+ bridgeRequest.withIPversion(request.args)
+ bridgeRequest.withPluggableTransportType(request.args)
+ bridgeRequest.withoutBlockInCountry(request)
+ bridgeRequest.generateFilters()
+
+ bridges = self.distributor.getBridges(bridgeRequest, interval)
+ bridgeLines = [replaceControlChars(bridge.getBridgeLine(
+ bridgeRequest, self.includeFingerprints)) for bridge in bridges]
- if transport:
- #XXX: A cleaner solution would differentiate between
- # addresses by protocol rather than have separate lists
- # Tor to be a transport, and selecting between them.
- rules = [filterBridgesByTransport(transport, addressClass)]
+ return self.renderAnswer(request, bridgeLines)
- if unblocked:
- rules.append(filterBridgesByNotBlockedIn(unblocked,
- addressClass, transport))
+ def getResponseFormat(self, request):
+ """Determine the requested format for the response.
+
+ :type request: :api:`twisted.web.http.Request`
+ :param request: A ``Request`` object containing the HTTP method, full
+ URI, and any URL/POST arguments and headers present.
+ :rtype: ``None`` or str
+ :returns: The argument of the first occurence of the ``format=`` HTTP
+ GET parameter, if any were present. (The only one which currently
+ has any effect is ``format=plain``, see note in
+ :meth:`renderAnswer`.) Otherwise, returns ``None``.
+ """
+ format = request.args.get("format", None)
+ if format and len(format):
+ format = format[0] # Choose the first arg
+ return format
- bridges = self.distributor.getBridgesForIP(ip, interval,
- self.nBridgesToGive,
- countryCode,
- bridgeFilterRules=rules)
- bridgeLines = [replaceControlChars(b.getConfigLine(
- includeFingerprint=self.includeFingerprints,
- addressClass=addressClass,
- transport=transport,
- request=bridgedb.Dist.uniformMap(ip))) for b in bridges]
+ def renderAnswer(self, request, bridgeLines=None):
+ """Generate a response for a client which includes **bridgesLines**.
- answer = self.renderAnswer(request, bridgeLines, format)
- return answer
+ .. note: The generated response can be plain or HTML. A plain response
+ looks like::
- def renderAnswer(self, request, bridgeLines=None, format=None):
- """Generate a response for a client which includes **bridges**.
+ voltron 1.2.3.4:1234 ABCDEF01234567890ABCDEF01234567890ABCDEF
+ voltron 5.5.5.5:5555 0123456789ABCDEF0123456789ABCDEF01234567
- The generated response can be plaintext or HTML.
+ That is, there is no HTML, what you see is what you get, and what
+ you get is suitable for pasting directly into Tor Launcher (or
+ into a torrc, if you prepend ``"Bridge "`` to each line). The
+ plain format can be requested from BridgeDB's web service by
+ adding an ``&format=plain`` HTTP GET parameter to the URL. Also
+ note that you won't get a QRCode, usage instructions, error
+ messages, or any other fanciness if you use the plain format.
:type request: :api:`twisted.web.http.Request`
:param request: A ``Request`` object containing the HTTP method, full
URI, and any URL/POST arguments and headers present.
:type bridgeLines: list or None
:param bridgeLines: A list of strings used to configure a Tor client
- to use a bridge.
- :type format: str or None
- :param format: If ``'plain'``, return a plaintext response. Otherwise,
- use the :file:`bridgedb/templates/bridges.html` template to render
- an HTML response page which includes the **bridges**.
+ to use a bridge. If ``None``, then the returned page will instead
+ explain that there were no bridges of the type they requested,
+ with instructions on how to proceed.
:rtype: str
:returns: A plaintext or HTML response to serve.
"""
rtl = False
+ format = self.getResponseFormat(request)
if format == 'plain':
request.setHeader("Content-Type", "text/plain")
diff --git a/lib/bridgedb/https/__init__.py b/lib/bridgedb/https/__init__.py
new file mode 100644
index 0000000..f1210ec
--- /dev/null
+++ b/lib/bridgedb/https/__init__.py
@@ -0,0 +1 @@
+"""Servers for BridgeDB's HTTPS bridge distributor."""
diff --git a/lib/bridgedb/https/request.py b/lib/bridgedb/https/request.py
new file mode 100644
index 0000000..a8f6920
--- /dev/null
+++ b/lib/bridgedb/https/request.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8; test-case-name: bridgedb.test.test_https_request; -*-
+#_____________________________________________________________________________
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Isis Lovecruft <isis@xxxxxxxxxxxxxx> 0xA3ADB67A2CDB8B35
+# :copyright: (c) 2007-2015, The Tor Project, Inc.
+# (c) 2013-2015, Isis Lovecruft
+# :license: see LICENSE for licensing information
+#_____________________________________________________________________________
+
+"""
+.. py:module:: bridgedb.https.request
+ :synopsis: Classes for parsing and storing information about requests for
+ bridges which are sent to the HTTPS distributor.
+
+bridgedb.https.request
+======================
+
+Classes for parsing and storing information about requests for bridges
+which are sent to the HTTPS distributor.
+
+::
+
+ bridgedb.https.request
+ |
+ |_ HTTPSBridgeRequest - A request for bridges which was received through
+ the HTTPS distributor.
+..
+"""
+
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import ipaddr
+import logging
+import re
+
+from bridgedb import bridgerequest
+from bridgedb import geo
+from bridgedb.parse import addr
+
+
+#: A regular expression for matching the Pluggable Transport methodname in
+#: HTTP GET request parameters.
+TRANSPORT_REGEXP = "[_a-zA-Z][_a-zA-Z0-9]*"
+TRANSPORT_PATTERN = re.compile(TRANSPORT_REGEXP)
+
+UNBLOCKED_REGEXP = "[a-zA-Z]{2}"
+UNBLOCKED_PATTERN = re.compile(UNBLOCKED_REGEXP)
+
+
+class HTTPSBridgeRequest(bridgerequest.BridgeRequestBase):
+ """We received a request for bridges through the HTTPS distributor."""
+
+ def __init__(self, addClientCountryCode=True):
+ """Process a new bridge request received through the
+ :class:`~bridgedb.Dist.IPBasedDistributor`.
+
+ :param bool addClientCountryCode: If ``True``, then calling
+ :meth:`withoutBlockInCountry` will attempt to add the client's own
+ country code, geolocated from her IP, to the ``notBlockedIn``
+ countries list.
+ """
+ super(HTTPSBridgeRequest, self).__init__()
+ self.addClientCountryCode = addClientCountryCode
+
+ def withIPversion(self, parameters):
+ """Determine if the request **parameters** were for bridges with IPv6
+ addresses or not.
+
+ .. note:: If there is an ``ipv6=`` parameter with anything non-zero
+ after it, then we assume the client wanted IPv6 bridges.
+
+ :param parameters: The :api:`twisted.web.http.Request.args`.
+ """
+ if parameters.get("ipv6", False):
+ logging.info("HTTPS request for bridges with IPv6 addresses.")
+ self.withIPv6()
+
+ def withoutBlockInCountry(self, request):
+ """Determine which countries the bridges for this **request** should
+ not be blocked in.
+
+ .. note:: Currently, a **request** for unblocked bridges is recognized
+ if it contains an HTTP GET parameter ``unblocked=`` whose value is
+ a comma-separater list of two-letter country codes. Any
+ two-letter country code found in the
+ :api:`request <twisted.web.http.Request>` ``unblocked=`` HTTP GET
+ parameter will be added to the :data:`notBlockedIn` list.
+
+ If :data:`addClientCountryCode` is ``True``, the the client's own
+ geolocated country code will be added to the to the
+ :data`notBlockedIn` list.
+
+ :type request: :api:`twisted.web.http.Request`
+ :param request: A ``Request`` object containing the HTTP method, full
+ URI, and any URL/POST arguments and headers present.
+ """
+ countryCodes = request.args.get("unblocked", list())
+
+ for countryCode in countryCodes:
+ try:
+ country = UNBLOCKED_PATTERN.match(countryCode).group()
+ except (TypeError, AttributeError):
+ pass
+ else:
+ if country:
+ self.notBlockedIn.append(country)
+ logging.info("HTTPS request for bridges not blocked in: %r"
+ % country)
+
+ if self.addClientCountryCode:
+ # Look up the country code of the input IP, and request bridges
+ # not blocked in that country.
+ if addr.isIPAddress(self.client):
+ country = geo.getCountryCode(ipaddr.IPAddress(self.client))
+ if country:
+ self.notBlockedIn.append(country)
+ logging.info(
+ ("HTTPS client's bridges also shouldn't be blocked "
+ "in their GeoIP country code: %s") % country)
+
+ def withPluggableTransportType(self, parameters):
+ """This request included a specific Pluggable Transport identifier.
+
+ Add any Pluggable Transport methodname found in the HTTP GET
+ **parameters** to the list of ``transports``. Currently, a request for
+ a transport is recognized if the request contains the
+ ``'transport='`` parameter.
+
+ :param parameters: The :api:`twisted.web.http.Request.args`.
+ """
+ for methodname in parameters.get("transport", list()):
+ try:
+ transport = TRANSPORT_PATTERN.match(methodname).group()
+ except (TypeError, AttributeError):
+ pass
+ else:
+ if transport:
+ self.transports.append(transport)
+ logging.info("HTTPS request for transport type: %r"
+ % transport)
diff --git a/lib/bridgedb/test/legacy_Tests.py b/lib/bridgedb/test/legacy_Tests.py
index 2e4d56f..97134e4 100644
--- a/lib/bridgedb/test/legacy_Tests.py
+++ b/lib/bridgedb/test/legacy_Tests.py
@@ -190,8 +190,8 @@ class IPBridgeDistTests(unittest.TestCase):
d = bridgedb.Dist.IPBasedDistributor(self.dumbAreaMapper, 3, "Foo")
for _ in xrange(256):
d.insert(fakeBridge())
- n = d.getBridgesForIP("1.2.3.4", "x", 2)
- n2 = d.getBridgesForIP("1.2.3.4", "x", 2)
+ n = d.getBridges("1.2.3.4", "x", 2)
+ n2 = d.getBridges("1.2.3.4", "x", 2)
self.assertEquals(n, n2)
def testDistWithCategories(self):
@@ -205,8 +205,8 @@ class IPBridgeDistTests(unittest.TestCase):
# Make sure that the categories do not overlap
f = lambda: ".".join([str(random.randrange(1,255)) for _ in xrange(4)])
g = lambda: ".".join([str(random.randrange(1,255)) for _ in xrange(3)] + ['255'])
- n = d.getBridgesForIP(g(), "x", 10)
- n2 = d.getBridgesForIP(f(), "x", 10)
+ n = d.getBridges(g(), "x", 10)
+ n2 = d.getBridges(f(), "x", 10)
assert(len(n) > 0)
assert(len(n2) > 0)
@@ -228,7 +228,7 @@ class IPBridgeDistTests(unittest.TestCase):
# d.insert(fakeBridge())
# for _ in xrange(32):
# i = randomIP()
- # n = d.getBridgesForIP(i, "x", 5)
+ # n = d.getBridges(i, "x", 5)
# count = 0
# fps = {}
# for b in n:
@@ -246,7 +246,7 @@ class IPBridgeDistTests(unittest.TestCase):
d.insert(fakeBridge(or_addresses=True))
for i in xrange(500):
- bridges = d.getBridgesForIP(randomIPv4String(),
+ bridges = d.getBridges(randomIPv4String(),
"faketimestamp",
bridgeFilterRules=[filterBridgesByIP6])
bridge = random.choice(bridges)
@@ -262,7 +262,7 @@ class IPBridgeDistTests(unittest.TestCase):
d.insert(fakeBridge(or_addresses=True))
for i in xrange(500):
- bridges = d.getBridgesForIP(randomIPv4String(),
+ bridges = d.getBridges(randomIPv4String(),
"faketimestamp",
bridgeFilterRules=[filterBridgesByIP4])
bridge = random.choice(bridges)
@@ -278,7 +278,7 @@ class IPBridgeDistTests(unittest.TestCase):
d.insert(fakeBridge(or_addresses=True))
for i in xrange(50):
- bridges = d.getBridgesForIP(randomIPv4String(),
+ bridges = d.getBridges(randomIPv4String(),
"faketimestamp", 1,
bridgeFilterRules=[
filterBridgesByIP4,
@@ -302,7 +302,7 @@ class IPBridgeDistTests(unittest.TestCase):
d.insert(fakeBridge(or_addresses=True))
for i in xrange(5):
- b = d.getBridgesForIP(randomIPv4String(), "x", 1, bridgeFilterRules=[
+ b = d.getBridges(randomIPv4String(), "x", 1, bridgeFilterRules=[
filterBridgesByIP4, filterBridgesByIP6])
assert len(b) == 0
@@ -325,10 +325,10 @@ class IPBridgeDistTests(unittest.TestCase):
b.blockingCountries[key] = set(['cn'])
for i in xrange(5):
- b = d.getBridgesForIP(randomIPv4String(), "x", 1, bridgeFilterRules=[
+ b = d.getBridges(randomIPv4String(), "x", 1, bridgeFilterRules=[
filterBridgesByNotBlockedIn("cn")])
assert len(b) == 0
- b = d.getBridgesForIP(randomIPv4String(), "x", 1, bridgeFilterRules=[
+ b = d.getBridges(randomIPv4String(), "x", 1, bridgeFilterRules=[
filterBridgesByNotBlockedIn("us")])
assert len(b) > 0
@@ -356,7 +356,7 @@ class IPBridgeDistTests(unittest.TestCase):
# we probably will get at least one bridge back!
# it's pretty unlikely to lose a coin flip 250 times in a row
for i in xrange(5):
- b = d.getBridgesForIP(randomIPString(), "x", 1,
+ b = d.getBridges(randomIPString(), "x", 1,
bridgeFilterRules=[
filterBridgesByNotBlockedIn("cn", methodname='obfs2'),
filterBridgesByTransport('obfs2'),
@@ -364,7 +364,7 @@ class IPBridgeDistTests(unittest.TestCase):
try: assert len(b) > 0
except AssertionError:
print("epic fail")
- b = d.getBridgesForIP(randomIPString(), "x", 1, bridgeFilterRules=[
+ b = d.getBridges(randomIPString(), "x", 1, bridgeFilterRules=[
filterBridgesByNotBlockedIn("us")])
assert len(b) > 0
diff --git a/lib/bridgedb/test/test_HTTPServer.py b/lib/bridgedb/test/test_HTTPServer.py
index d597c55..415fb8d 100644
--- a/lib/bridgedb/test/test_HTTPServer.py
+++ b/lib/bridgedb/test/test_HTTPServer.py
@@ -448,22 +448,22 @@ class DummyBridge(object):
self.or_addresses = {ipaddr.IPv6Address(self._returnSix()):
self._randORPort()}
- def getConfigLine(self, includeFingerprint=True,
- addressClass=ipaddr.IPv4Address,
- transport=None,
- request=None):
+ def getBridgeLine(self, bridgeRequest, includeFingerprint=True):
"""Get a "torrc" bridge config line to give to a client."""
+ if not bridgeRequest.isValid():
+ return
line = []
- if transport is not None:
- #print("getConfigLine got transport=%r" % transport)
- line.append(str(transport))
- line.append("%s:%s" % (self.ip, self.orport))
- if includeFingerprint is True: line.append(self.fingerprint)
+ if bridgeRequest.transports:
+ line.append(bridgeRequest.transports[-1]) # Just the last PT
+ if bridgeRequest.addressClass is ipaddr.IPv6Address:
+ line.append("[%s]:%s" % self.or_addresses.items()[0])
+ else:
+ line.append("%s:%s" % (self.ip, self.orport))
+ if includeFingerprint is True:
+ line.append(self.fingerprint)
if self.ptArgs:
line.append(','.join(['='.join(x) for x in self.ptArgs.items()]))
- bridgeLine = " ".join([item for item in line])
- #print "Created config line: %r" % bridgeLine
- return bridgeLine
+ return " ".join([item for item in line])
class DummyMaliciousBridge(DummyBridge):
@@ -499,8 +499,7 @@ class DummyIPBasedDistributor(object):
self.ipCategories = ipCategories
self.answerParameters = answerParameters
- def getBridgesForIP(self, ip=None, epoch=None, N=1,
- countyCode=None, bridgeFilterRules=None):
+ def getBridges(self, bridgeRequest=None, epoch=None, N=1):
"""Needed because it's called in
:meth:`WebResourceBridges.getBridgesForIP`.
"""
diff --git a/setup.py b/setup.py
index b788a7e..c75239a 100644
--- a/setup.py
+++ b/setup.py
@@ -392,6 +392,7 @@ setuptools.setup(
package_dir={'': 'lib'},
packages=['bridgedb',
'bridgedb.email',
+ 'bridgedb.https',
'bridgedb.parse',
'bridgedb.test'],
scripts=['scripts/bridgedb',
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits