Кратко

  • UDP ASSOCIATE создавал состояние реле внутри TCP-соединения после согласования метода и прекращал его при закрытии именно этого соединения. Поток задавал срок контекста, но не переносил UDP-данные и не делал их надёжными.
  • Возвращённый адрес реле, ожидаемый IP клиента, назначение в каждой дейтаграмме, метод защиты и очередь фрагментов оставались разными свидетельствами. Ни одно не доказывало само по себе пользователя или конечную доставку.

Последний пакет не мог закрыть и не мог продлить сессию

Представим приложение, которое через SOCKS отправляет по UDP запросы или медиаданные. Управляющий TCP-сокет исчезает из-за перезапуска процесса, смены мобильной сети либо утраты состояния межсетевым экраном. Через мгновение на старый UDP-порт приходит ещё одна правильно оформленная дейтаграмма. Реле знает её назначение и технически способно переслать.

RFC 1928, опубликованный в марте 1996 года, запрещал принимать эту возможность за полномочие. UDP-ассоциация завершается, когда завершается TCP-соединение, по которому поступил запрос UDP ASSOCIATE. Свежий трафик показывает знание порта, но не сохранность контекста, где сервер проверил метод и политику.

Полезные данные не шли через TCP. Они оставались самостоятельными UDP-дейтаграммами с возможной потерей, перестановкой и дублированием. Поток переносил согласование и акт создания локального состояния; его конец давал этому состоянию наблюдаемую точку отзыва.

Так соединённый транспорт предоставил бессоединительному сервису время жизни, не навязав ему собственные гарантии доставки.

Нулевая пара не открывала реле для всех

Сначала клиент предлагал методы аутентификации по TCP. Сервер выбирал один, стороны выполняли его подпротокол, и только затем клиент отправлял CONNECT, BIND либо UDP ASSOCIATE.

В UDP-запросе можно было указать адрес и порт, которые клиент ожидал использовать как источник. Сервер имел право ограничить ими доступ. Если значения ещё не были известны, клиент обязан был передать нулевые адрес и порт.

Нули обозначали отсутствие конкретного утверждения, а не универсальное доверие. Серверу по-прежнему требовалось вывести допустимый источник из текущего соединения, наблюдения и локальной конфигурации.

Успешный ответ содержал BND.ADDR и BND.PORT — UDP-точку, куда клиент должен отправлять SOCKS-конверты. У многосетевого сервера она могла не совпадать с адресом TCP-управления. Это был вход реле, а не имя удалённой цели.

В результате у одной операции существовали три адресные роли: TCP-сервер, выделенный UDP-вход и удалённое назначение внутри конкретной дейтаграммы. Запись одного «адреса прокси» стирает различие между недоступностью управления, недоступностью реле и отказом дальней цели.

Назначение принадлежало отдельной дейтаграмме

SOCKS-заголовок перед каждой UDP-нагрузкой включал reserved, FRAG, тип адреса, адрес и порт назначения. Одна ассоциация могла обслуживать разные цели. Реле истолковывало намерение и применяло политику заново для каждого конверта.

Ответ удалённого узла оно оборачивало той же грамматикой. Клиент получал источник, наблюдавшийся посредником. Это была полезная корреляция, а не криптографическое доказательство личности отправителя.

Реле могло подтвердить локальный приём, решение политики и попытку отправки. Оно не видело в одиночку, доставил ли путь пакет, приняло ли его приложение и наступил ли нужный эффект. Даже ответ добавлял новое наблюдение, но не превращал всю цепочку в гарантию «ровно один раз».

После закрытия TCP значение BND.ADDR могло остаться в памяти клиента. Правильный конверт, отправленный на старый вход, не создавал ассоциацию заново. Сохранённая координата не была сохраняющимся правом.

Проверка IP защищала границу, а не устанавливала личность

RFC 1928 требовал, чтобы UDP-реле получило от SOCKS-сервера ожидаемый IP клиента и молча отбрасывало дейтаграммы с другого адреса. Иной хост не должен был легко использовать чужой контекст.

Смысл проверки был ограничен. За одним IP могут находиться разные устройства и пользователи NAT, на одном хосте работают разные процессы, при мобильности адрес меняется. Сравнение говорит о сетевом атрибуте, видимом реле, а не о человеке или программе.

Аутентификацию определял выбранный метод. RFC 1929 описывал имя пользователя и пароль, но передавал их открытым текстом в подпротоколе и не рекомендовал метод там, где возможен перехват. RFC 1961 добавлял GSS-API, способный согласовать аутентификацию, целостность сообщений и необязательную конфиденциальность.

Поэтому надпись «SOCKS5» не определяла единую степень защиты. Журнал должен называть фактически выбранный метод, его результат, уровень и охват. Совпадение source IP не могло дописать свойства, которых этот метод не обеспечил.

FRAG создавал короткую и отзывную память

В SOCKS UDP-конверте FRAG=0 означал самостоятельную дейтаграмму. Значения от 1 до 127 обозначали место фрагмента, а старший бит — конец последовательности. Это была не IP-фрагментация, а собственное состояние реле.

Поддержка сборки оставалась необязательной. Реализация без неё должна была отбрасывать любой ненулевой FRAG. Поддерживающий получатель содержал очередь и таймер. По истечении срока фрагменты уничтожались; новый номер ниже максимального уже обработанного тоже сбрасывал очередь. Таймер не мог быть короче пяти секунд, а спецификация советовала по возможности избегать фрагментации.

ACK и обязательная повторная передача от этого не появлялись. Полная очередь лишь позволяла восстановить данные локально и перейти к следующему решению. Она не была квитанцией о последующей доставке.

В эксплуатации нужны отдельные причины: несовпавший источник, неподдерживаемый FRAG, возврат номера, истечение таймера, запрет назначения, потеря после выхода и отсутствие реакции приложения. Один показатель «UDP не прошёл» скрывает и место, и владельца исправления.

TCP-разрыв не оставлял бесхозного полномочия

Один idle timeout освобождает ресурсы, но не объясняет тишину. Продление при каждом пакете решает обратную задачу слишком щедро: тот, кто знает порт, фактически получает право поддерживать чужую ассоциацию.

TCP-контроль был промежуточным доказательством. Он не идентифицировал человека и не гарантировал здоровье приложения, зато сервер сам наблюдал его и мог связать с выбранным методом, политикой и UDP-entry. Исчезновение связи означало отзыв; дальнейшая работа требовала нового запроса.

Это ухудшало доступность при коротком TCP-сбое, усложняло мобильность и требовало держать socket. Но граница была проверяемой, а восстановление — явным. Бесхозное реле, продлеваемое плохо атрибутируемыми пакетами, давало бы более гладкий сервис ценой неясной ответственности.

Мост между IPv4 и IPv6 не стал сетевым маршрутизатором

Информационный RFC 3089 в 2001 году описал SOCKS-шлюз между IPv4 и IPv6. Он завершал коммуникацию с обеих сторон на прикладном уровне и мог поручить разрешение имени двухстековому узлу, если старое приложение не умело хранить IPv6-адрес.

Шлюз наследовал CONNECT, BIND и UDP ASSOCIATE, но не превращался в прозрачный IP-router. Запрошенное имя, результат DNS, управляющая ассоциация и передаваемые данные оставались самостоятельными объектами.

Историческая сила механизма заключалась в минимальном договоре. Общая спецификация давала проверяемую форму запроса. Посредник сохранял локальное право разрешить или отказать. Его наблюдение не становилось автоматически истиной о следующем участке пути.

Источники