From a13c1c0b1b253e1748e1ae6a66f652e4533b5cfa Mon Sep 17 00:00:00 2001 From: Lee Miller Date: Tue, 28 Mar 2023 07:01:29 +0300 Subject: [PATCH] A minimal implementation of proxy for outgoing connections using PySocks, special arg --tor currently just sets host and port for the socks_proxy. --- minode/connection.py | 45 +++++++++++++++++++++++++++++++++++++++++++- minode/main.py | 26 ++++++++++++++++++++++++- minode/manager.py | 6 ++++-- minode/shared.py | 3 +++ 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/minode/connection.py b/minode/connection.py index 05d552a..0d0415b 100644 --- a/minode/connection.py +++ b/minode/connection.py @@ -5,6 +5,7 @@ import errno import logging import math import random +import re import select import socket import ssl @@ -78,7 +79,9 @@ class ConnectionBase(threading.Thread): self.s.settimeout(0) if not self.server: if self.network == 'ip': - self.send_queue.put(message.Version(self.host, self.port)) + self.send_queue.put(message.Version( + ('127.0.0.1' if shared.socks_proxy else self.host), + self.port)) else: self.send_queue.put(message.Version('127.0.0.1', 7656)) while True: @@ -517,4 +520,44 @@ class Bootstrapper(ConnectionBase): self.send_queue.put(None) +class SocksConnection(Connection): + """The socks proxied connection""" + def _connect(self): + peer_str = '{0.host_print}:{0.port}'.format(self) + logging.debug('Connecting to %s', peer_str) + + import socks + + try: + self.s = socks.create_connection( + (self.host, self.port), 30, None, socks.PROXY_TYPE_SOCKS5, + shared.socks_proxy[0], shared.socks_proxy[1], True, + None, None, None) + self.status = 'connected' + logging.debug('Established SOCKS connection to %s', peer_str) + except socket.timeout: + pass + except socks.GeneralProxyError as e: + e = e.socket_err + if isinstance(e, socket.timeout) or ( + # general failure, unreachable, refused + not e.errno and re.match(r'^0x0[1,4,5].*', e.msg) + ): + logcall = logging.debug + else: + logcall = logging.info + logcall('Connection to %s failed. Reason: %s', peer_str, e) + except OSError as e: + # unreachable, refused, no route + (logging.info if e.errno not in (0, 101, 111, 113) + else logging.debug)( + 'Connection to %s failed. Reason: %s', peer_str, e) + except Exception: + logging.info( + 'Connection to %s failed.', peer_str, exc_info=True) + + if self.status != 'connected': + self.status = 'failed' + + shared.connection = Connection diff --git a/minode/main.py b/minode/main.py index 72cebe0..2fabfd5 100644 --- a/minode/main.py +++ b/minode/main.py @@ -8,6 +8,11 @@ import os import signal import socket +try: + import socks +except ImportError: + socks = None + from . import i2p, shared from .advertiser import Advertiser from .manager import Manager @@ -52,6 +57,14 @@ def parse_arguments(): # pylint: disable=too-many-branches,too-many-statements '--i2p-transient', action='store_true', help='Generate new I2P destination on start') + if socks is not None: + parser.add_argument( + '--socks-proxy', + help='SOCKS proxy address in the form :') + parser.add_argument( + '--tor', action='store_true', + help='The SOCKS proxy is tor, use 127.0.0.1:9050 if not specified') + args = parser.parse_args() if args.port: shared.listening_port = args.port @@ -71,7 +84,8 @@ def parse_arguments(): # pylint: disable=too-many-branches,too-many-statements if args.no_ip: shared.ip_enabled = False if args.trusted_peer: - if len(args.trusted_peer) > 50: + if len(args.trusted_peer + ) > 50 and not args.trusted_peer.endswith('onion'): # I2P shared.trusted_peer = (args.trusted_peer.encode(), 'i2p') else: @@ -99,6 +113,16 @@ def parse_arguments(): # pylint: disable=too-many-branches,too-many-statements if args.i2p_transient: shared.i2p_transient = True + if socks is None: + return + if args.tor: + shared.tor = True + if not args.socks_proxy: + shared.socks_proxy = ('127.0.0.1', 9050) + if args.socks_proxy: + addr = args.socks_proxy.split(':') + shared.socks_proxy = (addr[0], int(addr[1])) + def bootstrap_from_dns(): """Addes addresses of bootstrap servers to core nodes""" diff --git a/minode/manager.py b/minode/manager.py index 71f5873..1e600ba 100644 --- a/minode/manager.py +++ b/minode/manager.py @@ -11,7 +11,7 @@ import threading import time from . import proofofwork, shared, structure -from .connection import Bootstrapper, Connection +from .connection import Bootstrapper, Connection, SocksConnection from .i2p import I2PDialer @@ -174,7 +174,9 @@ class Manager(threading.Thread): else: continue else: - connect((host, port)) + connect( + (host, port), + Connection if not shared.socks_proxy else SocksConnection) hosts.add(group) shared.hosts = hosts diff --git a/minode/shared.py b/minode/shared.py index 72864ec..f6fd81e 100644 --- a/minode/shared.py +++ b/minode/shared.py @@ -27,6 +27,9 @@ header_length = 24 i2p_dest_obj_type = 0x493250 i2p_dest_obj_version = 1 +socks_proxy = None +tor = False + i2p_enabled = False i2p_transient = False i2p_sam_host = '127.0.0.1'