Odpowiedzi SOCKS5 w .NET 7+: obsługa częściowych odczytów TCP
Odczytaj stały prefiks odpowiedzi polecenia SOCKS5 i zależny od ATYP sufiks, nie polegając na granicach zapisu TCP serwera.
Serwer SOCKS5 zapisuje cztery bajty osobno, a klient działa dopiero po połączeniu tych zapisów. Połączenie może zmienić timing, ale TCP nadal nie obiecuje jednego odczytu na zapis.
Klient potrzebuje parsera odpowiedzi. Po wyborze metody i ewentualnym uwierzytelnieniu odpowiedź polecenia SOCKS5 zaczyna się od czterech bajtów: wersja, wynik, bajt zarezerwowany i typ adresu. Pozostała długość zależy od tego typu adresu.
Parser dla .NET 7+, który musi zużyć tylko udaną odpowiedź CONNECT, może użyć:
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);
}
Ostatnie dwa bajty to powiązany port. IPv4 wymaga czterech bajtów; IPv6 wymaga szesnastu; adres domeny niesie bajt długości przed nazwą.
Podaj token anulowania z zamierzonym terminem negocjacji. ReadExactlyAsync obsługuje krótsze pojedyncze odczyty i zgłasza wyjątek, gdy strumień kończy się przed żądaną długością. W razie niepowodzenia odrzuć nieudane połączenie ścieżką sprzątania jego właściciela.
Po sukcesie zatrzymaj się na granicy odpowiedzi. Bajty po niej należą do protokołu docelowego. Większy odczyt spekulatywny musi zachować nadmiarowe bajty, zamiast wyrzucać początek strumienia aplikacji.