From 9e7567a26a7af7c61f781aeb2bcf0499e6e0fd4a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Aug 2023 12:13:26 -0500 Subject: [PATCH 1/3] add required proxyInfo param to JsonRPC class --- lib/electrumx_rpc/rpc.dart | 83 +++++++++++++++++++++++++++++++++----- test/json_rpc_test.dart | 5 ++- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index 2dc0d3a71..8c44e6dcd 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -14,7 +14,9 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:mutex/mutex.dart'; +import 'package:stackwallet/networking/tor_service.dart'; import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/prefs.dart'; // Json RPC class to handle connecting to electrumx servers class JsonRPC { @@ -23,15 +25,17 @@ class JsonRPC { required this.port, this.useSSL = false, this.connectionTimeout = const Duration(seconds: 60), + required ({String host, int port})? proxyInfo, }); final bool useSSL; final String host; final int port; final Duration connectionTimeout; + ({String host, int port})? proxyInfo; final _requestMutex = Mutex(); final _JsonRPCRequestQueue _requestQueue = _JsonRPCRequestQueue(); - Socket? _socket; + Socket? _socket; // TODO make a SocksSocket extension/wrapper or similar StreamSubscription? _subscription; void _dataHandler(List data) { @@ -152,19 +156,76 @@ class JsonRPC { ); } - if (useSSL) { - _socket = await SecureSocket.connect( - host, - port, - timeout: connectionTimeout, - onBadCertificate: (_) => true, - ); // TODO do not automatically trust bad certificates - } else { + if (Prefs.instance.useTor) { + 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... + // https://github.com/LacticWhale/socks_dart/blob/master/lib/src/client/socks_client.dart#L50C46-L50C56 + + // 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 { _socket = await Socket.connect( - host, - port, + proxyInfo!.host, + proxyInfo!.port, timeout: connectionTimeout, ); + // final _client = SocksSocket.protected( + // _socket!, SocksConnectionType.connect + // ); + // final InternetAddress _host = + // await InternetAddress.lookup(host).then((value) => value.first); + // var _socket = await SocksSocket.initialize( + // [ + // ProxySettings( + // InternetAddress.loopbackIPv4, + // proxyInfo!.port, + // ) + // ], + // _host, + // port, + // SocksConnectionType.connect, + // ); + if (_socket == null) { + Logging.instance.log( + "JsonRPC.connect(): failed to connect to $host over tor proxy at $proxyInfo", + level: LogLevel.Error); + throw Exception("JsonRPC.connect(): failed to connect to tor proxy"); + } else { + Logging.instance.log( + "JsonRPC.connect(): connected to $host over tor proxy at $proxyInfo", + level: LogLevel.Info); + } + } else { + if (useSSL) { + _socket = await SecureSocket.connect( + host, + port, + timeout: connectionTimeout, + onBadCertificate: (_) => true, + ); // TODO do not automatically trust bad certificates + } else { + _socket = await Socket.connect( + host, + port, + timeout: connectionTimeout, + ); + } } _subscription = _socket!.listen( diff --git a/test/json_rpc_test.dart b/test/json_rpc_test.dart index 333c1bde6..b5df1d52f 100644 --- a/test/json_rpc_test.dart +++ b/test/json_rpc_test.dart @@ -11,6 +11,7 @@ void main() { port: DefaultNodes.bitcoin.port, useSSL: true, connectionTimeout: const Duration(seconds: 40), + proxyInfo: null, // TODO test for proxyInfo ); const jsonRequestString = @@ -27,7 +28,8 @@ void main() { final jsonRPC = JsonRPC( host: "some.bad.address.thingdsfsdfsdaf", port: 3000, - connectionTimeout: Duration(seconds: 10), + connectionTimeout: const Duration(seconds: 10), + proxyInfo: null, ); const jsonRequestString = @@ -47,6 +49,7 @@ void main() { port: 3000, useSSL: false, connectionTimeout: const Duration(seconds: 1), + proxyInfo: null, ); const jsonRequestString = From cfce22fa73eca2d625d38ea59e3b8cbfbfd0c765 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Aug 2023 12:14:24 -0500 Subject: [PATCH 2/3] v0l/socks5/master/lib/socks5.dart https://raw.githubusercontent.com/v0l/socks5/master/lib/socks5.dart --- lib/networking/socks5.dart | 365 +++++++++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 lib/networking/socks5.dart diff --git a/lib/networking/socks5.dart b/lib/networking/socks5.dart new file mode 100644 index 000000000..df4ecee1e --- /dev/null +++ b/lib/networking/socks5.dart @@ -0,0 +1,365 @@ +library socks; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +/// https://tools.ietf.org/html/rfc1928 +/// https://tools.ietf.org/html/rfc1929 +/// + +const SOCKSVersion = 0x05; +const RFC1929Version = 0x01; + +class AuthMethods { + static const NoAuth = const AuthMethods._(0x00); + static const GSSApi = const AuthMethods._(0x01); + static const UsernamePassword = const AuthMethods._(0x02); + static const NoAcceptableMethods = const AuthMethods._(0xFF); + + final int _value; + + const AuthMethods._(this._value); + + String toString() { + return const { + 0x00: 'AuthMethods.NoAuth', + 0x01: 'AuthMethods.GSSApi', + 0x02: 'AuthMethods.UsernamePassword', + 0xFF: 'AuthMethods.NoAcceptableMethods' + }[_value]; + } +} + +class SOCKSState { + static const Starting = const SOCKSState._(0x00); + static const Auth = const SOCKSState._(0x01); + static const RequestReady = const SOCKSState._(0x02); + static const Connected = const SOCKSState._(0x03); + static const AuthStarted = const SOCKSState._(0x04); + + final int _value; + + const SOCKSState._(this._value); + + String toString() { + return const [ + 'SOCKSState.Starting', + 'SOCKSState.Auth', + 'SOCKSState.RequestReady', + 'SOCKSState.Connected', + 'SOCKSState.AuthStarted' + ][_value]; + } +} + +class SOCKSAddressType { + static const IPv4 = const SOCKSAddressType._(0x01); + static const Domain = const SOCKSAddressType._(0x03); + static const IPv6 = const SOCKSAddressType._(0x04); + + final int _value; + + const SOCKSAddressType._(this._value); + + String toString() { + return const [ + null, + 'SOCKSAddressType.IPv4', + null, + 'SOCKSAddressType.Domain', + 'SOCKSAddressType.IPv6', + ][_value]; + } +} + +class SOCKSCommand { + static const Connect = const SOCKSCommand._(0x01); + static const Bind = const SOCKSCommand._(0x02); + static const UDPAssociate = const SOCKSCommand._(0x03); + + final int _value; + + const SOCKSCommand._(this._value); + + String toString() { + return const [ + null, + 'SOCKSCommand.Connect', + 'SOCKSCommand.Bind', + 'SOCKSCommand.UDPAssociate', + ][_value]; + } +} + +class SOCKSReply { + static const Success = const SOCKSReply._(0x00); + static const GeneralFailure = const SOCKSReply._(0x01); + static const ConnectionNotAllowedByRuleset = const SOCKSReply._(0x02); + static const NetworkUnreachable = const SOCKSReply._(0x03); + static const HostUnreachable = const SOCKSReply._(0x04); + static const ConnectionRefused = const SOCKSReply._(0x05); + static const TTLExpired = const SOCKSReply._(0x06); + static const CommandNotSupported = const SOCKSReply._(0x07); + static const AddressTypeNotSupported = const SOCKSReply._(0x08); + + final int _value; + + const SOCKSReply._(this._value); + + String toString() { + return const [ + 'SOCKSReply.Success', + 'SOCKSReply.GeneralFailure', + 'SOCKSReply.ConnectionNotAllowedByRuleset', + 'SOCKSReply.NetworkUnreachable', + 'SOCKSReply.HostUnreachable', + 'SOCKSReply.ConnectionRefused', + 'SOCKSReply.TTLExpired', + 'SOCKSReply.CommandNotSupported', + 'SOCKSReply.AddressTypeNotSupported' + ][_value]; + } +} + +class SOCKSRequest { + final int version = SOCKSVersion; + final SOCKSCommand command; + final SOCKSAddressType addressType; + final Uint8List address; + final int port; + + String getAddressString() { + if (addressType == SOCKSAddressType.Domain) { + return AsciiDecoder().convert(address); + } else if (addressType == SOCKSAddressType.IPv4) { + return address.join("."); + } else if (addressType == SOCKSAddressType.IPv6) { + var ret = List(); + for (var x = 0; x < address.length; x += 2) { + ret.add( + "${address[x].toRadixString(16).padLeft(2, "0")}${address[x + 1].toRadixString(16).padLeft(2, "0")}"); + } + return ret.join(":"); + } + return null; + } + + SOCKSRequest({ + this.command, + this.addressType, + this.address, + this.port, + }); +} + +class SOCKSSocket { + List _auth; + RawSocket _sock; + SOCKSRequest _request; + + StreamSubscription _sockSub; + StreamSubscription get subscription => _sockSub; + + SOCKSState _state; + final StreamController _stateStream = + StreamController(); + SOCKSState get state => _state; + Stream get stateStream => _stateStream?.stream; + + /// For username:password auth + final String username; + final String password; + + /// Waits for state to change to [SOCKSState.Connected] + /// If the connection request returns an error from the + /// socks server it will be thrown as an exception in the stream + /// + /// + Future get _waitForConnect => + stateStream.firstWhere((a) => a == SOCKSState.Connected); + + SOCKSSocket( + RawSocket socket, { + List auth = const [AuthMethods.NoAuth], + this.username, + this.password, + }) { + _sock = socket; + _auth = auth; + _setState(SOCKSState.Starting); + } + + void _setState(SOCKSState ns) { + _state = ns; + _stateStream.add(ns); + } + + /// Issue connect command to proxy + /// + Future connect(String domain) async { + final ds = domain.split(':'); + assert(ds.length == 2, "Domain must contain port, example.com:80"); + + _request = SOCKSRequest( + command: SOCKSCommand.Connect, + addressType: SOCKSAddressType.Domain, + address: AsciiEncoder().convert(ds[0]).sublist(0, ds[0].length), + port: int.tryParse(ds[1]) ?? 80, + ); + await _start(); + await _waitForConnect; + } + + Future connectIp(InternetAddress ip, int port) async { + _request = SOCKSRequest( + command: SOCKSCommand.Connect, + addressType: ip.type == InternetAddressType.IPv4 + ? SOCKSAddressType.IPv4 + : SOCKSAddressType.IPv6, + address: ip.rawAddress, + port: port, + ); + await _start(); + await _waitForConnect; + } + + Future close({bool keepOpen = true}) async { + await _stateStream.close(); + if (!keepOpen) { + await _sock.close(); + } + } + + Future _start() async { + // send auth methods + _setState(SOCKSState.Auth); + //print(">> Version: 5, AuthMethods: $_auth"); + _sock.write([ + 0x05, + _auth.length, + ..._auth.map((v) => v._value), + ]); + + _sockSub = _sock.listen((RawSocketEvent ev) { + switch (ev) { + case RawSocketEvent.read: + { + final have = _sock.available(); + final data = _sock.read(have); + _handleRead(data); + break; + } + case RawSocketEvent.closed: + { + _sockSub.cancel(); + break; + } + } + }); + } + + void _sendUsernamePassword(String uname, String password) { + if (uname.length > 255 || password.length > 255) { + throw "Username or Password is too long"; + } + + final data = [ + RFC1929Version, + uname.length, + ...AsciiEncoder().convert(uname), + password.length, + ...AsciiEncoder().convert(password) + ]; + + //print(">> Sending $username:$password"); + _sock.write(data); + } + + void _handleRead(Uint8List data) async { + if (state == SOCKSState.Auth) { + if (data.length == 2) { + final version = data[0]; + final auth = AuthMethods._(data[1]); + + //print("<< Version: $version, Auth: $auth"); + + if (auth._value == AuthMethods.UsernamePassword._value) { + _setState(SOCKSState.AuthStarted); + _sendUsernamePassword(username, password); + } else if (auth._value == AuthMethods.NoAuth._value) { + _setState(SOCKSState.RequestReady); + _writeRequest(_request); + } else if (auth._value == AuthMethods.NoAcceptableMethods._value) { + throw "No auth methods acceptable"; + } + } else { + throw "Expected 2 bytes"; + } + } else if (_state == SOCKSState.AuthStarted) { + if (_auth.contains(AuthMethods.UsernamePassword)) { + final version = data[0]; + final status = data[1]; + + if (version != RFC1929Version || status != 0x00) { + throw "Invalid username or password"; + } else { + _setState(SOCKSState.RequestReady); + _writeRequest(_request); + } + } + } else if (_state == SOCKSState.RequestReady) { + if (data.length >= 10) { + final version = data[0]; + final reply = SOCKSReply._(data[1]); + //data[2] reserved + final addrType = SOCKSAddressType._(data[3]); + Uint8List addr; + var port = 0; + + if (addrType == SOCKSAddressType.Domain) { + final len = data[4]; + addr = data.sublist(5, 5 + len); + port = data[5 + len] << 8 | data[6 + len]; + } else if (addrType == SOCKSAddressType.IPv4) { + addr = data.sublist(5, 9); + port = data[9] << 8 | data[10]; + } else if (addrType == SOCKSAddressType.IPv6) { + addr = data.sublist(5, 21); + port = data[21] << 8 | data[22]; + } + + //print("<< Version: $version, Reply: $reply, AddrType: $addrType, Addr: $addr, Port: $port"); + if (reply._value == SOCKSReply.Success._value) { + _setState(SOCKSState.Connected); + } else { + throw reply; + } + } else { + throw "Expected 10 bytes"; + } + } + } + + void _writeRequest(SOCKSRequest req) { + if (_state == SOCKSState.RequestReady) { + final data = [ + req.version, + req.command._value, + 0x00, + req.addressType._value, + if (req.addressType == SOCKSAddressType.Domain) + req.address.lengthInBytes, + ...req.address, + req.port >> 8, + req.port & 0xF0, + ]; + + //print(">> Version: ${req.version}, Command: ${req.command}, AddrType: ${req.addressType}, Addr: ${req.getAddressString()}, Port: ${req.port}"); + _sock.write(data); + } else { + throw "Must be in RequestReady state, current state $_state"; + } + } +} From 7c3c41ae5e3c532f776944c029d241445efaddd8 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 9 Aug 2023 12:14:37 -0500 Subject: [PATCH 3/3] fix v0l/socks5/master/lib/socks5.dart for dart 3 and Android Studio IDE warnings --- lib/networking/socks5.dart | 185 ++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/lib/networking/socks5.dart b/lib/networking/socks5.dart index df4ecee1e..72591f6d0 100644 --- a/lib/networking/socks5.dart +++ b/lib/networking/socks5.dart @@ -1,4 +1,6 @@ -library socks; +// https://github.com/v0l/socks5 https://pub.dev/packages/socks5 for Dart 3 + +// library socks; import 'dart:async'; import 'dart:convert'; @@ -7,42 +9,44 @@ import 'dart:typed_data'; /// https://tools.ietf.org/html/rfc1928 /// https://tools.ietf.org/html/rfc1929 -/// const SOCKSVersion = 0x05; const RFC1929Version = 0x01; class AuthMethods { - static const NoAuth = const AuthMethods._(0x00); - static const GSSApi = const AuthMethods._(0x01); - static const UsernamePassword = const AuthMethods._(0x02); - static const NoAcceptableMethods = const AuthMethods._(0xFF); + static const NoAuth = AuthMethods._(0x00); + static const GSSApi = AuthMethods._(0x01); + static const UsernamePassword = AuthMethods._(0x02); + static const NoAcceptableMethods = AuthMethods._(0xFF); final int _value; const AuthMethods._(this._value); + @override String toString() { return const { - 0x00: 'AuthMethods.NoAuth', - 0x01: 'AuthMethods.GSSApi', - 0x02: 'AuthMethods.UsernamePassword', - 0xFF: 'AuthMethods.NoAcceptableMethods' - }[_value]; + 0x00: 'AuthMethods.NoAuth', + 0x01: 'AuthMethods.GSSApi', + 0x02: 'AuthMethods.UsernamePassword', + 0xFF: 'AuthMethods.NoAcceptableMethods' + }[_value] ?? + 'Unknown AuthMethod'; } } class SOCKSState { - static const Starting = const SOCKSState._(0x00); - static const Auth = const SOCKSState._(0x01); - static const RequestReady = const SOCKSState._(0x02); - static const Connected = const SOCKSState._(0x03); - static const AuthStarted = const SOCKSState._(0x04); + static const Starting = SOCKSState._(0x00); + static const Auth = SOCKSState._(0x01); + static const RequestReady = SOCKSState._(0x02); + static const Connected = SOCKSState._(0x03); + static const AuthStarted = SOCKSState._(0x04); final int _value; const SOCKSState._(this._value); + @override String toString() { return const [ 'SOCKSState.Starting', @@ -55,59 +59,64 @@ class SOCKSState { } class SOCKSAddressType { - static const IPv4 = const SOCKSAddressType._(0x01); - static const Domain = const SOCKSAddressType._(0x03); - static const IPv6 = const SOCKSAddressType._(0x04); + static const IPv4 = SOCKSAddressType._(0x01); + static const Domain = SOCKSAddressType._(0x03); + static const IPv6 = SOCKSAddressType._(0x04); final int _value; const SOCKSAddressType._(this._value); + @override String toString() { return const [ - null, - 'SOCKSAddressType.IPv4', - null, - 'SOCKSAddressType.Domain', - 'SOCKSAddressType.IPv6', - ][_value]; + null, + 'SOCKSAddressType.IPv4', + null, + 'SOCKSAddressType.Domain', + 'SOCKSAddressType.IPv6', + ][_value] ?? + 'Unknown SOCKSAddressType'; } } class SOCKSCommand { - static const Connect = const SOCKSCommand._(0x01); - static const Bind = const SOCKSCommand._(0x02); - static const UDPAssociate = const SOCKSCommand._(0x03); + static const Connect = SOCKSCommand._(0x01); + static const Bind = SOCKSCommand._(0x02); + static const UDPAssociate = SOCKSCommand._(0x03); final int _value; const SOCKSCommand._(this._value); + @override String toString() { return const [ - null, - 'SOCKSCommand.Connect', - 'SOCKSCommand.Bind', - 'SOCKSCommand.UDPAssociate', - ][_value]; + null, + 'SOCKSCommand.Connect', + 'SOCKSCommand.Bind', + 'SOCKSCommand.UDPAssociate', + ][_value] ?? + 'Unknown SOCKSCommand'; } } class SOCKSReply { - static const Success = const SOCKSReply._(0x00); - static const GeneralFailure = const SOCKSReply._(0x01); - static const ConnectionNotAllowedByRuleset = const SOCKSReply._(0x02); - static const NetworkUnreachable = const SOCKSReply._(0x03); - static const HostUnreachable = const SOCKSReply._(0x04); - static const ConnectionRefused = const SOCKSReply._(0x05); - static const TTLExpired = const SOCKSReply._(0x06); - static const CommandNotSupported = const SOCKSReply._(0x07); - static const AddressTypeNotSupported = const SOCKSReply._(0x08); + static const Success = SOCKSReply._(0x00); + static const GeneralFailure = SOCKSReply._(0x01); + static const ConnectionNotAllowedByRuleset = SOCKSReply._(0x02); + static const NetworkUnreachable = SOCKSReply._(0x03); + static const HostUnreachable = SOCKSReply._(0x04); + static const ConnectionRefused = SOCKSReply._(0x05); + static const TTLExpired = SOCKSReply._(0x06); + static const CommandNotSupported = SOCKSReply._(0x07); + static const AddressTypeNotSupported = SOCKSReply._(0x08); final int _value; const SOCKSReply._(this._value); + @override String toString() { return const [ 'SOCKSReply.Success', @@ -130,13 +139,13 @@ class SOCKSRequest { final Uint8List address; final int port; - String getAddressString() { + String? getAddressString() { if (addressType == SOCKSAddressType.Domain) { - return AsciiDecoder().convert(address); + return const AsciiDecoder().convert(address); } else if (addressType == SOCKSAddressType.IPv4) { return address.join("."); } else if (addressType == SOCKSAddressType.IPv6) { - var ret = List(); + var ret = []; for (var x = 0; x < address.length; x += 2) { ret.add( "${address[x].toRadixString(16).padLeft(2, "0")}${address[x + 1].toRadixString(16).padLeft(2, "0")}"); @@ -147,30 +156,30 @@ class SOCKSRequest { } SOCKSRequest({ - this.command, - this.addressType, - this.address, - this.port, + required this.command, + required this.addressType, + required this.address, + required this.port, }); } class SOCKSSocket { - List _auth; - RawSocket _sock; - SOCKSRequest _request; + late List _auth; + late RawSocket _sock; + late SOCKSRequest _request; - StreamSubscription _sockSub; + late StreamSubscription _sockSub; StreamSubscription get subscription => _sockSub; - SOCKSState _state; + late SOCKSState _state; final StreamController _stateStream = StreamController(); SOCKSState get state => _state; - Stream get stateStream => _stateStream?.stream; + Stream get stateStream => _stateStream.stream; /// For username:password auth - final String username; - final String password; + final String? username; + final String? password; /// Waits for state to change to [SOCKSState.Connected] /// If the connection request returns an error from the @@ -198,21 +207,21 @@ class SOCKSSocket { /// Issue connect command to proxy /// - Future connect(String domain) async { + Future connect(String domain) async { final ds = domain.split(':'); assert(ds.length == 2, "Domain must contain port, example.com:80"); _request = SOCKSRequest( command: SOCKSCommand.Connect, addressType: SOCKSAddressType.Domain, - address: AsciiEncoder().convert(ds[0]).sublist(0, ds[0].length), + address: const AsciiEncoder().convert(ds[0]).sublist(0, ds[0].length), port: int.tryParse(ds[1]) ?? 80, ); await _start(); await _waitForConnect; } - Future connectIp(InternetAddress ip, int port) async { + Future connectIp(InternetAddress ip, int port) async { _request = SOCKSRequest( command: SOCKSCommand.Connect, addressType: ip.type == InternetAddressType.IPv4 @@ -225,14 +234,14 @@ class SOCKSSocket { await _waitForConnect; } - Future close({bool keepOpen = true}) async { + Future close({bool keepOpen = true}) async { await _stateStream.close(); if (!keepOpen) { await _sock.close(); } } - Future _start() async { + Future _start() async { // send auth methods _setState(SOCKSState.Auth); //print(">> Version: 5, AuthMethods: $_auth"); @@ -248,7 +257,7 @@ class SOCKSSocket { { final have = _sock.available(); final data = _sock.read(have); - _handleRead(data); + if (data != null) _handleRead(data); break; } case RawSocketEvent.closed: @@ -256,6 +265,12 @@ class SOCKSSocket { _sockSub.cancel(); break; } + case RawSocketEvent.readClosed: + // TODO: Handle this case. + break; + case RawSocketEvent.write: + // TODO: Handle this case. + break; } }); } @@ -268,9 +283,9 @@ class SOCKSSocket { final data = [ RFC1929Version, uname.length, - ...AsciiEncoder().convert(uname), + ...const AsciiEncoder().convert(uname), password.length, - ...AsciiEncoder().convert(password) + ...const AsciiEncoder().convert(password) ]; //print(">> Sending $username:$password"); @@ -280,14 +295,14 @@ class SOCKSSocket { void _handleRead(Uint8List data) async { if (state == SOCKSState.Auth) { if (data.length == 2) { - final version = data[0]; - final auth = AuthMethods._(data[1]); - + // final version = data[0]; //print("<< Version: $version, Auth: $auth"); + final auth = AuthMethods._(data[1]); if (auth._value == AuthMethods.UsernamePassword._value) { _setState(SOCKSState.AuthStarted); - _sendUsernamePassword(username, password); + _sendUsernamePassword(username ?? '', password ?? ''); + // TODO check that passing an empty string is valid (vs. null previously) } else if (auth._value == AuthMethods.NoAuth._value) { _setState(SOCKSState.RequestReady); _writeRequest(_request); @@ -311,26 +326,26 @@ class SOCKSSocket { } } else if (_state == SOCKSState.RequestReady) { if (data.length >= 10) { - final version = data[0]; final reply = SOCKSReply._(data[1]); //data[2] reserved - final addrType = SOCKSAddressType._(data[3]); - Uint8List addr; - var port = 0; - if (addrType == SOCKSAddressType.Domain) { - final len = data[4]; - addr = data.sublist(5, 5 + len); - port = data[5 + len] << 8 | data[6 + len]; - } else if (addrType == SOCKSAddressType.IPv4) { - addr = data.sublist(5, 9); - port = data[9] << 8 | data[10]; - } else if (addrType == SOCKSAddressType.IPv6) { - addr = data.sublist(5, 21); - port = data[21] << 8 | data[22]; - } + // final version = data[0]; + // final addrType = SOCKSAddressType._(data[3]); + // Uint8List addr; + // var port = 0; + // if (addrType == SOCKSAddressType.Domain) { + // final len = data[4]; + // addr = data.sublist(5, 5 + len); + // port = data[5 + len] << 8 | data[6 + len]; + // } else if (addrType == SOCKSAddressType.IPv4) { + // addr = data.sublist(5, 9); + // port = data[9] << 8 | data[10]; + // } else if (addrType == SOCKSAddressType.IPv6) { + // addr = data.sublist(5, 21); + // port = data[21] << 8 | data[22]; + // } + // print("<< Version: $version, Reply: $reply, AddrType: $addrType, Addr: $addr, Port: $port"); - //print("<< Version: $version, Reply: $reply, AddrType: $addrType, Addr: $addr, Port: $port"); if (reply._value == SOCKSReply.Success._value) { _setState(SOCKSState.Connected); } else {