commit c56eeaa962a33079685bbb7c30c944a594197275
Author: Matt Traudt <sirmatt(a)ksu.edu>
Date: Thu Jun 7 20:54:11 2018 -0400
Initial revision of RelayNS/Relay class
With unused support for ed25519 keys
---
sbws/globals.py | 3 +
sbws/lib/relaylist.py | 120 ++++++++++++++++++--------------
sbws/lib/resultdump.py | 4 +-
sbws/util/stem.py | 3 +-
tests/integration/lib/test_relaylist.py | 17 +++++
tests/unit/conftest.py | 50 +------------
tests/unit/lib/test_relaylist.py | 13 ----
7 files changed, 93 insertions(+), 117 deletions(-)
diff --git a/sbws/globals.py b/sbws/globals.py
index a6621ab..a16682c 100644
--- a/sbws/globals.py
+++ b/sbws/globals.py
@@ -22,6 +22,9 @@ TORRC_STARTING_POINT = {
'CookieAuthentication': '1',
# To avoid path bias warnings
'UseEntryGuards': '0',
+ # Because we need things from full server descriptors (namely for now: the
+ # bandwidth line)
+ 'UseMicrodescriptors': '0',
}
SCALE_CONSTANT = 7500
diff --git a/sbws/lib/relaylist.py b/sbws/lib/relaylist.py
index 6d0743d..c0ca9ad 100644
--- a/sbws/lib/relaylist.py
+++ b/sbws/lib/relaylist.py
@@ -1,5 +1,5 @@
from stem.descriptor.router_status_entry import RouterStatusEntryV3
-
+from stem.descriptor.server_descriptor import ServerDescriptor
import sbws.util.stem as stem_utils
from stem import Flag
from stem import DescriptorUnavailable
@@ -13,16 +13,73 @@ from sbws.globals import resolve
log = logging.getLogger(__name__)
-class RelayNS(RouterStatusEntryV3):
- """Inherit from RouterStatusEntryV3 and add the attribute
- master_key_ed25519.
+class Relay:
+ def __init__(self, fp, cont, ns=None, desc=None):
+ '''
+ Given a relay fingerprint, fetch all the information about a relay that
+ sbws currently needs and store it in this class. Acts as an abstraction
+ to hide the confusion that is Tor consensus/descriptor stuff.
+
+ :param str fp: fingerprint of the relay.
+ :param cont: active and valid stem Tor controller connection
+ '''
+ assert isinstance(fp, str)
+ assert len(fp) == 40
+ assert stem_utils.is_controller_okay(cont)
+ if ns is not None:
+ assert isinstance(ns, RouterStatusEntryV3)
+ self._ns = ns
+ else:
+ self._ns = cont.get_network_status(fp, default=None)
+ if desc is not None:
+ assert isinstance(desc, ServerDescriptor)
+ self._desc = desc
+ else:
+ self._desc = cont.get_server_descriptor(fp, default=None)
+
+ def _from_desc(self, attr):
+ if not self._desc:
+ return None
+ assert hasattr(self._desc, attr)
+ return getattr(self._desc, attr)
+
+ def _from_ns(self, attr):
+ if not self._ns:
+ return None
+ assert hasattr(self._ns, attr)
+ return getattr(self._ns, attr)
+
+ @property
+ def nickname(self):
+ return self._from_ns('nickname')
+
+ @property
+ def fingerprint(self):
+ return self._from_ns('fingerprint')
+
+ @property
+ def flags(self):
+ return self._from_ns('flags')
+
+ @property
+ def exit_policy(self):
+ return self._from_desc('exit_policy')
+
+ @property
+ def average_bandwidth(self):
+ return self._from_desc('average_bandwidth')
+
+ @property
+ def bandwidth(self):
+ return self._from_ns('bandwidth')
+
+ @property
+ def address(self):
+ return self._from_ns('address')
- :param str ed25519: the ed25519 master key base 64 encoded.
- """
- def __init__(self, ed25519=None, *args, **kwargs):
- super().__init__(*args, **kwargs)
- if ed25519 is not None:
- self.master_key_ed25519 = ed25519
+ @property
+ def ed25519_master_key(self):
+ return self._from_desc('ed25519_master_key').rstrip('=')
class RelayList:
@@ -82,40 +139,6 @@ class RelayList:
# return [r for r in relays if r.measured is not None]
return [r for r in relays if not r.is_unmeasured]
- def relay_ed25519_master_key(self, ns):
- """Obtain ed25519 master key of the relay represented by
- the network status relay line.
-
- :param RouterStatusEntryV3 ns: the network status relay
- :returns: str, the ed25519 master key base 64 encoded without
- trailing '='s.
- """
- # In theory this is never going to be the case?
- if ns.identifier is None or ns.identifier_type != 'ed25519':
- log.debug('Getting microdescriptor to obtain ed25519 identity.')
- mdesc = self._controller.get_microdescriptor(ns.fingerprint, None)
- if mdesc is not None:
- if 'ed25519' in mdesc.identifiers.keys():
- ed25519 = mdesc.identifiers['ed25519'].rstrip('=')
- log.debug('Found ed25519 master key.')
- return ed25519
- log.debug('No ed25519 master-key found')
- log.debug('Could not get microdescriptor')
- # In case Tor can not retrive microdescriptors,
- # try with server descriptors.
- log.debug('Getting server descriptor to obtain '
- 'ed25519 master key.')
- sdesc = self._controller.get_server_descriptor(ns.fingerprint,
- None)
- if sdesc is not None:
- ed25519 = sdesc.ed25519_master_key().rstrip('=')
- log.debug('Found ed25519 master key.')
- return ed25519
- log.debug('Could not get server descriptor')
- return None
- log.debug('Relay has already ed25519 master key')
- return ns.identifier
-
def exits_can_exit_to(self, host, port):
'''
Return exits that can MOST LIKELY exit to the given host:port. **host**
@@ -179,13 +202,8 @@ class RelayList:
def _init_relays(self):
c = self._controller
assert stem_utils.is_controller_okay(c)
- relays = []
- # for each network status relay, obtain the ed25519 master key
- # and generate a new list of RelayNS objects
- for ns in c.get_network_statuses():
- ed25519 = self.relay_ed25519_master_key(ns)
- rns = RelayNS(ed25519=ed25519, content=ns._raw_contents)
- relays.append(rns)
+ relays = [Relay(ns.fingerprint, c, ns=ns)
+ for ns in c.get_network_statuses()]
return relays
def _refresh(self):
diff --git a/sbws/lib/resultdump.py b/sbws/lib/resultdump.py
index 7cff36f..ffa493d 100644
--- a/sbws/lib/resultdump.py
+++ b/sbws/lib/resultdump.py
@@ -11,9 +11,9 @@ from queue import Empty
from datetime import datetime
from datetime import timedelta
from enum import Enum
-from stem.descriptor.router_status_entry import RouterStatusEntryV3
from sbws.globals import RESULT_VERSION
from sbws.util.filelock import DirectoryLock
+from sbws.lib.relaylist import Relay
log = logging.getLogger(__name__)
@@ -481,7 +481,7 @@ class ResultDump:
'Ignoring %s', type(data))
def results_for_relay(self, relay):
- assert isinstance(relay, RouterStatusEntryV3)
+ assert isinstance(relay, Relay)
fp = relay.fingerprint
with self.data_lock:
if fp not in self.data:
diff --git a/sbws/util/stem.py b/sbws/util/stem.py
index 5291bba..0341d74 100644
--- a/sbws/util/stem.py
+++ b/sbws/util/stem.py
@@ -2,7 +2,6 @@ from stem.control import (Controller, Listener)
from stem import (SocketError, InvalidRequest, UnsatisfiableRequest)
from stem.connection import IncorrectSocketType
import stem.process
-from stem.descriptor.router_status_entry import RouterStatusEntryV3
from configparser import ConfigParser
from threading import RLock
import copy
@@ -223,7 +222,7 @@ def only_relays_with_bandwidth(controller, relays, min_bw=None, max_bw=None):
assert max_bw is None or max_bw >= 0
ret = []
for relay in relays:
- assert isinstance(relay, RouterStatusEntryV3)
+ assert hasattr(relay, 'bandwidth')
if min_bw is not None and relay.bandwidth < min_bw:
continue
if max_bw is not None and relay.bandwidth > max_bw:
diff --git a/tests/integration/lib/test_relaylist.py b/tests/integration/lib/test_relaylist.py
new file mode 100644
index 0000000..8f16ef8
--- /dev/null
+++ b/tests/integration/lib/test_relaylist.py
@@ -0,0 +1,17 @@
+from sbws.lib.relaylist import Relay
+
+
+def test_relay_properties(persistent_launch_tor):
+ cont = persistent_launch_tor
+ # AA45C13025C037F056E734169891878ED0880231 is auth1
+ fp = 'AA45C13025C037F056E734169891878ED0880231'
+ relay = Relay(fp, cont)
+ assert relay.nickname == 'auth1'
+ assert relay.fingerprint == 'AA45C13025C037F056E734169891878ED0880231'
+ assert 'Authority' in relay.flags
+ assert not relay.exit_policy or not relay.exit_policy.is_exiting_allowed()
+ assert relay.average_bandwidth == 1073741824
+ assert relay.bandwidth == 0
+ assert relay.address == '127.10.0.1'
+ assert relay.ed25519_master_key == \
+ 'wLglSEw9/DHfpNrlrqjVRSnGLVWfnm0vYxkryH4aT6Q'
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
index 2847fe1..0701646 100644
--- a/tests/unit/conftest.py
+++ b/tests/unit/conftest.py
@@ -2,9 +2,8 @@ from sbws.lib.resultdump import ResultError
from sbws.lib.resultdump import ResultSuccess
from sbws.lib.resultdump import Result
from sbws.lib.resultdump import write_result_to_datadir
-from sbws.util.config import get_config, _get_default_config
+from sbws.util.config import get_config
from sbws.util.parser import create_parser
-import sbws.util.stem as stem_utils
import sbws.core.init
from tempfile import TemporaryDirectory
import pytest
@@ -28,53 +27,6 @@ class _PseudoArguments(argparse.Namespace):
setattr(self, key, kw[key])
-(a)pytest.fixture
-def tmpdir(tmpdir_factory, request):
- base = str(hash(request.node.nodeid))[:3]
- bn = tmpdir_factory.mktemp(base)
- return bn
-
-
-(a)pytest.fixture()
-def datadir(request):
- """ get, read, open test files from the "data" directory. """
- class D:
- def __init__(self, basepath):
- self.basepath = basepath
-
- def open(self, name, mode="r"):
- return self.basepath.join(name).open(mode)
-
- def join(self, name):
- return self.basepath.join(name).strpath
-
- def read_bytes(self, name):
- with self.open(name, "rb") as f:
- return f.read()
-
- def read(self, name):
- with self.open(name, "r") as f:
- return f.read()
-
- def readlines(self, name):
- with self.open(name, "r") as f:
- return f.readlines()
- return D(request.fspath.dirpath("data"))
-
-
-(a)pytest.fixture()
-def start_tor(request, tmpdir):
- """Star Tor or connect to existing socket in a temporal directory."""
- conf = _get_default_config()
- home = tmpdir.join('.sbws')
- conf['paths']['sbws_home'] = home.strpath
- controller, _ = stem_utils.init_controller(
- path=conf['tor']['control_socket'])
- if not controller:
- controller = stem_utils.launch_tor(conf)
- return controller
-
-
@pytest.fixture(scope='session')
def parser():
return create_parser()
diff --git a/tests/unit/lib/test_relaylist.py b/tests/unit/lib/test_relaylist.py
deleted file mode 100644
index cdb71c2..0000000
--- a/tests/unit/lib/test_relaylist.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from sbws.lib.relaylist import RelayList
-
-
-def test_relaylist_master_key_ed25519(start_tor):
- # This test starts tor, so it is slow. And it will fail whenever there are
- # network problems
- controller = start_tor
- rl = RelayList(None, None, controller)
- relay = [r for r in rl.relays if r.nickname == 'moria1'][0]
- assert relay.fingerprint == '9695DFC35FFEB861329B9F1AB04C46397020CE31'
- assert relay.identifier is None
- assert relay.master_key_ed25519 == \
- 'yp0fwtp4aa/VMyZJGz8vN7Km3zYet1YBZwqZEk1CwHI'