Server SOCKS5 zapisuje čtyři bajty odděleně a klient funguje jen poté, co se tyto zápisy spojí. Spojení může změnit časování, ale TCP stále neslibuje jedno čtení na zápis.

Klient potřebuje parser odpovědi. Po volbě metody a případné autentizaci začíná odpověď příkazu SOCKS5 čtyřmi bajty: verze, výsledek, rezervovaný bajt a typ adresy. Zbývající délka závisí na tomto typu adresy.

Parser pro .NET 7+, který má spotřebovat jen úspěšnou odpověď CONNECT, může použít:

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

Poslední dva bajty jsou připojený port. IPv4 vyžaduje čtyři bajty; IPv6 jich vyžaduje šestnáct; adresa domény nese před názvem bajt délky.

Předejte token zrušení s plánovaným termínem vyjednávání. ReadExactlyAsync zvládá kratší jednotlivá čtení a vyhodí výjimku, když proud skončí dříve než požadovaná délka. Při selhání zahoďte neúspěšné připojení cestou úklidu jeho vlastníka.

Při úspěchu se zastavte na hranici odpovědi. Bajty za ní patří protokolu cíle. Větší spekulativní čtení musí přebytečné bajty zachovat, místo aby zahodilo začátek aplikačního proudu.

Související články