Commit c56eeaa9 authored by Matt Traudt's avatar Matt Traudt Committed by juga
Browse files

Initial revision of RelayNS/Relay class

With unused support for ed25519 keys
parent a73e50f2
......@@ -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
......
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):
......
......@@ -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:
......
......@@ -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:
......
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'
......@@ -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])
@pytest.fixture
def tmpdir(tmpdir_factory, request):
base = str(hash(request.node.nodeid))[:3]
bn = tmpdir_factory.mktemp(base)
return bn
@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"))
@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()
......
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'
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