diff --git a/minode/connection.py b/minode/connection.py index b7f9a75..95c4e2c 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: @@ -507,4 +510,44 @@ class Connection(ConnectionBase): self.vectors_to_send.update(getdata.vectors) +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 7b52796..fa1faf9 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 known nodes""" diff --git a/minode/manager.py b/minode/manager.py index 93a3f30..1816384 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 Connection +from .connection import Connection, SocksConnection from .i2p import I2PDialer @@ -141,7 +141,8 @@ class Manager(threading.Thread): else: continue else: - c = Connection(addr[0], addr[1]) + c = (Connection if not shared.socks_proxy + else SocksConnection)(addr[0], addr[1]) c.start() hosts.add(c.host) with shared.connections_lock: @@ -189,7 +190,7 @@ class Manager(threading.Thread): 'r', newline='', encoding='ascii' ) as src: reader = csv.reader(src) - shared.core_nodes = {tuple(row) for row in reader} + shared.core_nodes = {(row[0], int(row[1])) for row in reader} shared.node_pool.update(shared.core_nodes) with open( diff --git a/minode/shared.py b/minode/shared.py index d49786c..301bee2 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'