Commit 8e3914d3 authored by Matt Traudt's avatar Matt Traudt Committed by Matt Traudt
Browse files

Perform periodic reachability tests to determine which helpers are useful

parent 764e1499
import random
from ..util.simpleauth import authenticate_to_server
from ..util.sockio import (make_socket, close_socket)
from sbws.globals import time_now
from sbws.lib.circuitbuilder import GapsCircuitBuilder as CB
import sbws.util.stem as stem_utils
from stem.descriptor.router_status_entry import RouterStatusEntryV3
from threading import RLock
import random
import logging
log = logging.getLogger(__name__)
......@@ -11,6 +17,11 @@ class HelperRelay:
self._server_host = conf_section['server_host']
self._server_port = conf_section.getint('server_port')
self._password = conf_section['password']
self._name ='.')[1]
def name(self):
return self._name
def fingerprint(self):
......@@ -39,7 +50,15 @@ class HelperRelayList:
self.controller = controller
for helper in helpers:
assert isinstance(helper, HelperRelay)
self.helpers = helpers
self._socks_proxy = (conf['tor']['socks_host'],
conf.getint('tor', 'socks_port'))
self._all_helpers = helpers
self._usable_helpers = set()
self._circuit_builder = CB(args, conf, controller)
self._last_reachability_test = 0
self._reachability_test_every = \
conf.getint('helpers', 'reachability_test_every')
self._reachability_lock = RLock()
def from_config(args, conf, controller=None):
......@@ -50,6 +69,8 @@ class HelperRelayList:
section = conf['helpers']
helpers = []
for key in section.keys():
if key == 'reachability_test_every':
if not section.getboolean(key):
log.debug('%s is disabled; not loading it', key)
......@@ -59,21 +80,80 @@ class HelperRelayList:
return HelperRelayList(args, conf, helpers, controller=controller), ''
def _should_perform_reachability_test(self):
return self._last_reachability_test + self._reachability_test_every <\
def _build_circuit_with_relay(self, relay):
assert isinstance(relay, RouterStatusEntryV3)
cb = self._circuit_builder
# Try three times to get a circuit ending at the target relay
# Note that the CircuitBuilder may try each path multiple times (as of
# the time of this comment's writing, it does)
for _ in range(0, 3):
circ_id = cb.build_circuit([None, relay.fingerprint])
if circ_id is not None:
return circ_id
return None
def _helper_server_is_functioning(self, helper, circ_id):
assert circ_id
cb = self._circuit_builder
sock = make_socket(*self._socks_proxy)
connected = stem_utils.connect_over_circuit(
cb.controller, circ_id, sock, helper.server_host,
if not connected:
return False
authed = authenticate_to_server(sock, helper.password)
return authed
def _perform_reachability_test(self):
log.debug('Performing reachability tests')
helpers = set()
for helper in self._all_helpers:
fp = helper.fingerprint
relay = stem_utils.fp_or_nick_to_relay(self.controller, fp)
if not relay:
log.warning('Helper %s\'s relay with fp %s does not seem to '
'be in the consensus. Ignoring it.',,
circ_id = self._build_circuit_with_relay(relay)
if not circ_id:
log.warning('Unable to build a circuit with helper %s\'s '
'relay %s in it. Ignoring it.',,
if not self._helper_server_is_functioning(helper, circ_id):
log.warning('Unable to speak with the sbws server for helper '
'%s. Ignoring it.',
log.debug('Helper %s is usable. Keeping it.', relay.nickname)
self._last_reachability_test = time_now()
self._usable_helpers = list(helpers)'After performing reachability tests, we have %d/%d usable '
'helpers: %s', len(self._usable_helpers),
', '.join([ for h in self._usable_helpers]))
def next(self, blacklist=[]):
''' Returns the next helper in the list that should be used. Do not
pick a helper that has a relay with a fingerprint in the given
blacklist. Returns None if no valid helper is available. '''
# XXX: Consider alternate designs. This just picks a random
# non-blacklisted helper. What if we could keep track of the health of
# the helpers? What if we picked geographically close helpers (probably
# in a different function called pick_best() or something)? What if we
# picked a helper that hasn't been used recently for this relay?
# Ideally we should see if the helper is online. It should be easy to
# do since the list has a copy of the stem controller. Just see if
# there's a descriptor for the given fingerprint.
for helper in self.helpers:
with self._reachability_lock:
if self._should_perform_reachability_test():
assert not self._should_perform_reachability_test()
for helper in self._usable_helpers:
if helper.fingerprint in blacklist:
return helper
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment