TCP и UDP через SOCKS5: что проходит через прокси и что остаётся на машине
Через SOCKS5 без оговорок проходит весь трафик по TCP: браузеры, парсеры, почтовые клиенты и любые программы, которые открывают соединение командой CONNECT сокетного протокола. UDP протоколом тоже описан, команда называется UDP ASSOCIATE, но поддержка на стороне клиентских программ встречается редко, поэтому игровой трафик, часть голосовых сессий и QUIC либо откатываются на TCP, либо уходят мимо посредника напрямую.
Ниже разобрано, чем два транспорта отличаются на уровне практики, как выглядит каждая команда протокола внутри, почему браузеры при включённом посреднике перестают пользоваться QUIC, что делать программе, которой UDP нужен по-настоящему, и какими командами проверить, каким транспортом реально идёт трафик прямо сейчас.
TCP и UDP: разница на уровне практики
TCP держит соединение. Стороны сначала договариваются трёхшаговым рукопожатием, потом обмениваются данными с нумерацией сегментов, подтверждениями и повторной отправкой потерянного. Порядок байтов на приёме совпадает с порядком отправки, потерянный кусок дойдёт со второй попытки, и приложение получает ровный поток. Плата за это простая: пока соединение живёт, обе стороны хранят его состояние, и любой промежуточный узел тоже держит запись о нём. Именно наличие состояния делает TCP удобным для посредника: прокси открывает своё соединение до цели и склеивает два потока.
UDP состояния не держит. Программа складывает данные в датаграмму, указывает адрес с портом и отправляет её в сеть. Подтверждений нет, нумерации нет, повторной отправки нет, порядок прихода может отличаться от порядка отправки, часть датаграмм теряется по дороге без всякого сигнала. Такой транспорт выбирают там, где задержка важнее полноты: запрос к DNS уложится в одну датаграмму и повторится сам, кадр голосового потока устареет раньше, чем дойдёт повторная копия, позиция игрока обновится следующим пакетом. Посреднику тут склеивать нечего, и работать с UDP приходится через отдельный механизм пересылки датаграмм.
| Свойство | TCP | UDP |
|---|---|---|
| Установка связи | Трёхшаговое рукопожатие | Отправка сразу, без подготовки |
| Порядок данных | Восстанавливается по номерам | Какой пришёл, такой и есть |
| Потери | Отправляются повторно | Теряются молча |
| Состояние соединения | Хранится на обеих сторонах | Отсутствует |
| Типичные задачи | Веб, почта, передача файлов, парсинг | Запросы к DNS, голос, игры, QUIC |
| Работа через посредника | Команда CONNECT, поддержка везде | Команда UDP ASSOCIATE, поддержка редкая |
Как SOCKS5 открывает соединение по TCP
Сокетный протокол начинается с короткого приветствия. Клиент подключается к посреднику по TCP на его порт и отправляет байт версии 0x05, число предлагаемых способов проверки доступа и их коды. Способа два: 0x00 без проверки, для пакетов с привязкой своего адреса, и 0x02 с парой логина и пароля. Посредник отвечает двумя байтами и называет выбранный способ.
клиент -> 05 02 00 02 версия 5, два способа: без проверки и пара
прокси -> 05 02 выбран способ с логином и паролем
клиент -> 01 08 user5521 06 pf39kd подстрока проверки доступа
прокси -> 01 00 пара принята
После проверки идёт сам запрос. Он занимает от 10 байтов и состоит из версии, кода команды, резервного нуля, типа адреса, самого адреса и порта в двух байтах:
клиент -> 05 01 00 03 0B example.com 01 BB
| | | | | |
| | | | | порт 443 (0x01BB)
| | | | длина имени и само имя
| | | тип адреса: 03 это доменное имя
| | резерв
| команда CONNECT
версия протокола
прокси -> 05 00 00 01 B9 18 57 0E 1F 40 успех, адрес и порт на стороне выхода
Ответный байт 0x00 означает успех. Дальше то же самое TCP-соединение превращается в двусторонний канал: клиент шлёт байты, посредник перекладывает их на своё соединение с целью и возвращает ответ обратно. Разбора содержимого тут нет вообще, потому что сокетный протокол работает уровнем ниже прикладных заголовков. Мы держим оба способа доступа на одних и тех же адресах, поэтому прокси SOCKS5 для программ и скриптов подключаются и по привязке, и парой логина с паролем без смены списка.
Тип адреса 0x03 заслуживает отдельной строки. С ним посреднику уходит доменное имя вместо готового адреса, и тогда имя разрешает выходная сторона. Локальный резолвер машины при этом про целевой домен ничего не узнаёт, и запрос к DNS уходит вместе с трафиком. Это ровно тот случай, когда UDP-запрос к серверу имён вообще не покидает машину клиента отдельным пакетом.
Команды протокола и что каждая делает
Команд в сокетном протоколе три, и они закрывают три разных сценария работы. Байт команды стоит вторым в запросе.
| Байт | Команда | Что делает | Поддержка в программах |
|---|---|---|---|
0x01 | CONNECT | Открывает TCP-соединение до узла и порта | Есть везде, основной режим |
0x02 | BIND | Просит посредника принять входящее соединение | Встречается у активного режима FTP |
0x03 | UDP ASSOCIATE | Открывает канал пересылки датаграмм | Реализована у части клиентов |
Ответный код посредника тоже стоит разобрать: по нему видно, где именно оборвалась установка.
| Код | Значение | Что проверить |
|---|---|---|
0x00 | Успех, канал открыт | Ничего, работа пошла |
0x01 | Общая ошибка посредника | Активность пакета и доступ |
0x02 | Соединение запрещено правилами | Порт назначения и права доступа |
0x03 | Сеть недоступна | Доступность цели с выходной стороны |
0x04 | Узел недоступен | Имя узла и его адрес |
0x05 | Целевой узел отклонил соединение | Открыт ли порт на цели |
0x07 | Команда не поддерживается | Не пытается ли программа звать UDP ASSOCIATE |
0x08 | Тип адреса не поддерживается | Формат адреса в настройках программы |
Мы смотрим на этот байт первым, когда программа отказывается работать через сокетного посредника. Код 0x07 встречается чаще остальных при разборе обращений по UDP. Программа просит канал для датаграмм, получает отказ по команде и дальше ведёт себя по-разному: одна откатывается на TCP молча, другая пишет в лог ошибку сокета, третья продолжает слать датаграммы напрямую с адреса машины.
Как устроен UDP ASSOCIATE
Механизм пересылки датаграмм устроен сложнее обычного соединения, и в этой сложности прячется ответ на вопрос, почему поддержка встречается редко.
Разбираем порядок по шагам. Клиент открывает управляющее TCP-соединение с посредником и отправляет команду 0x03. В полях адреса он указывает ту пару адреса и порта, с которой сам будет слать датаграммы, либо нули, если заранее её не знает. Посредник отвечает успехом и называет собственный адрес и порт для приёма датаграмм. Дальше клиент шлёт UDP-пакеты на этот порт, причём каждая датаграмма получает служебную приставку: два нулевых байта резерва, байт номера фрагмента, тип адреса, адрес цели, порт цели и уже потом сами данные.
+-----+------+------+----------+----------+----------+
| RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+-----+------+------+----------+----------+----------+
| 2 | 1 | 1 | по типу | 2 | переменн |
+-----+------+------+----------+----------+----------+
Отсюда четыре требования, которые программа обязана выполнить. Первое: держать управляющее TCP-соединение открытым всё время работы, потому что его закрытие рвёт канал датаграмм. Второе: заворачивать каждую датаграмму в приставку и снимать её с ответных пакетов. Третье: слать датаграммы на адрес и порт, которые назвал посредник, вместо адреса цели. Четвёртое: обрабатывать поле фрагментации, которое почти нигде не реализовано и обычно остаётся нулевым.
Обычный сетевой стек операционной системы всего этого не умеет. Приложение вызывает sendto и ждёт, что датаграмма уйдёт по указанному адресу. Чтобы включить сокетного посредника, разработчику приходится переписывать работу с сокетами вручную или подключать библиотеку-обёртку. Поэтому поддержка живёт точечно: она есть у части торрент-клиентов, у отдельных сетевых утилит и у прослоек, которые перехватывают трафик целиком. Браузеры, парсеры и почтовые клиенты обходятся TCP и команду 0x03 не зовут.
Что вообще пробует ходить по UDP
| Вид трафика | Транспорт | Что происходит при работе через посредника |
|---|---|---|
| Веб-страницы по HTTP и HTTPS | TCP | Проходят полностью, командой CONNECT |
| Почта SMTP, IMAP, POP3 | TCP | Проходит полностью |
| Запросы к DNS | UDP по умолчанию | Уходят на сторону выхода, если имя передано типом 0x03 |
| QUIC и HTTP третьей версии | UDP на порту 443 | Браузер откатывается на TCP при включённом посреднике |
| Игровой трафик | UDP на своих портах | Проходит только у клиентов с поддержкой UDP ASSOCIATE |
| Голос и видео в мессенджерах | UDP с откатом на TCP | Обычно переключаются на TCP и работают |
| Торренты | TCP плюс UDP для обмена узлами | Соединения идут по TCP, обмен узлами частично |
| Проверка узла командой ping | ICMP | Через сокетный протокол не передаётся |
Запросы к DNS стоят в этом списке первыми по частоте. Программа резолвит имя, получает адрес и только потом открывает соединение. Сокетный протокол умеет забирать этот шаг себе: клиент передаёт имя типом адреса 0x03, и разрешение уходит на выходную сторону вместе с трафиком. Когда программа резолвит сама, датаграмма к серверу имён покидает машину напрямую, и картина по адресу выходит неполной. Разбор этого случая с проверкой собран в статье про утечку запросов к DNS.
Игровой трафик через сокетного посредника проходит редко и упирается ровно в то, о чём написано выше: клиент игры пользуется системным стеком и про команду 0x03 не знает. Голосовые и видеозвонки в мессенджерах устроены дружелюбнее, потому что почти все они умеют откатываться на TCP при недоступности датаграмм. Обмен узлами в торрентах работает частично: соединения с пирами идут по TCP, а часть служебного обмена по UDP остаётся за бортом.
Почему браузеры откатываются с QUIC на TCP
QUIC несёт HTTP третьей версии поверх UDP на том же порту 443. Сервер объявляет о поддержке заголовком alt-svc: h3=":443"; ma=86400, браузер запоминает объявление и следующую сессию пробует поднять по датаграммам. Выигрыш там в скорости установки: рукопожатие транспорта и шифрования складываются в один обмен.
Включённый посредник эту схему выключает. Chromium при заданном прокси перестаёт пробовать QUIC для проксируемых адресов, потому что канал датаграмм через посредника ему недоступен. Firefox ведёт себя так же. Результат один: браузер игнорирует объявление alt-svc, открывает обычное TCP-соединение и работает по HTTP второй версии внутри туннеля. Для сбора страниц, работы с кабинетами и проверки выдачи разницы в результате нет, отличается только время установки первой сессии.
В логах откат виден по трём признакам. В chrome://net-export при включённом посреднике отсутствуют события QUIC_SESSION, зато присутствуют HTTP2_SESSION. На вкладке сети инструментов разработчика колонка протокола показывает h2 вместо h3. На стороне машины tcpdump не видит исходящих датаграмм на порт 443 в сторону целевых узлов.
# при работе без посредника исходящий QUIC видно сразу
tcpdump -n -i any 'udp and port 443' -c 20
# при включённом посреднике этот фильтр остаётся пустым,
# зато соединение до прокси видно обычным фильтром
tcpdump -n -i any 'tcp and port 1080' -c 20
Иногда браузер всё же пробует датаграммы в обход настроек, и тогда часть трафика уходит с адреса машины. Проверяется это тем же фильтром tcpdump. Если исходящие датаграммы на 443 есть, QUIC выключается флагом запуска или настройкой профиля, после чего весь веб идёт через посредника по TCP. Мы держим адреса на собственном оборудовании с постоянным откликом, и серверные адреса для длинных сессий выдерживают долгие TCP-соединения без разрывов, которые обычно и толкают браузер пробовать другой транспорт.
Что делать, если приложению нужен UDP
Первый путь самый прямой: проверить настройки самой программы. Часть клиентов имеет отдельную галочку вроде «использовать прокси для UDP» или «резолвить имена через прокси». Когда такая галочка есть, поддержка команды 0x03 в клиенте реализована, и работа пойдёт без обходных манёвров.
Второй путь это перевод задачи на TCP. Многие протоколы имеют TCP-вариант, и включается он одной настройкой. Запросы к DNS ходят по TCP на том же порту 53 и через DoH по HTTPS. Голосовые сессии в мессенджерах переключаются сами. Игровые клиенты иногда предлагают запасной режим соединения. Такой перевод закрывает большую часть задач, потому что рабочие сценарии со сбором данных, кабинетами и почтой построены на TCP целиком. Мы держим сокетный режим на всех адресах пула, и пакет с доступом по SOCKS5 обслуживает эти сценарии тем же списком, что и веб.
Третий путь для веба: туннельный режим по HTTPS. Он устроен на TCP по определению и пропускает произвольные порты, что закрывает почтовые и прикладные протоколы поверх шифрования. Разбор механизма собран в материале про туннель CONNECT и работу с шифрованием, а сам режим включён на всех адресах пакета, где доступны прокси HTTPS с туннельным режимом.
Четвёртый путь подходит там, где переписать программу нельзя. Локальная прослойка принимает трафик приложения и сама говорит с посредником по сокетному протоколу, разворачивая приставки датаграмм. Настройка занимает время и требует прав на машине, зато приложение при этом не меняется ни строкой.
| Ситуация | Что сделать | Результат |
|---|---|---|
| В клиенте есть галочка UDP через прокси | Включить её | Датаграммы идут через посредника |
| Протокол имеет TCP-вариант | Переключить транспорт в настройках | Трафик проходит обычной командой CONNECT |
| Нужен веб с произвольными портами | Взять туннельный режим по HTTPS | Работает поверх TCP, порт любой |
| Программу менять нельзя | Поставить локальную прослойку | Приложение работает без правок |
| Нужны только запросы к DNS | Передавать имя типом адреса 0x03 | Разрешение уходит на выходную сторону |
Как проверить, каким транспортом идёт трафик
Проверка занимает одну команду и отвечает точнее любых предположений. Мы берём её за первый шаг разбора, потому что она сразу отделяет вопросы транспорта от вопросов доступа. На Windows список соединений с разбивкой по транспорту выдаёт netstat:
:: соединения по TCP с номерами процессов
netstat -ano -p TCP | findstr :1080
:: слушающие и активные сокеты UDP
netstat -ano -p UDP
На Linux и macOS то же самое короче делает ss. Ключ -t показывает TCP, ключ -u показывает UDP, ключ -p добавляет имя процесса:
# что открыто по TCP в сторону посредника
ss -tnp | grep 1080
# какие сокеты UDP держит машина прямо сейчас
ss -unap
# сводка по числу соединений каждого транспорта
ss -s
Полную картину даёт tcpdump: он показывает реальные пакеты на интерфейсе, включая те, что программа отправила мимо настроек. Три фильтра закрывают типовой разбор:
# всё, что уходит с машины по UDP, кроме запросов к серверу имён
tcpdump -n -i any 'udp and not port 53' -c 40
# запросы к DNS отдельно: видно, резолвит машина сама или нет
tcpdump -n -i any 'udp port 53' -c 20
# трафик до посредника: тут должно быть видно основной поток
tcpdump -n -i any 'host 185.24.87.14 and port 1080' -c 40
Читается вывод так. Строки с IP ... > ...: UDP, length означают датаграммы, ушедшие напрямую. Строки с флагами Flags [S], [.], [P.] относятся к TCP. Если весь основной обмен виден на адресе посредника, а датаграмм наружу нет, трафик идёт через пул полностью. Мы проверяем это первым делом, когда программа ведёт себя странно при рабочем на вид подключении.
| Инструмент | Команда | Что показывает |
|---|---|---|
| netstat | netstat -ano -p UDP | Сокеты UDP и процессы на Windows |
| ss | ss -tnp и ss -unap | Разбивка соединений по транспорту |
| ss сводкой | ss -s | Число соединений каждого вида |
| tcpdump | tcpdump -n 'udp and not port 53' | Датаграммы, ушедшие мимо посредника |
| curl | curl -v --socks5-hostname ... | Работу команды CONNECT сокетного протокола |
Отдельно проверяем сам факт работы через сокетный протокол. Ключ --socks5-hostname заставляет curl передать имя посреднику типом 0x03, и в подробном выводе видно строку SOCKS5 request granted:
curl -v --socks5-hostname user5521:[email protected]:1080 https://example.com -o /dev/null
Нагрузка и число соединений при работе по TCP
Раз основной трафик идёт по TCP, планирование нагрузки строится вокруг числа одновременных соединений. Каждый вызов команды CONNECT в сокетном протоколе занимает один поток, и парсер с настройкой в 200 потоков держит до 200 открытых каналов через пул. Стандартные пакеты дают до 1000 потоков, корпоративный до 3000, пакеты по потокам не складываются. При двух привязанных адресах общее число делится между ними пополам, и этот момент забывают чаще остальных.
Трафик считать не нужно вовсе: он безлимитный на всех вариантах, поэтому объём выкачанных страниц на выбор пакета не влияет. Для долгих прогонов, где заранее неизвестно, сколько данных придёт, спокойнее всего работают пакеты с безлимитным трафиком.
Число адресов в работе тоже стоит прикинуть заранее. Пул держится в районе 12 000 активных адресов, ротация внутри него автоматическая, список обновляется в реальном времени. Один и тот же наш список одинаково подходит и для сокетного режима, и для обычного веба, потому что протокол выбирается строкой подключения. Состав пакета целиком описан там, где оформляется доступ к пулу IPv4 и SOCKS5.
Для сокетного режима порт в строке подключения обычно отличается от порта HTTP-режима, и это единственное, что меняется при переключении. Логин и пароль остаются прежними, адреса те же, список перечитывать не нужно. Программа, которая умеет и то, и другое, переключается правкой одного поля в настройках, и работа продолжается без паузы.
Частые вопросы
Какой тип прокси выбрать, если программа умеет и HTTP, и SOCKS?
В пакете доступны IPv4, HTTP, HTTPS, SOCKS4 и SOCKS5, выбор идёт строкой подключения. Мы рекомендуем SOCKS5: он работает уровнем ниже прикладных заголовков, умеет передавать доменное имя на выходную сторону и подходит для произвольных портов. Доплачивать за протокол не нужно, адреса используются одни и те же.
Пойдёт ли моя программа по UDP через ваши прокси?
Заранее предугадать поведение каждой программы нельзя, поэтому перед покупкой доступен бесплатный тест длительностью до 2 часов под ваш запрос. Проверка занимает несколько минут: запускается тот же клиент с теми же настройками, а tcpdump с фильтром по датаграммам показывает, зовёт ли программа команду 0x03 или обходится соединениями по TCP.
Чем отличается SOCKS4 от SOCKS5 в части транспорта?
SOCKS4 работает только с соединениями по TCP и принимает адрес в числовом виде. SOCKS5 добавляет проверку доступа парой логина и пароля, приём доменных имён отдельным типом адреса и команду для пересылки датаграмм. Оба варианта включены в пакет, рекомендуется пятая версия.
Сколько соединений можно держать одновременно?
Число упирается в лимит потоков пакета: стандартные пакеты дают до 1000, корпоративный до 3000. Пакеты по потокам не складываются. При двух привязанных адресах общее число делится между ними пополам, поэтому вторую привязку разумно держать пустой, пока она реально не понадобится.
Остальные разборы по протоколам собраны рядом: чем отличаются HTTP, HTTPS, SOCKS4 и SOCKS5 с таблицей по каждому режиму, как идёт письмо по протоколу SMTP от команды до кода ответа, как принимать почту по IMAP и POP3 через посредника. Разбор полей строки подключения, включая порт сокетного режима, собран в материале про строку подключения по полям.