From 84927734389251ac74e6ea084010326e7f679bd5 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Aug 2023 18:28:19 -0500 Subject: [PATCH] WIP very messy need to remove SocksSocket as it apparently doesn't work, whereas SocksProxy does --- lib/electrumx_rpc/rpc.dart | 244 ++++++++++++++++++++++++++----------- lib/networking/socks5.dart | 1 + 2 files changed, 172 insertions(+), 73 deletions(-) diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index ecbdda49d..94038e8d3 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -38,6 +38,7 @@ class JsonRPC { final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue(); Socket? _socket; SOCKSSocket? _socksSocket; + SOCKS5Proxy? _socksProxy; StreamSubscription? _subscription; void _dataHandler(List data) { @@ -85,8 +86,17 @@ class JsonRPC { _socket!.write('${req.jsonRequest}\r\n'); } if (_socksSocket != null) { + print('writing to _socksSocket: ${req.jsonRequest}'); + print(req.jsonRequest); + print(req); _socksSocket!.write('${req.jsonRequest}\r\n'); } + if (_socksProxy != null) { + print('writing to _socksProxy: ${req.jsonRequest}'); + print(req.jsonRequest); + print(req); + _socksProxy!.write('${req.jsonRequest}\r\n'); + } // TODO different timeout length? req.initiateTimeout( @@ -109,12 +119,19 @@ class JsonRPC { "JsonRPC request: opening socket $host:$port", level: LogLevel.Info, ); - await connect(); + // await connect(); } } else { - if (_socksSocket == null) { + // if (_socksSocket == null) { + // Logging.instance.log( + // "JsonRPC request: opening SOCKS socket to $host:$port", + // level: LogLevel.Info, + // ); + // await connect(); + // } + if (_socksProxy == null) { Logging.instance.log( - "JsonRPC request: opening SOCKS socket to $host:$port", + "JsonRPC request: opening SOCKS proxy to $host:$port", level: LogLevel.Info, ); await connect(); @@ -185,82 +202,163 @@ class JsonRPC { timeout: connectionTimeout, ); } + + _subscription = _socket!.listen( + _dataHandler, + onError: _errorHandler, + onDone: _doneHandler, + cancelOnError: true, + ); } else { - if (proxyInfo == null) { - // TODO await tor / make sure it's running - proxyInfo = ( - host: InternetAddress.loopbackIPv4.address, - port: TorService.sharedInstance.port + if (_socksProxy == null) { + print(1111111); + _socksProxy = SOCKS5Proxy(); + // TODO check if null + await _socksProxy!.connect(); + print(222222); + // TODO check if null + await _socksProxy!.connectTo('bitcoincash.stackwallet.com', 50002); + + print(333333); + + // TODO check if null + _subscription = _socksProxy!.socket.listen( + _dataHandler, + onError: _errorHandler, + onDone: _doneHandler, + cancelOnError: true, ); - Logging.instance.log( - "ElectrumX.connect(): no tor proxy info, read $proxyInfo", - level: LogLevel.Warning); - } - // TODO connect to proxy socket... - - // TODO implement ssl over tor - // if (useSSL) { - // _socket = await SecureSocket.connect( - // host, - // port, - // timeout: connectionTimeout, - // onBadCertificate: (_) => true, - // ); // TODO do not automatically trust bad certificates - // final _client = SocksSocket.protected(_socket, type); - // } else { - final sock = await RawSocket.connect( - InternetAddress.loopbackIPv4, proxyInfo!.port); - - if (_socksSocket == null) { - Logging.instance.log( - "JsonRPC.connect(): creating SOCKS socket at $proxyInfo", - level: LogLevel.Info); - _socksSocket = SOCKSSocket(sock); - if (_socksSocket == null) { - Logging.instance.log( - "JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo", - level: LogLevel.Error); - throw Exception( - "JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo"); - } else { - Logging.instance.log( - "JsonRPC.connect(): created SOCKS socket at $proxyInfo", - level: LogLevel.Info); - } } else { - // TODO also check if sock == previous sock, eg. if RawSocket is different - Logging.instance.log( - "JsonRPC.connect(): using pre-existing SOCKS socket at $proxyInfo", - level: LogLevel.Info); - } - - try { - Logging.instance.log( - "JsonRPC.connect(): connecting to $host:$port over SOCKS socket at $proxyInfo...", - level: LogLevel.Info); - if (!isIpAddress(host)) { - await _socksSocket!.connect("$host:$port"); - } else { - await _socksSocket!.connectIp(InternetAddress(host), port); - } - Logging.instance.log( - "JsonRPC.connect(): connected to $host:$port over SOCKS socket at $proxyInfo", - level: LogLevel.Info); - } catch (e) { - Logging.instance.log( - "JsonRPC.connect(): failed to connect to $host over tor proxy at $proxyInfo, $e", - level: LogLevel.Error); - throw Exception( - "JsonRPC.connect(): failed to connect to tor proxy, $e"); + print('0000000'); } + // if (proxyInfo == null) { + // // TODO await tor / make sure it's running + // proxyInfo = ( + // host: InternetAddress.loopbackIPv4.address, + // port: TorService.sharedInstance.port + // ); + // Logging.instance.log( + // "ElectrumX.connect(): no tor proxy info, read $proxyInfo", + // level: LogLevel.Warning); + // } + // // TODO connect to proxy socket... + // + // // TODO implement ssl over tor + // // if (useSSL) { + // // _socket = await SecureSocket.connect( + // // host, + // // port, + // // timeout: connectionTimeout, + // // onBadCertificate: (_) => true, + // // ); // TODO do not automatically trust bad certificates + // // final _client = SocksSocket.protected(_socket, type); + // // } else { + // final sock = await RawSocket.connect( + // InternetAddress.loopbackIPv4, proxyInfo!.port); + // + // if (_socksSocket == null) { + // Logging.instance.log( + // "JsonRPC.connect(): creating SOCKS socket at $proxyInfo", + // level: LogLevel.Info); + // _socksSocket = SOCKSSocket(sock); + // if (_socksSocket == null) { + // Logging.instance.log( + // "JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo", + // level: LogLevel.Error); + // throw Exception( + // "JsonRPC.connect(): failed to create SOCKS socket at $proxyInfo"); + // } else { + // Logging.instance.log( + // "JsonRPC.connect(): created SOCKS socket at $proxyInfo", + // level: LogLevel.Info); + // } + // } else { + // // TODO also check if sock == previous sock, eg. if RawSocket is different + // Logging.instance.log( + // "JsonRPC.connect(): using pre-existing SOCKS socket at $proxyInfo", + // level: LogLevel.Info); + // } + // + // try { + // Logging.instance.log( + // "JsonRPC.connect(): connecting to $host:$port over SOCKS socket at $proxyInfo...", + // level: LogLevel.Info); + // if (!isIpAddress(host)) { + // await _socksSocket!.connect("$host:$port"); + // } else { + // await _socksSocket!.connectIp(InternetAddress(host), port); + // } + // Logging.instance.log( + // "JsonRPC.connect(): connected to $host:$port over SOCKS socket at $proxyInfo", + // level: LogLevel.Info); + // } catch (e) { + // Logging.instance.log( + // "JsonRPC.connect(): failed to connect to $host over tor proxy at $proxyInfo, $e", + // level: LogLevel.Error); + // throw Exception( + // "JsonRPC.connect(): failed to connect to tor proxy, $e"); + // } } + } +} - _subscription = _socket!.listen( - _dataHandler, - onError: _errorHandler, - onDone: _doneHandler, - cancelOnError: true, - ); +class SOCKS5Proxy { + final String host; + final int port; + + late Socket _socks5Socket; + Socket get socket => _socks5Socket; + + SOCKS5Proxy({String? host, int? port}) + : host = host ?? InternetAddress.loopbackIPv4.address, + port = port ?? TorService.sharedInstance.port; + + Future connect() async { + _socks5Socket = await Socket.connect(host, port); + + // Greeting and method selection + _socks5Socket.add([0x05, 0x01, 0x00]); + + // Wait for server response + var response = await _socks5Socket.first; + if (response[1] != 0x00) { + throw Exception('Failed to connect to SOCKS5 proxy.'); + } + } + + // This is just a basic example for domain-based addresses. + Future connectTo(String domain, int port) async { + // Command, Reserved, Address Type, Address, Port + var request = [ + 0x05, + 0x01, + 0x00, + 0x03, + domain.length, + ...domain.codeUnits, + (port >> 8) & 0xFF, + port & 0xFF + ]; + + _socks5Socket.add(request); + print(444444); + + // Wait for server response + // var response = await _socks5Socket.first; + // if (response[1] != 0x00) { + // throw Exception('Failed to connect to target through SOCKS5 proxy.'); + // } + // print(response); + print(55555); + } + + /// Converts [object] to a String by invoking [Object.toString] and + /// sends the encoding of the result to the socket. + void write(Object? object) { + if (object == null) return; + + List data = utf8.encode(object.toString()); + _socks5Socket.add(data); } } diff --git a/lib/networking/socks5.dart b/lib/networking/socks5.dart index 18feb9b18..619685381 100644 --- a/lib/networking/socks5.dart +++ b/lib/networking/socks5.dart @@ -275,6 +275,7 @@ class SOCKSSocket { break; case RawSocketEvent.write: // TODO: Handle this case. + Logging.instance.log( "SOCKSSocket._start(): unhandled event RawSocketEvent.write", level: LogLevel.Warning);