From 8a6129f4e2f11860258cc825c30d6f4241e3944e Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Mon, 8 Feb 2021 01:52:12 +0000 Subject: [PATCH] work on torgossip --- .../torcontrol/onionservice/__init__.py | 31 ++++++++------ .../torcontrol/onionservice/servicecontrol.py | 17 +++++--- src/onionrcommands/daemonlaunch/__init__.py | 8 ++++ .../default-plugins/torgossip/client.py | 41 ++++++++++++------- .../torgossip/clientfuncs/__init__.py | 0 .../default-plugins/torgossip/constants.py | 1 + static-data/default-plugins/torgossip/main.py | 28 ++++++++++++- .../default-plugins/torgossip/peerdb.py | 1 - .../default-plugins/torgossip/server.py | 2 +- 9 files changed, 95 insertions(+), 34 deletions(-) create mode 100644 static-data/default-plugins/torgossip/clientfuncs/__init__.py diff --git a/src/netcontroller/torcontrol/onionservice/__init__.py b/src/netcontroller/torcontrol/onionservice/__init__.py index 87f786fe..02bab477 100644 --- a/src/netcontroller/torcontrol/onionservice/__init__.py +++ b/src/netcontroller/torcontrol/onionservice/__init__.py @@ -9,7 +9,7 @@ from utils.identifyhome import identify_home from .servicecontrol import create_new_service, restore_service -ONION_KEY_DATABASE_FILE = identify_home() + "torgossip-onion-address-keys.db" +ONION_KEY_DATABASE_FILE = identify_home() + "onion-address-keys.db" class OnionServiceTarget(NamedTuple): @@ -17,27 +17,34 @@ class OnionServiceTarget(NamedTuple): unix_socket_path: str +class NoServices(ValueError): + pass + + def load_services(controller): db = SafeDB(ONION_KEY_DATABASE_FILE, protected=True) - keys = db.keys() + keys = db.db_conn.keys() + keys.remove(b'enc') + restored = [] if not keys: db.close() - raise ValueError("No addresses to restore") + raise NoServices("No addresses to restore") while keys: # Not most pythonic but reduces mem usage as it runs key = keys.pop() - if len(len) > 3: - try: - service = unpackb(db.get(key)) - restore_service( - controller, service['k'], service['p'], - unix_socket=service['s']) - except Exception as _: # noqa - db.close() - raise + try: + service = unpackb(db.get(key)) + service_id = restore_service( + controller, service['k'], int(service['p']), + unix_socket=service['s'])[0] + restored.append((service_id, service['s'])) + except Exception as _: # noqa + db.close() + raise db.close() + return restored def run_new_and_store_service(controller, target: OnionServiceTarget) -> bytes: diff --git a/src/netcontroller/torcontrol/onionservice/servicecontrol.py b/src/netcontroller/torcontrol/onionservice/servicecontrol.py index fbb31b7a..59678701 100644 --- a/src/netcontroller/torcontrol/onionservice/servicecontrol.py +++ b/src/netcontroller/torcontrol/onionservice/servicecontrol.py @@ -27,16 +27,18 @@ along with this program. If not, see . def _add_ephemeral_service( controller: 'Controller', virtual_port, target, key_content=None): - key_type = "ED25519-V3" + key_type = "NEW" if not key_content: - key_type = "NEW" + key_content = "ED25519-V3" + else: + key_type = "ED25519-V3" hs = controller.create_ephemeral_hidden_service( {virtual_port: target}, key_type=key_type, key_content=key_content, - await_publication=True, detached=True ) + return (hs.service_id, hs.private_key) @@ -65,10 +67,15 @@ def restore_service( bind_location: str = None): if unix_socket and bind_location or (not unix_socket and not bind_location): raise ValueError("Must pick unix socket or ip:port, and not both") - key = base64.b64encode(key) + + key = base64.b64encode(key).decode() + target = unix_socket if bind_location: target = bind_location - return _add_ephemeral_service(controller, virtual_port, target, key) + if not target.startswith("unix:"): + target = "unix:" + target + return _add_ephemeral_service( + controller, virtual_port, target, key_content=key) diff --git a/src/onionrcommands/daemonlaunch/__init__.py b/src/onionrcommands/daemonlaunch/__init__.py index 7dc0e77c..b4f07b05 100755 --- a/src/onionrcommands/daemonlaunch/__init__.py +++ b/src/onionrcommands/daemonlaunch/__init__.py @@ -18,6 +18,7 @@ from deadsimplekv import DeadSimpleKV import psutil import config +from netcontroller.torcontrol import onionservice, torcontroller import onionrstatistics from onionrstatistics import serializeddata import apiservers @@ -246,6 +247,13 @@ def daemon(): _setup_online_mode(use_existing_tor, net, security_level) _show_info_messages() + + with torcontroller.get_controller() as c: + try: + onionservice.load_services(c) + except onionservice.NoServices: + pass + logger.info( "Onionr daemon is running under " + str(os.getpid()), terminal=True) events.event('init', threaded=False, data=shared_state) diff --git a/static-data/default-plugins/torgossip/client.py b/static-data/default-plugins/torgossip/client.py index cdbe05d2..b55e5d8d 100644 --- a/static-data/default-plugins/torgossip/client.py +++ b/static-data/default-plugins/torgossip/client.py @@ -4,6 +4,7 @@ Torgossip client Create streams to random peers """ +import sys from base64 import b32encode from os import path from typing import TYPE_CHECKING @@ -17,6 +18,9 @@ from netcontroller.torcontrol import torcontroller if TYPE_CHECKING: from .peerdb import TorGossipPeers from stem.control import Controller +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) + +from commands import GossipCommands """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +35,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ +controller = torcontroller.get_controller() def _add_bootstrap_peers(peer_db: 'TorGossipPeers'): @@ -38,13 +43,18 @@ def _add_bootstrap_peers(peer_db: 'TorGossipPeers'): with open(bootstap_peers, 'r') as bs_peers: peers = bs_peers.split(',') for peer in peers: - if peer: + try: + peer_db.get(peer) + except KeyError: + pass + else: + continue + if peer and service_online_recently(controller, peer): peer_db.add_peer(peer) -def _client_pool(shared_state, controller: 'Controller'): +def _client_pool(shared_state, socket_pool: dict): peer_db: 'TorGossipPeers' = shared_state.get_by_string('TorGossipPeers') - socket_pool = {} socks_port = shared_state.get_by_string('NetController').socksPort peers = peer_db.get_highest_score_peers(20) @@ -61,20 +71,23 @@ def _client_pool(shared_state, controller: 'Controller'): try: socket_pool[peer] = s.connect( (b32encode(peer).decode().lower() + ".onion", 2021)) + except socket.GeneralProxyError: s.close() - -def client_pool(shared_state): - controller = torcontroller.get_controller() - # Pass the stem Controller and then close it even if an exception raises - try: - _client_pool(shared_state, controller) - except Exception: - raise - finally: - # Calls before the above raise no matter what - controller.close() +def client_loop(shared_state, socket_pool): + while True: + if not socket_pool: + _client_pool(shared_state, socket_pool) + + +def start_client(shared_state): + # add boot strap peers to db if we need peers + _add_bootstrap_peers(shared_state.get_by_string('TorGossipPeers')) + # create and fill pool of sockets to peers (over tor socks) + socket_pool = {} + _client_pool(shared_state, socket_pool) + client_loop(shared_state, socket_pool) diff --git a/static-data/default-plugins/torgossip/clientfuncs/__init__.py b/static-data/default-plugins/torgossip/clientfuncs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/static-data/default-plugins/torgossip/constants.py b/static-data/default-plugins/torgossip/constants.py index 148eb8ed..7a9cad43 100644 --- a/static-data/default-plugins/torgossip/constants.py +++ b/static-data/default-plugins/torgossip/constants.py @@ -1,3 +1,4 @@ from utils.identifyhome import identify_home SERVER_SOCKET = identify_home() + "torgossip.sock" +HOSTNAME_FILE = identify_home() + "torgossip-hostname" GOSSIP_PORT = 2020 \ No newline at end of file diff --git a/static-data/default-plugins/torgossip/main.py b/static-data/default-plugins/torgossip/main.py index 85482678..5d76d51b 100755 --- a/static-data/default-plugins/torgossip/main.py +++ b/static-data/default-plugins/torgossip/main.py @@ -3,14 +3,18 @@ Onionr - Private P2P Communication Default plugin which allows users to encrypt/decrypt messages w/o using blocks """ -from inspect import trace +from base64 import b32encode import locale + +from netcontroller.torcontrol import torcontroller locale.setlocale(locale.LC_ALL, '') import sys import os import traceback from threading import Thread +from netcontroller.torcontrol import onionservice sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +from constants import SERVER_SOCKET, GOSSIP_PORT, HOSTNAME_FILE """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,5 +48,27 @@ def on_init(api, data=None): shared_state.get(TorGossipPeers) + hs = "" + + try: + with open(HOSTNAME_FILE, "rb") as f: + hs = f.read() + if not hs: + raise FileNotFoundError + except FileNotFoundError: + with torcontroller.get_controller() as c: + + try: + hs = onionservice.run_new_and_store_service( + c, onionservice.OnionServiceTarget( + GOSSIP_PORT, SERVER_SOCKET)) + except Exception: + print(traceback.format_exc()) + raise + + with open(HOSTNAME_FILE, "wb") as hf: + hf.write(hs) + + Thread(target=start_server, daemon=True, args=[shared_state]).start() diff --git a/static-data/default-plugins/torgossip/peerdb.py b/static-data/default-plugins/torgossip/peerdb.py index e5562dbf..2f59a0c1 100644 --- a/static-data/default-plugins/torgossip/peerdb.py +++ b/static-data/default-plugins/torgossip/peerdb.py @@ -25,7 +25,6 @@ along with this program. If not, see . """ - class TorGossipPeers: # name it this way to avoid collisions in SharedState PACK_FORMAT = "qQ" diff --git a/static-data/default-plugins/torgossip/server.py b/static-data/default-plugins/torgossip/server.py index 19b9db3a..96673526 100644 --- a/static-data/default-plugins/torgossip/server.py +++ b/static-data/default-plugins/torgossip/server.py @@ -14,7 +14,7 @@ sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) from commands import GossipCommands # noqa import commandhandlers -from .constants import SERVER_SOCKET +from constants import SERVER_SOCKET """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by