Введение в v2ray транспорты

Материал из Все о VPN, прокси и свободном интернете

Введение в v2ray транспорты

Оригинал:
https://github.com/mmmray/wall-documents/blob/main/transports.md


Давайте вместе с Вами исследуем тему транспортов в xray-core, так как видимо сухая техническая документация с указанием параметров, не дает общее представление конечному пользователю.
Вообще, транспорты в нашем ядре — это буквально все что угодно, что может эмулировать (или оборачивать) двунаправленное соединение между сервером и клиентом.
TCP-сокеты являются именно такими, но это не обязательно должен быть TCP или даже основанный на TCP. Это должно быть что-то, что передает байты по порядку от клиента к серверу без их потери и наоборот.
После предоставления этого канала поверх него могут работать Vmess, VLESS или Trojan. Или что-то совершенно другое.
Наш первый транспорт
Мы будем использовать некоторые команды Linux, чтобы показать, что могут делать транспорты. Начнем с чего-то простого.
Откройте терминал и введите это:
nc -l localhost 6003  # сервер
и в другом терминале:
nc localhost 6003  # клиент
Напишите несколько строк в одном терминале и наблюдайте, как они появляются в другом. Это работает в обоих направлениях.
Эта команда эквивалентна “TCP транспорту” в нашем ядре. Вы можете заменить хост и порт в команде client, чтобы проверить, открыт ли TCP-порт.


WebSocket
WebSocket — это протокол на основе HTTP, предназначенный для выполнения той же задачи, что и TCP-сокет.
Основное различие заключается в следующем:
WebSocket добавляет большое рукопожатие на основе HTTP в начале соединения. Это полезно для кукисов, аутентификации, обслуживания нескольких “WebSocket-сервисов” под разными HTTP-путями и для обеспечения того, чтобы веб-сайт мог открывать соединения только с собственным источником, а не с случайными хостами. 
Для целей создания транспортов некоторые из этих функций, особенно маршрутизация на основе пути, очень полезны, но дополнительный обмен данными в начале добавляет дополнительную задержку.
WebSocket не передает байты, а “сообщения”. Для наших целей это означает, что вместо Write("hello world") и отправки hello world (как в TCP), фактически отправляется <frame header>hello world. Для целей создания транспортов этот дизайн является ненужной нагрузкой.
Причина, по которой мы миримся с этими аспектами, заключается в том, что некоторые CDN могут напрямую пересылать WebSocket.
Если nc фактически является TCP-транспортом, то такие инструменты, как websocat или wscat, фактически являются WebSocket-транспортами.
На самом деле, эти инструменты очень полезны для проверки, правильно ли слушает WebSocket на определенном пути:
curl https://example.com  # example.com является действительным HTTP-сервисом...
websocat wss://example.com  # ...но фактически не является WebSocket! Это не работает
Чтобы получить локальный тестовый сервер, как в предыдущем примере с nc, вы можете сделать это:
websocat -s 6003  # сервер
websocat ws://localhost:6003  # клиент
Еще раз, вы можете отправлять случайные строки текста туда и обратно.
Поскольку WebSocket — это просто HTTP/1.1, а HTTP/1.1 — это просто TCP, мы можем даже направить websocat на nc, чтобы вывести начальное рукопожатие:
nc -l localhost 6003  # сервер
websocat ws://localhost:6003  # клиент
Когда вы выполните вторую команду, она фактически выведет:
GET / HTTP/1.1
Host: localhost:6003
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg==
Это часть нагрузки WebSocket. Клиент все еще ждет ответа. Давайте также извлечем ответ.
Прекратите обе команды с помощью Ctrl-C и запустите websocat -s 6003 как сервер.
Возьмите приведенный выше текст и отправьте его напрямую с помощью nc:
echo 'GET / HTTP/1.1
Host: localhost:6003
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg==
' | unix2dos | nc localhost 6003 | cat -v
echo просто выводит данные
unix2dos преобразует окончания строк, поскольку HTTP/1.1 ожидает \r\n вместо \n
nc отправляет данные на сервер и выводит ответ
cat -v делает специальные символы (или, скорее, байты, не являющиеся символами) видимыми.
Вы увидите ответ:
HTTP/1.1 101 Switching Protocols^M
Sec-WebSocket-Accept: HKN6nOSb0JT0jWhszYuKJPUPpHg=^M
Connection: Upgrade^M
Upgrade: websocket^M
После этого вы можете отправлять данные с сервера на клиент, вводя их в окне терминала сервера.
Введите hello в сервер websocat, и наблюдайте на клиенте:
M-^A^Fhello
Мусор в начале — это фрейм сообщения WebSocket.
Отправка данных с клиента на сервер не работает, поскольку WebSocket ожидает, что данные будут обернуты в этот фрейм сообщений.
WebSocket 0-RTT
(избавляемся от накладных расходов на рукопожатие)
Где TCP просто выполняет свое рукопожатие, WebSocket отправляет HTTP-запрос и ждет ответа. Эта дополнительная задержка очень заметна, особенно в следующем потоке:
Выполнить TCP рукопожатие
Отправить HTTP-запрос
Ждать HTTP-ответа и прочитать его
Отправить инструкцию VLESS для подключения к какому-то веб-сайту pornhub.com
Ждать ответа и прочитать его
Отправьте немного данных, подождите, отправьте еще немного данных, снова подождите.
Было бы быстрее отправить много данных, а затем подождать много данных:
Выполнить TCP рукопожатие (его не избежать… пока…)
Отправить HTTP-запрос вместе с первой инструкцией VLESS для подключения к pornhub.com
Ждать HTTP-ответа и первых байтов тела одновременно
Если мы сможем это достичь, это будет на один RTT меньше.
Xray и Sing-Box реализуют эту идею под названием Early Data или иногда “0-RTT”. Ранние данные — это просто любые данные, которые клиент хочет записать на шаге 2.
Sing-Box по умолчанию отправляет это как часть URL, но может быть настроен на использование любого имени заголовка (значение в base64).
В Xray это всегда отправляется как заголовок Sec-WebSocket-Protocol. Существует довольно неочевидная причина для этого под названием Browser Dialer для этого конкретного выбора заголовка.
Можно сказать, что отправка этих данных в URL более совместима с HTTP-проксами, но заголовки имеют большую емкость для больших данных.
Это в основном решает проблемы задержки, связанные с WebSocket.
HTTPUpgrade
(избавляемся от накладных расходов на фреймирование данных)
Помните? Когда вы отправляете hello в WebSocket, фактически передается:
M-^A^Fhello 
Что за мусор впереди? Это просто трата полосы пропускания. Мы ранее говорили, что стандарт WebSocket этого требует.
Но на самом деле оказывается, что многие CDN не заботятся о том, если вы отправляете фактические данные WebSocket через соединение WebSocket. После первого HTTP-запроса и HTTP-ответа, кажется, что сокет можно использовать напрямую без каких-либо накладных расходов вообще.
Вся идея HTTPUpgrade заключается в этом. Изначально этот вид транспорта был разработан Tor под названием HTTPT, некоторое время после того, как v2ray добавил WebSocket. Позже v2fly перенес его как новый транспорт, затем он был скопирован в Xray.
Таким образом, передача становится такой. От клиента:
GET / HTTP/1.1
Host: localhost:6003
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: MOIjFT7/cVsCCr95mkpCtg==
Затем ответ от сервера:
HTTP/1.1 101 Switching Protocols
Sec-WebSocket-Accept: HKN6nOSb0JT0jWhszYuKJPUPpHg=
Connection: Upgrade
Upgrade: websocket
…и затем он используется напрямую как обычное TCP-соединение, как в первом примере с nc.
HTTPUpgrade 0-RTT
(избавляемся от обеих проблем)
Это работает так же, как WebSocket 0-RTT. Теперь обе проблемы WebSocket устранены: Накладные расходы на рукопожатие (в основном) и накладные расходы на сообщения (полностью).
GRPC
TODO
SplitHTTP
TODO