Un servidor SOCKS5 escribe cuatro bytes por separado, y el cliente solo funciona cuando esas escrituras se combinan. Combinarlas puede cambiar el momento, pero TCP sigue sin prometer una lectura por escritura.

El cliente necesita un analizador de respuestas. Tras la selección de método y cualquier autenticación necesaria, una respuesta de comando SOCKS5 comienza con cuatro bytes: versión, resultado, byte reservado y tipo de dirección. La longitud restante depende de ese tipo de dirección.

Un analizador para .NET 7+ que solo necesite consumir una respuesta CONNECT correcta puede usar:

static async Task ReadConnectReply(Stream stream, CancellationToken ct)
{
    byte[] prefix = new byte[4];
    await stream.ReadExactlyAsync(prefix, ct);
    if (prefix[0] != 5 || prefix[2] != 0)
        throw new IOException("Invalid SOCKS5 reply");
    if (prefix[1] != 0)
        throw new IOException($"SOCKS5 request failed: {prefix[1]}");

    int remaining;
    switch (prefix[3])
    {
        case 1: remaining = 4 + 2; break;
        case 4: remaining = 16 + 2; break;
        case 3:
            byte[] length = new byte[1];
            await stream.ReadExactlyAsync(length, ct);
            remaining = length[0] + 2;
            break;
        default:
            throw new IOException("Unknown SOCKS5 address type");
    }
    await stream.ReadExactlyAsync(new byte[remaining], ct);
}

Los dos últimos bytes son el puerto vinculado. IPv4 necesita cuatro bytes; IPv6 necesita dieciséis; una dirección de dominio lleva un byte de longitud antes del nombre.

Proporciona un token de cancelación con el plazo de negociación previsto. ReadExactlyAsync maneja lecturas individuales más cortas y lanza una excepción cuando el flujo termina antes de la longitud solicitada. Si falla, descarta la conexión fallida por la ruta de limpieza de su propietario.

Si tiene éxito, detente en el límite de la respuesta. Los bytes posteriores pertenecen al protocolo de destino. Una lectura especulativa mayor debe conservar los bytes sobrantes en lugar de descartar el inicio del flujo de la aplicación.

Artículos relacionados