From 05726e45a9f8392acb0fe9d01722b957ed825b6d Mon Sep 17 00:00:00 2001
From: julian <julian@cypherstack.com>
Date: Mon, 14 Aug 2023 14:56:18 -0600
Subject: [PATCH] make proper enums

---
 lib/networking/socks5.dart | 339 +++++++++++++++++++------------------
 1 file changed, 171 insertions(+), 168 deletions(-)

diff --git a/lib/networking/socks5.dart b/lib/networking/socks5.dart
index 4737ed640..ba655f3db 100644
--- a/lib/networking/socks5.dart
+++ b/lib/networking/socks5.dart
@@ -1,137 +1,132 @@
-// https://github.com/v0l/socks5 https://pub.dev/packages/socks5 for Dart 3
-
-// library socks;
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
 import 'dart:typed_data';
 
-import 'package:stackwallet/utilities/logger.dart';
-
 /// https://tools.ietf.org/html/rfc1928
 /// https://tools.ietf.org/html/rfc1929
+///
 
 const SOCKSVersion = 0x05;
 const RFC1929Version = 0x01;
 
-class AuthMethods {
-  static const NoAuth = AuthMethods._(0x00);
-  static const GSSApi = AuthMethods._(0x01);
-  static const UsernamePassword = AuthMethods._(0x02);
-  static const NoAcceptableMethods = AuthMethods._(0xFF);
+enum AuthMethods {
+  NoAuth(0x00),
+  GSSApi(0x01),
+  UsernamePassword(0x02),
+  NoAcceptableMethods(0xFF);
 
-  final int _value;
+  final int rawValue;
 
-  const AuthMethods._(this._value);
+  const AuthMethods(this.rawValue);
+
+  factory AuthMethods.fromValue(int value) {
+    for (final v in values) {
+      if (v.rawValue == value) {
+        return v;
+      }
+    }
+    throw UnsupportedError("Invalid AuthMethods value");
+  }
 
   @override
-  String toString() {
-    return const {
-          0x00: 'AuthMethods.NoAuth',
-          0x01: 'AuthMethods.GSSApi',
-          0x02: 'AuthMethods.UsernamePassword',
-          0xFF: 'AuthMethods.NoAcceptableMethods'
-        }[_value] ??
-        'Unknown AuthMethod';
-  }
+  String toString() => "$runtimeType.$name";
 }
 
-class SOCKSState {
-  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);
+enum SOCKSState {
+  Starting(0x00),
+  Auth(0x01),
+  RequestReady(0x02),
+  Connected(0x03),
+  AuthStarted(0x04);
 
-  final int _value;
+  final int rawValue;
 
-  const SOCKSState._(this._value);
+  const SOCKSState(this.rawValue);
+
+  factory SOCKSState.fromValue(int value) {
+    for (final v in values) {
+      if (v.rawValue == value) {
+        return v;
+      }
+    }
+    throw UnsupportedError("Invalid SOCKSState value");
+  }
 
   @override
-  String toString() {
-    return const [
-      'SOCKSState.Starting',
-      'SOCKSState.Auth',
-      'SOCKSState.RequestReady',
-      'SOCKSState.Connected',
-      'SOCKSState.AuthStarted'
-    ][_value];
-  }
+  String toString() => "$runtimeType.$name";
 }
 
-class SOCKSAddressType {
-  static const IPv4 = SOCKSAddressType._(0x01);
-  static const Domain = SOCKSAddressType._(0x03);
-  static const IPv6 = SOCKSAddressType._(0x04);
+enum SOCKSAddressType {
+  IPv4(0x01),
+  Domain(0x03),
+  IPv6(0x04);
 
-  final int _value;
+  final int rawValue;
 
-  const SOCKSAddressType._(this._value);
+  const SOCKSAddressType(this.rawValue);
+
+  factory SOCKSAddressType.fromValue(int value) {
+    for (final v in values) {
+      if (v.rawValue == value) {
+        return v;
+      }
+    }
+    throw UnsupportedError("Invalid SOCKSAddressType value");
+  }
 
   @override
-  String toString() {
-    return const [
-          null,
-          'SOCKSAddressType.IPv4',
-          null,
-          'SOCKSAddressType.Domain',
-          'SOCKSAddressType.IPv6',
-        ][_value] ??
-        'Unknown SOCKSAddressType';
-  }
+  String toString() => "$runtimeType.$name";
 }
 
-class SOCKSCommand {
-  static const Connect = SOCKSCommand._(0x01);
-  static const Bind = SOCKSCommand._(0x02);
-  static const UDPAssociate = SOCKSCommand._(0x03);
+enum SOCKSCommand {
+  Connect(0x01),
+  Bind(0x02),
+  UDPAssociate(0x03);
 
-  final int _value;
+  final int rawValue;
 
-  const SOCKSCommand._(this._value);
+  const SOCKSCommand(this.rawValue);
+
+  factory SOCKSCommand.fromValue(int value) {
+    for (final v in values) {
+      if (v.rawValue == value) {
+        return v;
+      }
+    }
+    throw UnsupportedError("Invalid SOCKSCommand value");
+  }
 
   @override
-  String toString() {
-    return const [
-          null,
-          'SOCKSCommand.Connect',
-          'SOCKSCommand.Bind',
-          'SOCKSCommand.UDPAssociate',
-        ][_value] ??
-        'Unknown SOCKSCommand';
-  }
+  String toString() => "$runtimeType.$name";
 }
 
-class SOCKSReply {
-  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);
+enum SOCKSReply {
+  Success(0x00),
+  GeneralFailure(0x01),
+  ConnectionNotAllowedByRuleSet(0x02),
+  NetworkUnreachable(0x03),
+  HostUnreachable(0x04),
+  ConnectionRefused(0x05),
+  TTLExpired(0x06),
+  CommandNotSupported(0x07),
+  AddressTypeNotSupported(0x08);
 
-  final int _value;
+  final int rawValue;
 
-  const SOCKSReply._(this._value);
+  const SOCKSReply(this.rawValue);
+
+  factory SOCKSReply.fromValue(int value) {
+    for (final v in values) {
+      if (v.rawValue == value) {
+        return v;
+      }
+    }
+    throw UnsupportedError("Invalid SOCKSReply value");
+  }
 
   @override
-  String toString() {
-    return const [
-      'SOCKSReply.Success',
-      'SOCKSReply.GeneralFailure',
-      'SOCKSReply.ConnectionNotAllowedByRuleset',
-      'SOCKSReply.NetworkUnreachable',
-      'SOCKSReply.HostUnreachable',
-      'SOCKSReply.ConnectionRefused',
-      'SOCKSReply.TTLExpired',
-      'SOCKSReply.CommandNotSupported',
-      'SOCKSReply.AddressTypeNotSupported'
-    ][_value];
-  }
+  String toString() => "$runtimeType.$name";
 }
 
 class SOCKSRequest {
@@ -141,20 +136,20 @@ class SOCKSRequest {
   final Uint8List address;
   final int port;
 
-  String? getAddressString() {
-    if (addressType == SOCKSAddressType.Domain) {
-      return const AsciiDecoder().convert(address);
-    } else if (addressType == SOCKSAddressType.IPv4) {
-      return address.join(".");
-    } else if (addressType == SOCKSAddressType.IPv6) {
-      var ret = <String>[];
-      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(":");
+  String getAddressString() {
+    switch (addressType) {
+      case SOCKSAddressType.Domain:
+        return const AsciiDecoder().convert(address);
+      case SOCKSAddressType.IPv4:
+        return address.join(".");
+      case SOCKSAddressType.IPv6:
+        final List<String> ret = [];
+        for (int 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({
@@ -168,13 +163,12 @@ class SOCKSRequest {
 class SOCKSSocket {
   late List<AuthMethods> _auth;
   late RawSocket _sock;
-  RawSocket get socket => _sock;
-  late SOCKSRequest _request;
+  SOCKSRequest? _request;
 
-  late StreamSubscription<RawSocketEvent> _sockSub;
-  StreamSubscription<RawSocketEvent> get subscription => _sockSub;
+  StreamSubscription<RawSocketEvent>? _sockSub;
+  StreamSubscription<RawSocketEvent>? get subscription => _sockSub;
 
-  late SOCKSState _state;
+  SOCKSState _state = SOCKSState.Starting;
   final StreamController<SOCKSState> _stateStream =
       StreamController<SOCKSState>();
   SOCKSState get state => _state;
@@ -217,7 +211,7 @@ class SOCKSSocket {
     _request = SOCKSRequest(
       command: SOCKSCommand.Connect,
       addressType: SOCKSAddressType.Domain,
-      address: const AsciiEncoder().convert(ds[0]).sublist(0, ds[0].length),
+      address: AsciiEncoder().convert(ds[0]).sublist(0, ds[0].length),
       port: int.tryParse(ds[1]) ?? 80,
     );
     await _start();
@@ -251,7 +245,7 @@ class SOCKSSocket {
     _sock.write([
       0x05,
       _auth.length,
-      ..._auth.map((v) => v._value),
+      ..._auth.map((v) => v.rawValue),
     ]);
 
     _sockSub = _sock.listen((RawSocketEvent ev) {
@@ -260,26 +254,32 @@ class SOCKSSocket {
           {
             final have = _sock.available();
             final data = _sock.read(have);
-            if (data != null) _handleRead(data);
+            if (data != null) {
+              _handleRead(data);
+            } else {
+              print("========= sock read DATA is NULL");
+            }
             break;
           }
         case RawSocketEvent.closed:
           {
-            _sockSub.cancel();
+            _sockSub?.cancel();
             break;
           }
-        case RawSocketEvent.readClosed:
-          // TODO: Handle this case.
-          Logging.instance.log(
-              "SOCKSSocket._start(): unhandled event RawSocketEvent.readClosed",
-              level: LogLevel.Warning);
-          break;
-        case RawSocketEvent.write:
-          // TODO: Handle this case.
-          Logging.instance.log(
-              "SOCKSSocket._start(): unhandled event RawSocketEvent.write",
-              level: LogLevel.Warning);
-          break;
+        default:
+          print("AAAAAAAAAAAAA: unhandled raw socket event: $ev");
+        // case RawSocketEvent.closed:
+        //   // TODO: Handle this case.
+        //   break;
+        // case RawSocketEvent.read:
+        //   // TODO: Handle this case.
+        //   break;
+        // case RawSocketEvent.readClosed:
+        //   // TODO: Handle this case.
+        //   break;
+        // case RawSocketEvent.write:
+        //   // TODO: Handle this case.
+        //   break;
       }
     });
   }
@@ -304,19 +304,23 @@ class SOCKSSocket {
   void _handleRead(Uint8List data) async {
     if (state == SOCKSState.Auth) {
       if (data.length == 2) {
-        // final version = data[0];
-        //print("<< Version: $version, Auth: $auth");
-        final auth = AuthMethods._(data[1]);
+        final version = data[0];
+        final auth = AuthMethods.fromValue(data[1]);
 
-        if (auth._value == AuthMethods.UsernamePassword._value) {
-          _setState(SOCKSState.AuthStarted);
-          _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);
-        } else if (auth._value == AuthMethods.NoAcceptableMethods._value) {
-          throw "No auth methods acceptable";
+        print("_handleRead << Version: $version, Auth: $auth");
+
+        switch (auth) {
+          case AuthMethods.UsernamePassword:
+            _setState(SOCKSState.AuthStarted);
+            _sendUsernamePassword(username ?? '', password ?? '');
+            break;
+          case AuthMethods.NoAuth:
+            _setState(SOCKSState.RequestReady);
+            _writeRequest(_request!);
+
+            break;
+          default:
+            throw "No auth methods acceptable";
         }
       } else {
         throw "Expected 2 bytes";
@@ -330,33 +334,37 @@ class SOCKSSocket {
           throw "Invalid username or password";
         } else {
           _setState(SOCKSState.RequestReady);
-          _writeRequest(_request);
+          _writeRequest(_request!);
         }
       }
     } else if (_state == SOCKSState.RequestReady) {
       if (data.length >= 10) {
-        final reply = SOCKSReply._(data[1]);
-        //data[2] reserved
-
         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];
+        final reply = SOCKSReply.fromValue(data[1]);
+        //data[2] reserved
+        final addrType = SOCKSAddressType.fromValue(data[3]);
+        Uint8List addr;
+        int port = 0;
+
+        switch (addrType) {
+          case SOCKSAddressType.Domain:
+            final len = data[4];
+            addr = data.sublist(5, 5 + len);
+            port = data[5 + len] << 8 | data[6 + len];
+            break;
+          case SOCKSAddressType.IPv4:
+            addr = data.sublist(5, 9);
+            port = data[9] << 8 | data[10];
+            break;
+          case SOCKSAddressType.IPv6:
+            addr = data.sublist(5, 21);
+            port = data[21] << 8 | data[22];
+            break;
         }
+
         print(
             "<< Version: $version, Reply: $reply, AddrType: $addrType, Addr: $addr, Port: $port");
-
-        if (reply._value == SOCKSReply.Success._value) {
+        if (reply.rawValue == SOCKSReply.Success.rawValue) {
           _setState(SOCKSState.Connected);
         } else {
           throw reply;
@@ -371,9 +379,9 @@ class SOCKSSocket {
     if (_state == SOCKSState.RequestReady) {
       final data = [
         req.version,
-        req.command._value,
+        req.command.rawValue,
         0x00,
-        req.addressType._value,
+        req.addressType.rawValue,
         if (req.addressType == SOCKSAddressType.Domain)
           req.address.lengthInBytes,
         ...req.address,
@@ -382,15 +390,10 @@ class SOCKSSocket {
       ];
 
       print(
-          ">> Version: ${req.version}, Command: ${req.command}, AddrType: ${req.addressType}, Addr: ${req.getAddressString()}, Port: ${req.port}");
+          "_writeRequest >> 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";
     }
   }
-
-  void write(String data) {
-    _sock.write(utf8.encode(data));
-    // TODO make sure the is correct; see _writeRequest above, may need to construct a SOCKSRequest from the data coming in
-  }
 }