Un server SOCKS5 scrive quattro byte separatamente, e il client funziona solo dopo che tali scritture sono state combinate. Combinarle può cambiare i tempi, ma TCP continua a non promettere una lettura per scrittura.

Il client ha bisogno di un parser della risposta. Dopo la selezione del metodo e l'eventuale autenticazione richiesta, una risposta al comando SOCKS5 inizia con quattro byte: versione, risultato, byte riservato e tipo di indirizzo. La lunghezza rimanente dipende da quel tipo di indirizzo.

Un parser per .NET 7+ che deve consumare solo una risposta CONNECT riuscita può usare:

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);
}

Gli ultimi due byte sono la porta associata. IPv4 richiede quattro byte; IPv6 ne richiede sedici; un indirizzo di dominio porta un byte di lunghezza prima del nome.

Fornisci un token di annullamento con la scadenza di negoziazione prevista. ReadExactlyAsync gestisce letture individuali più corte e genera un'eccezione quando il flusso termina prima della lunghezza richiesta. In caso di errore, scarta la connessione fallita tramite il percorso di pulizia del suo proprietario.

In caso di successo, fermati al confine della risposta. I byte successivi appartengono al protocollo di destinazione. Una lettura speculativa più ampia deve conservare i byte in eccesso invece di scartare l'inizio del flusso applicativo.

Articoli correlati