Autoformat

This commit is contained in:
hhh
2024-05-27 17:12:21 +03:00
parent 0afca0dd67
commit 918d5af851
25 changed files with 358 additions and 378 deletions

View File

@@ -1,7 +1,3 @@
from .onion import Onion
from .onion import get_available_port
from .onion import Onion, get_available_port
__all__ = [
'Onion',
'get_available_port'
]
__all__ = ["Onion", "get_available_port"]

View File

@@ -1,23 +1,23 @@
from stem.control import Controller
from .stem_process import launch_tor_with_config
from stem import ProtocolError
import socket
import random
import base64
import os
import psutil
import platform
import random
import socket
import subprocess
import tempfile
import platform
import time
import base64
import nacl.public
import psutil
from rich import print
from stem import ProtocolError
from stem.control import Controller
from dragonion_server.utils.core import dirs
from dragonion_server.utils import config
from dragonion_server.utils.config.db import services
from dragonion_server.utils.core import dirs
from .stem_process import launch_tor_with_config
def get_available_port(min_port: int = 1000, max_port: int = 65535):
@@ -61,9 +61,9 @@ class Onion(object):
try:
cmdline = proc.cmdline()
if (
cmdline[0] == self.tor_path
and cmdline[1] == "-f"
and cmdline[2] == self.tor_torrc
cmdline[0] == self.tor_path
and cmdline[1] == "-f"
and cmdline[2] == self.tor_torrc
):
proc.terminate()
proc.wait()
@@ -82,18 +82,18 @@ class Onion(object):
self.kill_same_tor()
tor_config = {
'DataDirectory': tor_data_directory_name,
'SocksPort': str(self.tor_socks_port),
'CookieAuthentication': '1',
'CookieAuthFile': self.tor_cookie_auth_file,
'AvoidDiskWrites': '1',
'Log': [
'NOTICE stdout'
]
"DataDirectory": tor_data_directory_name,
"SocksPort": str(self.tor_socks_port),
"CookieAuthentication": "1",
"CookieAuthFile": self.tor_cookie_auth_file,
"AvoidDiskWrites": "1",
"Log": ["NOTICE stdout"],
}
if platform.system() in ["Windows", "Darwin"] or \
len(tor_data_directory_name) > 90:
if (
platform.system() in ["Windows", "Darwin"]
or len(tor_data_directory_name) > 90
):
try:
self.tor_control_port = get_available_port(1000, 65535)
tor_config = tor_config | {"ControlPort": str(self.tor_control_port)}
@@ -102,24 +102,22 @@ class Onion(object):
self.tor_control_socket = None
else:
self.tor_control_port = None
self.tor_control_socket = os.path.abspath(os.path.join(
tor_data_directory_name, "control_socket"
))
self.tor_control_socket = os.path.abspath(
os.path.join(tor_data_directory_name, "control_socket")
)
tor_config = tor_config | {"ControlSocket": str(self.tor_control_socket)}
return tor_config
def connect(self):
self.tor_data_directory = tempfile.TemporaryDirectory(
dir=dirs.build_tmp_dir()
)
self.tor_data_directory = tempfile.TemporaryDirectory(dir=dirs.build_tmp_dir())
self.tor_data_directory_name = self.tor_data_directory.name
self.tor_proc = launch_tor_with_config(
config=self.get_config(self.tor_data_directory_name),
tor_cmd=self.tor_path,
take_ownership=True,
init_msg_handler=print
init_msg_handler=print,
)
time.sleep(2)
@@ -149,14 +147,12 @@ class Onion(object):
client_auth_priv_key_raw = nacl.public.PrivateKey.generate()
client_auth_priv_key = key_str(client_auth_priv_key_raw)
client_auth_pub_key = key_str(
client_auth_priv_key_raw.public_key
)
client_auth_pub_key = key_str(client_auth_priv_key_raw.public_key)
services[name] = config.models.ServiceModel(
port=port,
client_auth_priv_key=client_auth_priv_key,
client_auth_pub_key=client_auth_pub_key
client_auth_pub_key=client_auth_pub_key,
)
return services[name]
@@ -167,7 +163,7 @@ class Onion(object):
:return: .onion url
"""
if name not in services.keys():
raise 'Service not created'
raise "Service not created"
service: config.models.ServiceModel = services[name]
@@ -193,8 +189,9 @@ class Onion(object):
service.key_type = "ED25519-V3"
service.key_content = res.private_key
self.auth_string = f'{res.service_id}:descriptor:' \
f'x25519:{service.client_auth_priv_key}'
self.auth_string = (
f"{res.service_id}:descriptor:" f"x25519:{service.client_auth_priv_key}"
)
services[name] = service
@@ -204,9 +201,7 @@ class Onion(object):
service: config.models.ServiceModel = services[name]
if service.service_id:
try:
self.c.remove_ephemeral_hidden_service(
service.service_id
)
self.c.remove_ephemeral_hidden_service(service.service_id)
except Exception as e:
print(e)
@@ -222,8 +217,8 @@ class Onion(object):
rendezvous_circuit_ids = []
for c in self.c.get_circuits():
if (
c.purpose == "HS_SERVICE_REND"
and c.rend_query in self.graceful_close_onions
c.purpose == "HS_SERVICE_REND"
and c.rend_query in self.graceful_close_onions
):
rendezvous_circuit_ids.append(c.id)
@@ -237,9 +232,7 @@ class Onion(object):
num_rend_circuits += 1
if num_rend_circuits == 0:
print(
"\rTor rendezvous circuits have closed" + " " * 20
)
print("\rTor rendezvous circuits have closed" + " " * 20)
break
if num_rend_circuits == 1:
@@ -271,7 +264,7 @@ class Onion(object):
try:
self.tor_data_directory.cleanup()
except Exception as e:
print(f'Cannot cleanup temporary directory: {e}')
print(f"Cannot cleanup temporary directory: {e}")
@property
def get_tor_socks_port(self):

View File

@@ -15,13 +15,21 @@ import stem.util.str_tools
import stem.util.system
import stem.version
NO_TORRC = '<no torrc>'
NO_TORRC = "<no torrc>"
DEFAULT_INIT_TIMEOUT = 90
def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100,
init_msg_handler=None, timeout=DEFAULT_INIT_TIMEOUT,
take_ownership=False, close_output=True, stdin=None):
def launch_tor(
tor_cmd="tor",
args=None,
torrc_path=None,
completion_percent=100,
init_msg_handler=None,
timeout=DEFAULT_INIT_TIMEOUT,
take_ownership=False,
close_output=True,
stdin=None,
):
"""
Initializes a tor process. This blocks until initialization completes or we
error out.
@@ -68,13 +76,14 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
if stem.util.system.is_windows():
if timeout is not None and timeout != DEFAULT_INIT_TIMEOUT:
raise OSError('You cannot launch tor with a timeout on Windows')
raise OSError("You cannot launch tor with a timeout on Windows")
timeout = None
elif threading.current_thread().__class__.__name__ != '_MainThread':
elif threading.current_thread().__class__.__name__ != "_MainThread":
if timeout is not None and timeout != DEFAULT_INIT_TIMEOUT:
raise OSError(
'Launching tor with a timeout can only be done in the main thread')
"Launching tor with a timeout can only be done in the main thread"
)
timeout = None
@@ -88,8 +97,10 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
elif not os.path.isfile(tor_cmd):
raise OSError("'%s' doesn't exist" % tor_cmd)
elif not stem.util.system.is_available(tor_cmd):
raise OSError(f"{tor_cmd} isn't available on your system. "
f"Maybe it's not in your PATH?")
raise OSError(
f"{tor_cmd} isn't available on your system. "
f"Maybe it's not in your PATH?"
)
# double check that we have a torrc to work with
if torrc_path not in (None, NO_TORRC) and not os.path.exists(torrc_path):
@@ -103,13 +114,13 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
if torrc_path:
if torrc_path == NO_TORRC:
temp_file = tempfile.mkstemp(prefix='empty-torrc-', text=True)[1]
runtime_args += ['-f', temp_file]
temp_file = tempfile.mkstemp(prefix="empty-torrc-", text=True)[1]
runtime_args += ["-f", temp_file]
else:
runtime_args += ['-f', torrc_path]
runtime_args += ["-f", torrc_path]
if take_ownership:
runtime_args += ['__OwningControllerProcess', str(os.getpid())]
runtime_args += ["__OwningControllerProcess", str(os.getpid())]
tor_process = None
@@ -119,8 +130,9 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
env=None if platform.system() == 'Windows' else
{"LD_LIBRARY_PATH": os.path.dirname(tor_cmd)}
env=None
if platform.system() == "Windows"
else {"LD_LIBRARY_PATH": os.path.dirname(tor_cmd)},
)
if stdin:
@@ -129,15 +141,16 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
tor_process.stdin.close()
if timeout:
def timeout_handler(*_):
raise OSError('reached a %i second timeout without success' % timeout)
raise OSError("reached a %i second timeout without success" % timeout)
signal.signal(signal.SIGALRM, timeout_handler)
signal.setitimer(signal.ITIMER_REAL, timeout)
bootstrap_line = re.compile('Bootstrapped ([0-9]+)%')
problem_line = re.compile('\\[(warn|err)] (.*)$')
last_problem = 'Timed out'
bootstrap_line = re.compile("Bootstrapped ([0-9]+)%")
problem_line = re.compile("\\[(warn|err)] (.*)$")
last_problem = "Timed out"
while True:
# Tor's stdout will be read as ASCII bytes. This is fine for python 2, but
@@ -147,12 +160,12 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
# It seems like python 2.x is perfectly happy for this to be unicode, so
# normalizing to that.
init_line = tor_process.stdout.readline().decode('utf-8', 'replace').strip()
init_line = tor_process.stdout.readline().decode("utf-8", "replace").strip()
# this will provide empty results if the process is terminated
if not init_line:
raise OSError('Process terminated: %s' % last_problem)
raise OSError("Process terminated: %s" % last_problem)
# provide the caller with the initialization message if they want it
@@ -169,9 +182,9 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
elif problem_match:
runlevel, msg = problem_match.groups()
if 'see warnings above' not in msg:
if ': ' in msg:
msg = msg.split(': ')[-1].strip()
if "see warnings above" not in msg:
if ": " in msg:
msg = msg.split(": ")[-1].strip()
last_problem = msg
except Exception as e:
@@ -199,9 +212,15 @@ def launch_tor(tor_cmd='tor', args=None, torrc_path=None, completion_percent=100
assert e
def launch_tor_with_config(config, tor_cmd='tor', completion_percent=100,
init_msg_handler=None, timeout=DEFAULT_INIT_TIMEOUT,
take_ownership=False, close_output=True):
def launch_tor_with_config(
config,
tor_cmd="tor",
completion_percent=100,
init_msg_handler=None,
timeout=DEFAULT_INIT_TIMEOUT,
take_ownership=False,
close_output=True,
):
"""
Initializes a tor process, like :func:`~stem.process.launch_tor`, but with a
customized configuration. This writes a temporary torrc to disk, launches
@@ -246,62 +265,71 @@ def launch_tor_with_config(config, tor_cmd='tor', completion_percent=100,
"""
try:
use_stdin = stem.version.get_system_tor_version(
tor_cmd) >= stem.version.Requirement.TORRC_VIA_STDIN
use_stdin = (
stem.version.get_system_tor_version(tor_cmd)
>= stem.version.Requirement.TORRC_VIA_STDIN
)
except IOError:
use_stdin = False
# we need to be sure that we're logging to stdout to figure out when we're
# done bootstrapping
if 'Log' in config:
stdout_options = ['DEBUG stdout', 'INFO stdout', 'NOTICE stdout']
if "Log" in config:
stdout_options = ["DEBUG stdout", "INFO stdout", "NOTICE stdout"]
if isinstance(config['Log'], str):
config['Log'] = [config['Log']]
if isinstance(config["Log"], str):
config["Log"] = [config["Log"]]
has_stdout = False
for log_config in config['Log']:
for log_config in config["Log"]:
if log_config in stdout_options:
has_stdout = True
break
if not has_stdout:
config['Log'].append('NOTICE stdout')
config["Log"].append("NOTICE stdout")
config_str = ''
config_str = ""
for key, values in list(config.items()):
if isinstance(values, str):
config_str += '%s %s\n' % (key, values)
config_str += "%s %s\n" % (key, values)
else:
for value in values:
config_str += '%s %s\n' % (key, value)
config_str += "%s %s\n" % (key, value)
if use_stdin:
return launch_tor(
tor_cmd=tor_cmd,
args=['-f', '-'],
args=["-f", "-"],
completion_percent=completion_percent,
init_msg_handler=init_msg_handler,
timeout=timeout,
take_ownership=take_ownership,
close_output=close_output,
stdin=config_str
stdin=config_str,
)
else:
torrc_descriptor, torrc_path = tempfile.mkstemp(prefix='torrc-', text=True)
torrc_descriptor, torrc_path = tempfile.mkstemp(prefix="torrc-", text=True)
try:
with open(torrc_path, 'w') as torrc_file:
with open(torrc_path, "w") as torrc_file:
torrc_file.write(config_str)
# prevents tor from error-ing out due to a missing torrc if it gets a sighup
args = ['__ReloadTorrcOnSIGHUP', '0']
args = ["__ReloadTorrcOnSIGHUP", "0"]
return launch_tor(tor_cmd, args, torrc_path, completion_percent,
init_msg_handler, timeout, take_ownership)
return launch_tor(
tor_cmd,
args,
torrc_path,
completion_percent,
init_msg_handler,
timeout,
take_ownership,
)
finally:
try:
os.close(torrc_descriptor)

View File

@@ -1,51 +1,51 @@
import os
import io
import tarfile
import requests
import os
import re
import sys
import tarfile
from typing import Literal
import requests
def get_latest_version() -> str:
"""
Gets latest non-alfa version name from dist.torproject.org
:return:
"""
r = requests.get('https://dist.torproject.org/torbrowser/').text
r = requests.get("https://dist.torproject.org/torbrowser/").text
results = re.findall(r'<a href=".+/">(.+)/</a>', r)
for res in results:
if 'a' not in res:
if "a" not in res:
return res
def get_build() -> Literal[
'windows-x86_64',
'linux-x86_64',
'macos-x86_64',
'macos-aarch64'
"windows-x86_64", "linux-x86_64", "macos-x86_64", "macos-aarch64"
]:
"""
Gets proper build name for your system
:return:
"""
if sys.platform == 'win32':
return 'windows-x86_64'
elif sys.platform == 'linux':
return 'linux-x86_64'
elif sys.platform == 'darwin':
if sys.platform == "win32":
return "windows-x86_64"
elif sys.platform == "linux":
return "linux-x86_64"
elif sys.platform == "darwin":
import platform
if platform.uname().machine == 'arm64':
return 'macos-aarch64'
if platform.uname().machine == "arm64":
return "macos-aarch64"
else:
return 'macos-x86_64'
return "macos-x86_64"
else:
raise 'System not supported'
raise "System not supported"
def get_tor_expert_bundles(version: str = get_latest_version(),
platform: str = get_build()):
def get_tor_expert_bundles(
version: str = get_latest_version(), platform: str = get_build()
):
"""
Returns a link for downloading tor expert bundle by version and platform
:param version: Tor expert bundle version that exists in dist.torproject.org
@@ -53,11 +53,13 @@ def get_tor_expert_bundles(version: str = get_latest_version(),
get_build()
:return:
"""
return f'https://dist.torproject.org/torbrowser/{version}/tor-expert-bundle-' \
f'{version}-{platform}.tar.gz'
return (
f"https://dist.torproject.org/torbrowser/{version}/tor-expert-bundle-"
f"{version}-{platform}.tar.gz"
)
def download_tor(url: str = get_tor_expert_bundles(), dist: str = 'tor'):
def download_tor(url: str = get_tor_expert_bundles(), dist: str = "tor"):
"""
Downloads tor from url and unpacks it to specified directory. Note, that
it doesn't unpack only tor executable to dist folder, but creates there
@@ -69,15 +71,15 @@ def download_tor(url: str = get_tor_expert_bundles(), dist: str = 'tor'):
if not os.path.exists(dist):
os.makedirs(dist)
(tar := tarfile.open(fileobj=io.BytesIO(requests.get(url).content),
mode='r:gz')).extractall(
(
tar := tarfile.open(fileobj=io.BytesIO(requests.get(url).content), mode="r:gz")
).extractall(
members=[
tarinfo
for tarinfo
in tar.getmembers()
if tarinfo.name.startswith("tor/")
], path=dist)
tarinfo for tarinfo in tar.getmembers() if tarinfo.name.startswith("tor/")
],
path=dist,
)
if __name__ == '__main__':
if __name__ == "__main__":
download_tor()