A rough implementation of onion service based on pybitmessage plugin
This commit is contained in:
parent
638f613c03
commit
291ea9b195
|
@ -276,6 +276,15 @@ def main():
|
||||||
logging.error('Unsupported proxy schema!')
|
logging.error('Unsupported proxy schema!')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if shared.tor:
|
||||||
|
try:
|
||||||
|
from . import tor
|
||||||
|
except ImportError:
|
||||||
|
logging.info('Failed to import tor module.', exc_info=True)
|
||||||
|
else:
|
||||||
|
if not tor.start_tor_service():
|
||||||
|
logging.warning('Failed to start tor service.')
|
||||||
|
|
||||||
if shared.ip_enabled and not shared.trusted_peer:
|
if shared.ip_enabled and not shared.trusted_peer:
|
||||||
bootstrap_from_dns()
|
bootstrap_from_dns()
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Manager(threading.Thread):
|
||||||
self.last_pickled_objects = time.time()
|
self.last_pickled_objects = time.time()
|
||||||
self.last_pickled_nodes = time.time()
|
self.last_pickled_nodes = time.time()
|
||||||
# Publish destination 5-15 minutes after start
|
# Publish destination 5-15 minutes after start
|
||||||
self.last_published_i2p_destination = \
|
self.last_published_destination = \
|
||||||
time.time() - 50 * 60 + random.uniform(-1, 1) * 300 # nosec B311
|
time.time() - 50 * 60 + random.uniform(-1, 1) * 300 # nosec B311
|
||||||
|
|
||||||
def fill_bootstrap_pool(self):
|
def fill_bootstrap_pool(self):
|
||||||
|
@ -56,9 +56,10 @@ class Manager(threading.Thread):
|
||||||
if now - self.last_pickled_nodes > 60:
|
if now - self.last_pickled_nodes > 60:
|
||||||
self.pickle_nodes()
|
self.pickle_nodes()
|
||||||
self.last_pickled_nodes = now
|
self.last_pickled_nodes = now
|
||||||
if now - self.last_published_i2p_destination > 3600:
|
if now - self.last_published_destination > 3600:
|
||||||
self.publish_i2p_destination()
|
self.publish_i2p_destination()
|
||||||
self.last_published_i2p_destination = now
|
self.publish_onion_peer()
|
||||||
|
self.last_published_destination = now
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def clean_objects():
|
def clean_objects():
|
||||||
|
@ -321,3 +322,11 @@ class Manager(threading.Thread):
|
||||||
shared.i2p_dest_obj_type, shared.i2p_dest_obj_version,
|
shared.i2p_dest_obj_type, shared.i2p_dest_obj_version,
|
||||||
shared.stream, dest_pub_raw)
|
shared.stream, dest_pub_raw)
|
||||||
proofofwork.do_pow_and_publish(obj)
|
proofofwork.do_pow_and_publish(obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def publish_onion_peer():
|
||||||
|
if shared.onion_hostname:
|
||||||
|
logging.info('Publishing our onion peer')
|
||||||
|
obj = structure.OnionPeer(
|
||||||
|
shared.onion_hostname, shared.listening_port).to_object()
|
||||||
|
proofofwork.do_pow_and_publish(obj)
|
||||||
|
|
|
@ -31,6 +31,7 @@ onion_obj_version = 3
|
||||||
|
|
||||||
socks_proxy = None
|
socks_proxy = None
|
||||||
tor = False
|
tor = False
|
||||||
|
onion_hostname = ''
|
||||||
|
|
||||||
i2p_enabled = False
|
i2p_enabled = False
|
||||||
i2p_transient = False
|
i2p_transient = False
|
||||||
|
|
137
minode/tor.py
Normal file
137
minode/tor.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
"""Tor specific procedures"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
import random
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import stem
|
||||||
|
import stem.control
|
||||||
|
import stem.process
|
||||||
|
import stem.util
|
||||||
|
import stem.version
|
||||||
|
|
||||||
|
from . import shared
|
||||||
|
|
||||||
|
|
||||||
|
def logwrite(line):
|
||||||
|
"""A simple log writing handler for tor messages"""
|
||||||
|
try:
|
||||||
|
level, line = line.split('[', 1)[1].split(']', 1)
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
logging.warning(line)
|
||||||
|
else:
|
||||||
|
if level in ('err', 'warn'):
|
||||||
|
logging.info('(tor)%s', line)
|
||||||
|
|
||||||
|
|
||||||
|
def start_tor_service():
|
||||||
|
"""Start own tor instance and configure a hidden service"""
|
||||||
|
try:
|
||||||
|
socket_dir = os.path.join(shared.data_directory, 'tor')
|
||||||
|
os.makedirs(socket_dir, exist_ok=True)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
socket_dir = tempfile.mkdtemp()
|
||||||
|
except OSError:
|
||||||
|
logging.info('Failed to create a temp dir.')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
present_permissions = os.stat(socket_dir)[0]
|
||||||
|
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
||||||
|
allowed_permissions = ((1 << 32) - 1) ^ disallowed_permissions
|
||||||
|
os.chmod(socket_dir, allowed_permissions & present_permissions)
|
||||||
|
except OSError:
|
||||||
|
logging.debug('Failed to set dir permissions.')
|
||||||
|
return
|
||||||
|
|
||||||
|
stem.util.log.get_logger().setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
control_socket = os.path.abspath(os.path.join(socket_dir, 'tor_control'))
|
||||||
|
tor_config = {
|
||||||
|
'SocksPort': str(shared.socks_proxy.port),
|
||||||
|
'ControlSocket': control_socket}
|
||||||
|
|
||||||
|
for attempt in range(50):
|
||||||
|
if attempt > 0:
|
||||||
|
port = random.randint(32767, 65535) # nosec B311
|
||||||
|
tor_config['SocksPort'] = str(port)
|
||||||
|
try:
|
||||||
|
stem.process.launch_tor_with_config(
|
||||||
|
tor_config, take_ownership=True, timeout=20,
|
||||||
|
init_msg_handler=logwrite)
|
||||||
|
except OSError:
|
||||||
|
if not attempt:
|
||||||
|
try:
|
||||||
|
stem.version.get_system_tor_version()
|
||||||
|
except IOError:
|
||||||
|
return
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logging.info('Started tor on port %s', port)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.debug('Failed to start tor.')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
controller = stem.control.Controller.from_socket_file(control_socket)
|
||||||
|
controller.authenticate()
|
||||||
|
except stem.SocketError:
|
||||||
|
logging.debug('Failed to instantiate or authenticate on controller.')
|
||||||
|
return
|
||||||
|
|
||||||
|
onionkey = onionkeytype = None
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
os.path.join(shared.data_directory, 'onion_dest_priv.key'),
|
||||||
|
'r', encoding='ascii'
|
||||||
|
) as src:
|
||||||
|
onionkey = src.read()
|
||||||
|
logging.debug('Loaded onion service private key.')
|
||||||
|
onionkeytype = 'ED25519-V3'
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
logging.info(
|
||||||
|
'Error while loading onion service private key.', exc_info=True)
|
||||||
|
|
||||||
|
response = controller.create_ephemeral_hidden_service(
|
||||||
|
shared.listening_port, key_type=onionkeytype or 'NEW',
|
||||||
|
key_content=onionkey or 'BEST'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not response.is_ok():
|
||||||
|
logging.info('Bad response from controller ):')
|
||||||
|
return
|
||||||
|
|
||||||
|
shared.onion_hostname = '{}.onion'.format(response.service_id)
|
||||||
|
logging.info('Started hidden service %s', shared.onion_hostname)
|
||||||
|
|
||||||
|
if onionkey:
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
os.path.join(shared.data_directory, 'onion_dest_priv.key'),
|
||||||
|
'w', encoding='ascii'
|
||||||
|
) as src:
|
||||||
|
src.write(response.private_key)
|
||||||
|
logging.debug('Saved onion service private key.')
|
||||||
|
except Exception:
|
||||||
|
logging.warning(
|
||||||
|
'Error while saving onion service private key.', exc_info=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
os.path.join(shared.data_directory, 'onion_dest.pub'),
|
||||||
|
'w', encoding='ascii'
|
||||||
|
) as src:
|
||||||
|
src.write(response.service_id)
|
||||||
|
logging.debug('Saved onion service public key.')
|
||||||
|
except Exception:
|
||||||
|
logging.warning(
|
||||||
|
'Error while saving onion service public key.', exc_info=True)
|
||||||
|
|
||||||
|
return True
|
Loading…
Reference in New Issue
Block a user