socks5NegotiateAuth function

Future<void> socks5NegotiateAuth(
  1. Socks5HandshakeReader reader,
  2. void sink(
    1. List<int> bytes
    ), {
  3. String? username,
  4. String? password,
})

Negotiates the method-selection + optional RFC1929 user/pass leg against reader, writing through sink. Public and socket-free so the unit suite drives it with a scripted byte stream.

Implementation

Future<void> socks5NegotiateAuth(
  Socks5HandshakeReader reader,
  void Function(List<int> bytes) sink, {
  String? username,
  String? password,
}) async {
  final useAuth = username != null;
  sink(socks5GreetingBytes(useAuth: useAuth));
  final greeting = await reader.readExactly(2);
  if (greeting[0] != 0x05) {
    throw const SocketException('SOCKS5 proxy sent a non-v5 greeting');
  }
  switch (greeting[1]) {
    case 0x00:
      break;
    case 0x02 when useAuth:
      sink(_rfc1929AuthBytes(username: username, password: password));
      final verdict = await reader.readExactly(2);
      if (verdict[1] != 0x00) {
        throw const SocketException(
          'SOCKS5 username/password authentication failed (RFC1929 status '
          'non-zero) — check proxyUsername / proxyPasswordEnv',
        );
      }
    case 0xFF:
      throw const SocketException(
        'SOCKS5 proxy rejected every offered auth method',
      );
    default:
      throw SocketException(
        'SOCKS5 proxy chose unsupported auth method 0x'
        '${greeting[1].toRadixString(16)}',
      );
  }
}