Ориентация главы

Модуль proxy превращает «установление соединения через прокси» в инженерное решение: размещённый дозвон (xnetproxydial — размещённая цепочка DNS + рукопожатие + соединение: при неудаче все созданные ресурсы автоматически возвращаются); SOCKS5 (машина состояний инкрементного рукопожатия greeting/auth/CONNECT — чистый протокольный слой); HTTP CONNECT (строка метода + заголовки + подтверждение 2xx); типы адресов (IPv4/IPv6/домен — три представления цели, фирменная черта протоколов прокси: цель может быть доменом — резолв на стороне прокси); формы аутентификации (без/логин-пароль). Пример proxy_dial_http_connect — полная боевая позиция.

70.1引入

В корпоративных сетях и по требованиям комплаенса исходящие соединения идут через прокси: клиент → прокси → цель. Ручная реализация — трёхэтапная каторга: соединиться с прокси (резолв адреса + установление TCP) → протокол рукопожатия (SOCKS5 два оборота или CONNECT один — машины состояний двух кодировок, бинарной и текстовой) → только после успеха «канал до цели». Падение любого этапа — уже созданный TCP, уже отправленное рукопожатие — всё обязано корректно вернуться (очистка частичных состояний — рассадник багов).

Ответ модуля proxy: xnetproxydial размещает всю цепочку — дозвон (адрес прокси) → рукопожатие (машина состояний протокола) → доставка (Stream до цели); автоматический возврат при неудаче (смысл «размещённости» — то же обещание примера tcp_dial золотого стандарта, повторённое для сценария прокси). Детали протоколов (кодирование сообщений/машина состояний/типы адресов) заперты в модуле — пользователь заявляет «через какой прокси, к какой цели», остальное делает модуль.

70.2概念

Формы дозвола через прокси

ФормаВходПримечания
Конфигурационнаяконфиг прокси + дозвонТип/адрес/аутентификация — всё в конфигурации
Инкрементное рукопожатиепошаговая генерация/потребление сообщенийЧистый протокольный слой (пример socks5)
HTTP CONNECTстрока метода + подтверждениеТекстовая протокольная позиция

Конфигурационная — самая употребительная форма: объявить прокси (тип + адрес + опциональная аутентификация) и цель (xnetaddr или домен) — один дозвон доставляет Stream. Инкрементное рукопожатие — для особых сценариев (вставка собственного рукопожатия, тесты/обучение): сообщения greeting/CONNECT генерируются по шагам, ответы потребляются по шагам — машина состояний обнажена, но управляема.

Инкрементное рукопожатие SOCKS5

1greeting05 01 00 (версия 5 / 1 метод / без аутентификации) -> ответ выбирает метод
2auth (опционально)суб-согласование логин-пароль (RFC 1929)
3CONNECT05 01 00 + тип адреса (01 v4/03 домен/04 v6) + цель + порт
4ответ05 00 (успех) + связанный адрес — с этого момента канал прямо в цель

Доменная цель — квинтэссенция протоколов прокси (03 0E 6F 72 69 ... примера socks5 — 03 = тип домен, 0E = 14 байт, далее байты домена): цель передаётся прокси как домен — резолв на стороне прокси (клиент не касается DNS цели — ценность для интрасетевых доменов и защиты от утечек). Три типа адресов + порт образуют единый xnetproxyendpoint.

HTTP CONNECT

Текстовая протокольная позиция: CONNECT host:port HTTP/1.1 + заголовок Host (+заголовки аутентификации) → прокси отвечает 2xx → канал прямо. Выбор против SOCKS5 обычно решает среда (политика файрвола / тип прокси в сети) — XRT поставляет оба (примеры http_connect и socks5 симметричны).

Аутентификация и безопасность

Аутентификация: SOCKS5 без/логин-пароль (RFC 1929); HTTP Basic/кастомные заголовки. Безопасность канала: сам прокси не шифрует — TLS over прокси (TLS-рукопожатие после CONNECT — композиция главы 82); цепочки прокси: теоретически сцепляемы (дозвон через прокси A к прокси B к цели — композиция на уровне конфигурации).

Семантика размещённости

Размещённость xnetproxydial = прокси-версия tcp_dial золотого стандарта: DNS (домен прокси) → TCP (до прокси) → рукопожатие (протокол) → доставка; неудача на любом этапе — объект состояния xnetproxydialstate записывает, докуда дошли, созданные ресурсы возвращаются все; успех — жизненный цикл Stream неотличим от прямого соединения (последующие TLS/HTTP не чувствуют разницы — прокси прозрачен для верхних слоёв).

70.3示例

Полная программа: инкрементное рукопожатие SOCKS5

Из примера репозитория examples/network/proxy_socks5/main.c — генерация сообщений чистого протокольного слоя:

examples/network/proxy_socks5/main.c源码 ↗















 */


#include <stdio.h>
#include <xrt.h>



/* 打印一个二进制协议报文,便于观察纯协议层生成的线路内容。 */
static void printPacket(cstr sName, xnetspan Packet)
{
	printf("%s:", sName);
	for ( size_t i = 0; i < Packet.Size; i++ ) {
		printf(" %02X", (unsigned)Packet.Data[i]);
	}
	printf("\n");
}



/* 演示不依赖 Socket 的 SOCKS5 增量握手。 */
int main(void)
{
	static const uint8 MethodReply[] = { 0x05, 0x00 };
	xnetproxyhandshakeconfig HandshakeConfig;
	xnetproxyconfig ProxyConfig;
	xnetproxyhandshake* pHandshake;
	xnetproxy* pProxy;
	xnetspan Output;
	xnetbuf Input;

	xrtNetProxyConfigInit(&ProxyConfig);
	ProxyConfig.Host = XRT_STR_LITERAL("127.0.0.1");
	ProxyConfig.Port = 1080;
	pProxy = xrtNetProxyCreate(&ProxyConfig);
	if ( pProxy == NULL ) {
		return 1;
	}

	xrtNetProxyHandshakeConfigInit(&HandshakeConfig);
	HandshakeConfig.Proxy = pProxy;
	HandshakeConfig.TargetHost = XRT_STR_LITERAL("origin.example");
	HandshakeConfig.TargetPort = 443;
	pHandshake = xrtNetProxyHandshakeCreate(&HandshakeConfig);
	xrtNetProxyRelease(pProxy);
	if ( (pHandshake == NULL) || !xrtNetBufInit(&Input, NULL) ) {
		xrtNetProxyHandshakeDestroy(pHandshake);
		return 1;
	}

	if ( !xrtNetProxyHandshakeOutput(pHandshake, &Output) ) {
		xrtNetBufClear(&Input);
		xrtNetProxyHandshakeDestroy(pHandshake);
		return 1;
	}
	printPacket("greeting", Output);
	(void)xrtNetProxyHandshakeSent(pHandshake, Output.Size);

	if ( !xrtNetBufAppend(&Input, MethodReply, sizeof(MethodReply)) ||
		(xrtNetProxyHandshakeStep(pHandshake, &Input) !=
		 XNET_PROXY_HANDSHAKE_WRITE) ||
		!xrtNetProxyHandshakeOutput(pHandshake, &Output) ) {
		xrtNetBufClear(&Input);
		xrtNetProxyHandshakeDestroy(pHandshake);
		return 1;
	}
	printPacket("connect", Output);

	xrtNetBufClear(&Input);
	xrtNetProxyHandshakeDestroy(pHandshake);
	return 0;
}
$ gcc -O1 -DXRT_MODULE_ALL -I single -include xrt.h impl.c examples/network/proxy_socks5/main.c -lws2_32 -liphlpapigreeting: 05 01 00connect: 05 01 00 03 0E 6F 72 69 ...

Что только что произошло. (1) Сообщение greeting 05 01 00 — версия 5, один метод (00 = без аутентификации) — минимальный трёхбайтовый приветствие в провод. (2) Сообщение CONNECT 05 01 00 03 0E 6F 72 69 ... — 05 версия / 01 CONNECT / 00 резерв / 03 тип домен / 0E четырнадцать байт / первые байты домена (ori... — доменная цель примера) — бинарное кодирование типов адресов видно напрямую. (3) Печатается «бинарник, готовый к выводу в провод» — инкрементное рукопожатие делает байтовый поток протокольного слоя явным (позиция обучения/отладки); продакшн-код использует конфигурационный дозвон (протокольный слой заперт). Пример proxy_tour — тур по всем интерфейсам.

Полная программа: HTTP CONNECT в бою

Из examples/network/proxy_dial_http_connect/main.c — полная цепочка дозвола через прокси:

examples/network/proxy_dial_http_connect/main.c源码 ↗
















 */


/* 复用托管代理示例,只切换为 HTTP CONNECT 协议。 */
#define EXAMPLE_PROXY_HTTP_CONNECT 1
#include "../proxy_dial/main.c"
$ gcc -O1 -DXRT_MODULE_ALL -I single -include xrt.h impl.c examples/network/proxy_dial_http_connect/main.c -lws2_32 -liphlpapiusage: proxy_dial <proxy-host> ...

Что только что произошло. (1) Без аргументов печатается usage — пример спроектирован как боевая проверка против реального прокси (контраст с чисто протокольным примером socks5 — один голый протокол, другая полная цепочка). (2) С аргументами полная цепочка: резолв адреса прокси → соединение TCP → отправка CONNECT host:port → ожидание подтверждения 2xx → успех — канал → связь с целью (проверка эхом). (3) Размещённый возврат пути неудачи — падение любого шага очищает все ресурсы (доказательная позиция семантики «managed»). Пример proxy_dial — изоморфная боевая версия SOCKS5 — два протокола симметричны.

70.4契约

  • Размещённая вся цепочка: DNS + TCP + рукопожатие + доставка за один раз; при неудаче созданные ресурсы возвращаются полностью — размещённая семантика tcp_dial в версии прокси.
  • Прозрачный канал: после успеха Stream неотличим от прямого — TLS/HTTP наверху не чувствуют.
  • Доменные цели: резолв на стороне прокси (клиент не касается DNS цели) — три типа адресов, единое кодирование.
  • Двойной протокол: SOCKS5 (бинарное инкрементное рукопожатие) / HTTP CONNECT (текст) — решает среда, XRT поставляет оба.
  • Опциональная аутентификация: без / логин-пароль / кастомные заголовки — заявляется конфигурацией.
  • Безопасность канала — ваша забота: прокси не шифрует — TLS over прокси (позиция композиции тома 8).

От примеров к инженерии: три хозяина прокси

Корпоративный выход (дом комплаенса): исходящие интрасетевого сервиса через назначенные прокси — конфигурация заявляет пул прокси (переменные среды главы 43 или файл конфигурации), щуп доступности прокси ведёт таблицу годных (испытательное упражнение), дозвон идёт размещённой цепочкой. Мультирегиональный доступ: выбор прокси по цели (интрасетевые домены через A, публичные через B) — таблица маршрутизации целей + отображение прокси; доменный тип цели здесь имеет дополнительную ценность (резолв на стороне прокси — интрасетевые домены резолвит правильный прокси).Приватность/тестовая инфраструктура: локальный SOCKS5-пересыльщик (полное цветение продвинутого упражнения) для наблюдения/внедрения/повтора трафика — прокси-позиция тестовой инфраструктуры. Три хозяина делят конфигурационную плоскость: управление пулом (доступность/сортировка), маршрутизация целей (какие цели через какой прокси), политика неудач (повтор/деградация в прямое/быстрый отказ) — этот слой встроен в библиотеку расширений xhttp (том 10).

TLS поверх прокси: анонс композиции тома 8

Канал прокси не шифруется — чувствительные данные обязаны идти TLS over прокси. Анонс композиции (разворачивается в томе 8): Stream, доставленный CONNECT/SOCKS5, неотличим от прямого Stream → слой TLS надевается напрямую (обёртка xtlssession) → HTTP надевается снова (том 9) — трёхслойная вложенность, где каждый слой не знает, прокси под ним или прямое соединение. Эта прозрачность не случайна — контракт Stream главы 67 (жизненный цикл, идентичный прямому) есть её структурная гарантия. Обратная композиция тоже существует: сам прокси может быть TLS (прокси-сервер требует TLS-соединения — TLS-дозвон адреса прокси — расширение на уровне конфигурации). Читайте том 8 с этой картой композиции — прокси есть прототип «транспортного среднего слоя», а прозрачность — дизайн-парадигма среднего слоя.

Историческая сноска: имя и эволюция SOCKS

Имя SOCKS происходит от "SOCKetS" (шутливое множественное от «сокеты») — родилось из потребностей пробивания файрволов 1992 года. Линия эволюции: SOCKS4 (только TCP + v4) → SOCKS4a (доменные цели) → SOCKS5 (RFC 1928 — v6/UDP/каркас аутентификации) — каждый шаг прокси-протоколов отвечал реальному пробелу предшественника. HTTP CONNECT вырос со стороны потребностей обратных прокси (раннее HTTPS-пробивание) — сосуществование двух протоколов-дуумвиратов — наслоение истории. Ценность родословной: каждая возможность прокси-протокола (доменные цели/аутентификация/UDP-пересылка) соответствует классу реальных потребностей развёртывания — читайте протокол по потребностям, а не по байтам. Модуль proxy XRT покрывает клиентскую сторону обоих протоколов (серверные прокси — инфраструктура развёртывания, не ядро — локальный прокси продвинутого упражнения — учебная форма).

Типичные ошибки

Ловушка 1: голый TCP до прокси как канал:Ловушка 1: голый TCP до прокси как канал

Симптомы: прямое соединение с целью падает или отвергается — соединён только TCP прокси без протокольного рукопожатия (или данные отправлены до подтверждения CONNECT — прокси принимает данные приложения за протокольные сообщения и отбрасывает/ошибается).

Причина: TCP до прокси — только транспорт установлен — канал существует лишь после подтверждения протокольным рукопожатием (SOCKS5 два оборота / CONNECT одно подтверждение).

错误写法
xnetstream* S = xrtNetStreamConnect(Engine, ProxyAddr, 1, NULL, NULL, NULL);
xrtNetStreamSend(S, TargetData, Size);   /* без рукопожатия — прокси не знает эти данные */
正确写法
/* конфигурационный дозвон — машина состояний рукопожатия заперта в модуле */
xnetproxydialconfig Dial;
Dial.Type = XNET_PROXY_SOCKS5;
Dial.Proxy = ProxyAddr;
Dial.Target = Target;   /* xnetproxyendpoint (включая доменную форму) */
xnetstream* S = ManagedDial(Engine, &Dial);   /* доставляется только после завершения рукопожатия */
Ловушка 2: открытый текст учётных данных на канале прокси:Ловушка 2: открытый текст учётных данных на канале прокси

Симптомы: аудит безопасности — логины-пароли/бизнес-данные на плече прокси ловятся в открытом виде (видно в захвате пакетов).

Причина: протокол прокси не шифрует — аутентификация SOCKS5 / бизнес-данные идут по плечу прокси открытым текстом.

错误写法
/* SOCKS5-аутентификация логин-пароль + открытый бизнес — видно и админу прокси, и прослушивающим линк */
Dial.Auth = &PlainAuth;
SendSecret(S, Data);
正确写法
/* чувствительные данные — TLS over прокси: после CONNECT сперва TLS-рукопожатие (том 8), аутентификация тоже внутри TLS */
xnetstream* S = ManagedDial(Engine, &Dial);
/* на S поднимаем TLS (композиция главы 82) — содержимое канала шифруется */

Практические задания

Базовое: воспроизведение генерации сообщений

Воспроизведите генерацию greeting/CONNECT примера socks5; сгенерируйте по одному CONNECT на каждый из трёх типов адресов (v4/домен/v6) — сверьте по hex.

Продвинутое: локальный петлевой прокси

Поднимите минимальный SOCKS5-петлевой прокси (ответ на greeting + CONNECT всегда успех + данные насквозь) — клиентом XRT через него к петлевому сервису. Обе стороны рукопожатия напишите сами — протокол явится посередине.

Вызов: щуп доступности прокси

Реализуйте щуп пула прокси: N прокси конкурентно дозваниваются с тестовой целью → записываются задержка/доля успеха/классификация неудач (протокольная ошибка/тайм-аут/отказ аутентификации) → сортировка таблицы годных. Критерий приёмки: тайм-аут и политика повторов щупа настраиваемы; полные цепочки причин неудач (глава 4) в журнале; таблица годных обновляется периодически (глава 42); нулевая утечка ресурсов при возврате негодных прокси.

Итоги

ТемаКраткая справка
Размещённый дозвонDNS + TCP + рукопожатие + доставка за раз; при неудаче полный возврат
Прозрачный каналпосле успеха Stream неотличим от прямого — верхние слои не чувствуют
SOCKS5greeting → [auth] → CONNECT, бинарное инкрементное рукопожатие
CONNECTтекстовая строка метода + подтверждение 2xx — частый гость файрвольных сред
Типы адресов01 v4 / 03 домен (резолв на стороне прокси) / 04 v6
Аутентификациябез / логин-пароль / кастомные заголовки — заявляется конфигурацией
Безопасность каналапрокси не шифрует — TLS over прокси (том 8)

📖 Справочники API в этой главе:proxy (39 записей)net (247 записей)tcp (81 записей)

Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки