fix v0l/socks5/master/lib/socks5.dart for dart 3

and Android Studio IDE warnings
This commit is contained in:
sneurlax 2023-08-09 12:14:37 -05:00
parent cfce22fa73
commit 7c3c41ae5e

View file

@ -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:async';
import 'dart:convert'; import 'dart:convert';
@ -7,42 +9,44 @@ import 'dart:typed_data';
/// https://tools.ietf.org/html/rfc1928 /// https://tools.ietf.org/html/rfc1928
/// https://tools.ietf.org/html/rfc1929 /// https://tools.ietf.org/html/rfc1929
///
const SOCKSVersion = 0x05; const SOCKSVersion = 0x05;
const RFC1929Version = 0x01; const RFC1929Version = 0x01;
class AuthMethods { class AuthMethods {
static const NoAuth = const AuthMethods._(0x00); static const NoAuth = AuthMethods._(0x00);
static const GSSApi = const AuthMethods._(0x01); static const GSSApi = AuthMethods._(0x01);
static const UsernamePassword = const AuthMethods._(0x02); static const UsernamePassword = AuthMethods._(0x02);
static const NoAcceptableMethods = const AuthMethods._(0xFF); static const NoAcceptableMethods = AuthMethods._(0xFF);
final int _value; final int _value;
const AuthMethods._(this._value); const AuthMethods._(this._value);
@override
String toString() { String toString() {
return const { return const {
0x00: 'AuthMethods.NoAuth', 0x00: 'AuthMethods.NoAuth',
0x01: 'AuthMethods.GSSApi', 0x01: 'AuthMethods.GSSApi',
0x02: 'AuthMethods.UsernamePassword', 0x02: 'AuthMethods.UsernamePassword',
0xFF: 'AuthMethods.NoAcceptableMethods' 0xFF: 'AuthMethods.NoAcceptableMethods'
}[_value]; }[_value] ??
'Unknown AuthMethod';
} }
} }
class SOCKSState { class SOCKSState {
static const Starting = const SOCKSState._(0x00); static const Starting = SOCKSState._(0x00);
static const Auth = const SOCKSState._(0x01); static const Auth = SOCKSState._(0x01);
static const RequestReady = const SOCKSState._(0x02); static const RequestReady = SOCKSState._(0x02);
static const Connected = const SOCKSState._(0x03); static const Connected = SOCKSState._(0x03);
static const AuthStarted = const SOCKSState._(0x04); static const AuthStarted = SOCKSState._(0x04);
final int _value; final int _value;
const SOCKSState._(this._value); const SOCKSState._(this._value);
@override
String toString() { String toString() {
return const [ return const [
'SOCKSState.Starting', 'SOCKSState.Starting',
@ -55,59 +59,64 @@ class SOCKSState {
} }
class SOCKSAddressType { class SOCKSAddressType {
static const IPv4 = const SOCKSAddressType._(0x01); static const IPv4 = SOCKSAddressType._(0x01);
static const Domain = const SOCKSAddressType._(0x03); static const Domain = SOCKSAddressType._(0x03);
static const IPv6 = const SOCKSAddressType._(0x04); static const IPv6 = SOCKSAddressType._(0x04);
final int _value; final int _value;
const SOCKSAddressType._(this._value); const SOCKSAddressType._(this._value);
@override
String toString() { String toString() {
return const [ return const [
null, null,
'SOCKSAddressType.IPv4', 'SOCKSAddressType.IPv4',
null, null,
'SOCKSAddressType.Domain', 'SOCKSAddressType.Domain',
'SOCKSAddressType.IPv6', 'SOCKSAddressType.IPv6',
][_value]; ][_value] ??
'Unknown SOCKSAddressType';
} }
} }
class SOCKSCommand { class SOCKSCommand {
static const Connect = const SOCKSCommand._(0x01); static const Connect = SOCKSCommand._(0x01);
static const Bind = const SOCKSCommand._(0x02); static const Bind = SOCKSCommand._(0x02);
static const UDPAssociate = const SOCKSCommand._(0x03); static const UDPAssociate = SOCKSCommand._(0x03);
final int _value; final int _value;
const SOCKSCommand._(this._value); const SOCKSCommand._(this._value);
@override
String toString() { String toString() {
return const [ return const [
null, null,
'SOCKSCommand.Connect', 'SOCKSCommand.Connect',
'SOCKSCommand.Bind', 'SOCKSCommand.Bind',
'SOCKSCommand.UDPAssociate', 'SOCKSCommand.UDPAssociate',
][_value]; ][_value] ??
'Unknown SOCKSCommand';
} }
} }
class SOCKSReply { class SOCKSReply {
static const Success = const SOCKSReply._(0x00); static const Success = SOCKSReply._(0x00);
static const GeneralFailure = const SOCKSReply._(0x01); static const GeneralFailure = SOCKSReply._(0x01);
static const ConnectionNotAllowedByRuleset = const SOCKSReply._(0x02); static const ConnectionNotAllowedByRuleset = SOCKSReply._(0x02);
static const NetworkUnreachable = const SOCKSReply._(0x03); static const NetworkUnreachable = SOCKSReply._(0x03);
static const HostUnreachable = const SOCKSReply._(0x04); static const HostUnreachable = SOCKSReply._(0x04);
static const ConnectionRefused = const SOCKSReply._(0x05); static const ConnectionRefused = SOCKSReply._(0x05);
static const TTLExpired = const SOCKSReply._(0x06); static const TTLExpired = SOCKSReply._(0x06);
static const CommandNotSupported = const SOCKSReply._(0x07); static const CommandNotSupported = SOCKSReply._(0x07);
static const AddressTypeNotSupported = const SOCKSReply._(0x08); static const AddressTypeNotSupported = SOCKSReply._(0x08);
final int _value; final int _value;
const SOCKSReply._(this._value); const SOCKSReply._(this._value);
@override
String toString() { String toString() {
return const [ return const [
'SOCKSReply.Success', 'SOCKSReply.Success',
@ -130,13 +139,13 @@ class SOCKSRequest {
final Uint8List address; final Uint8List address;
final int port; final int port;
String getAddressString() { String? getAddressString() {
if (addressType == SOCKSAddressType.Domain) { if (addressType == SOCKSAddressType.Domain) {
return AsciiDecoder().convert(address); return const AsciiDecoder().convert(address);
} else if (addressType == SOCKSAddressType.IPv4) { } else if (addressType == SOCKSAddressType.IPv4) {
return address.join("."); return address.join(".");
} else if (addressType == SOCKSAddressType.IPv6) { } else if (addressType == SOCKSAddressType.IPv6) {
var ret = List<String>(); var ret = <String>[];
for (var x = 0; x < address.length; x += 2) { for (var x = 0; x < address.length; x += 2) {
ret.add( ret.add(
"${address[x].toRadixString(16).padLeft(2, "0")}${address[x + 1].toRadixString(16).padLeft(2, "0")}"); "${address[x].toRadixString(16).padLeft(2, "0")}${address[x + 1].toRadixString(16).padLeft(2, "0")}");
@ -147,30 +156,30 @@ class SOCKSRequest {
} }
SOCKSRequest({ SOCKSRequest({
this.command, required this.command,
this.addressType, required this.addressType,
this.address, required this.address,
this.port, required this.port,
}); });
} }
class SOCKSSocket { class SOCKSSocket {
List<AuthMethods> _auth; late List<AuthMethods> _auth;
RawSocket _sock; late RawSocket _sock;
SOCKSRequest _request; late SOCKSRequest _request;
StreamSubscription<RawSocketEvent> _sockSub; late StreamSubscription<RawSocketEvent> _sockSub;
StreamSubscription<RawSocketEvent> get subscription => _sockSub; StreamSubscription<RawSocketEvent> get subscription => _sockSub;
SOCKSState _state; late SOCKSState _state;
final StreamController<SOCKSState> _stateStream = final StreamController<SOCKSState> _stateStream =
StreamController<SOCKSState>(); StreamController<SOCKSState>();
SOCKSState get state => _state; SOCKSState get state => _state;
Stream<SOCKSState> get stateStream => _stateStream?.stream; Stream<SOCKSState> get stateStream => _stateStream.stream;
/// For username:password auth /// For username:password auth
final String username; final String? username;
final String password; final String? password;
/// Waits for state to change to [SOCKSState.Connected] /// Waits for state to change to [SOCKSState.Connected]
/// If the connection request returns an error from the /// If the connection request returns an error from the
@ -198,21 +207,21 @@ class SOCKSSocket {
/// Issue connect command to proxy /// Issue connect command to proxy
/// ///
Future connect(String domain) async { Future<void> connect(String domain) async {
final ds = domain.split(':'); final ds = domain.split(':');
assert(ds.length == 2, "Domain must contain port, example.com:80"); assert(ds.length == 2, "Domain must contain port, example.com:80");
_request = SOCKSRequest( _request = SOCKSRequest(
command: SOCKSCommand.Connect, command: SOCKSCommand.Connect,
addressType: SOCKSAddressType.Domain, 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, port: int.tryParse(ds[1]) ?? 80,
); );
await _start(); await _start();
await _waitForConnect; await _waitForConnect;
} }
Future connectIp(InternetAddress ip, int port) async { Future<void> connectIp(InternetAddress ip, int port) async {
_request = SOCKSRequest( _request = SOCKSRequest(
command: SOCKSCommand.Connect, command: SOCKSCommand.Connect,
addressType: ip.type == InternetAddressType.IPv4 addressType: ip.type == InternetAddressType.IPv4
@ -225,14 +234,14 @@ class SOCKSSocket {
await _waitForConnect; await _waitForConnect;
} }
Future close({bool keepOpen = true}) async { Future<void> close({bool keepOpen = true}) async {
await _stateStream.close(); await _stateStream.close();
if (!keepOpen) { if (!keepOpen) {
await _sock.close(); await _sock.close();
} }
} }
Future _start() async { Future<void> _start() async {
// send auth methods // send auth methods
_setState(SOCKSState.Auth); _setState(SOCKSState.Auth);
//print(">> Version: 5, AuthMethods: $_auth"); //print(">> Version: 5, AuthMethods: $_auth");
@ -248,7 +257,7 @@ class SOCKSSocket {
{ {
final have = _sock.available(); final have = _sock.available();
final data = _sock.read(have); final data = _sock.read(have);
_handleRead(data); if (data != null) _handleRead(data);
break; break;
} }
case RawSocketEvent.closed: case RawSocketEvent.closed:
@ -256,6 +265,12 @@ class SOCKSSocket {
_sockSub.cancel(); _sockSub.cancel();
break; 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 = [ final data = [
RFC1929Version, RFC1929Version,
uname.length, uname.length,
...AsciiEncoder().convert(uname), ...const AsciiEncoder().convert(uname),
password.length, password.length,
...AsciiEncoder().convert(password) ...const AsciiEncoder().convert(password)
]; ];
//print(">> Sending $username:$password"); //print(">> Sending $username:$password");
@ -280,14 +295,14 @@ class SOCKSSocket {
void _handleRead(Uint8List data) async { void _handleRead(Uint8List data) async {
if (state == SOCKSState.Auth) { if (state == SOCKSState.Auth) {
if (data.length == 2) { if (data.length == 2) {
final version = data[0]; // final version = data[0];
final auth = AuthMethods._(data[1]);
//print("<< Version: $version, Auth: $auth"); //print("<< Version: $version, Auth: $auth");
final auth = AuthMethods._(data[1]);
if (auth._value == AuthMethods.UsernamePassword._value) { if (auth._value == AuthMethods.UsernamePassword._value) {
_setState(SOCKSState.AuthStarted); _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) { } else if (auth._value == AuthMethods.NoAuth._value) {
_setState(SOCKSState.RequestReady); _setState(SOCKSState.RequestReady);
_writeRequest(_request); _writeRequest(_request);
@ -311,26 +326,26 @@ class SOCKSSocket {
} }
} else if (_state == SOCKSState.RequestReady) { } else if (_state == SOCKSState.RequestReady) {
if (data.length >= 10) { if (data.length >= 10) {
final version = data[0];
final reply = SOCKSReply._(data[1]); final reply = SOCKSReply._(data[1]);
//data[2] reserved //data[2] reserved
final addrType = SOCKSAddressType._(data[3]);
Uint8List addr;
var port = 0;
if (addrType == SOCKSAddressType.Domain) { // final version = data[0];
final len = data[4]; // final addrType = SOCKSAddressType._(data[3]);
addr = data.sublist(5, 5 + len); // Uint8List addr;
port = data[5 + len] << 8 | data[6 + len]; // var port = 0;
} else if (addrType == SOCKSAddressType.IPv4) { // if (addrType == SOCKSAddressType.Domain) {
addr = data.sublist(5, 9); // final len = data[4];
port = data[9] << 8 | data[10]; // addr = data.sublist(5, 5 + len);
} else if (addrType == SOCKSAddressType.IPv6) { // port = data[5 + len] << 8 | data[6 + len];
addr = data.sublist(5, 21); // } else if (addrType == SOCKSAddressType.IPv4) {
port = data[21] << 8 | data[22]; // 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) { if (reply._value == SOCKSReply.Success._value) {
_setState(SOCKSState.Connected); _setState(SOCKSState.Connected);
} else { } else {