SOCKS5 отговори в .NET 7+: работа с частични TCP четения
Прочетете фиксирания префикс на отговора на SOCKS5 команда и зависимия от ATYP суфикс, без да разчитате на границите на TCP запис от сървъра.
SOCKS5 сървърът записва четири байта поотделно и клиентът работи само след като тези записи се обединят. Обединяването може да промени времето, но TCP все още не обещава едно четене на запис.
Клиентът се нуждае от анализатор на отговора. След избор на метод и всяка необходима автентикация отговорът на SOCKS5 команда започва с четири байта: версия, резултат, резервиран байт и тип адрес. Останалата дължина зависи от този тип адрес.
Анализатор за .NET 7+, който трябва да консумира само успешен CONNECT отговор, може да използва:
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);
}
Последните два байта са свързаният порт. IPv4 изисква четири байта; IPv6 изисква шестнадесет; домейн адресът носи байт за дължина преди името си.
Подайте токен за отмяна със предвидения краен срок за договаряне. ReadExactlyAsync обработва по-кратки отделни четения и хвърля изключение, когато потокът свърши преди заявената дължина. При неуспех изхвърлете неуспешната връзка през пътя за почистване на собственика ѝ.
При успех спрете на границата на отговора. Байтовете след нея принадлежат на протокола на дестинацията. По-голямо спекулативно четене трябва да запази излишните байтове, вместо да изхвърли началото на приложния поток.