translate /developerment/ section (#583)

This commit is contained in:
Nikita Korotaev
2024-09-28 10:20:39 +05:00
committed by GitHub
parent b8524c7437
commit d3e8b62f2c
9 changed files with 496 additions and 472 deletions
+67 -63
View File
@@ -1,92 +1,96 @@
# mKCP 协议
# Протокол mKCP
mKCP 是流式传输协议,由 [KCP 协议](https://github.com/skywind3000/kcp) 修改而来,可以按顺序传输任意的数据流。
mKCP - это потоковый транспортный протокол, основанный на [протоколе KCP](https://github.com/skywind3000/kcp), который может передавать любые потоки данных по порядку.
## 版本
## Версия
mKCP 没有版本号,不保证版本之间兼容性。
Протокол mKCP не имеет номера версии, совместимость между версиями не гарантируется.
## 依赖
## Зависимости
### 底层协议
### Базовый протокол
mKCP 是一个基于 UDP 的协议,所有通讯使用 UDP 传输。
mKCP - это протокол, основанный на UDP, все коммуникации осуществляются по UDP.
### 函数
### Функции
- fnv: [FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) 哈希函数
- 输入参数为任意长度的字符串;
- 输入出一个 32 位无符号整数;
- fnv: хэш-функция [FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function)
- Входные данные: строка произвольной длины;
- Выходные данные: 32-битное беззнаковое целое число;
## 通讯过程
## Процесс коммуникации
1. mKCP 将数据流拆成若干个数据包进行发送。一个数据流有一个唯一标识,用以区分不同的数据流。数据流中的每一个数据包都携带了同样的标识。
1. mKCP 没有握手过程,当收到一个数据包时,根据其携带的数据流的标识来判断是否为新的通话,或是正在进行中的通话。
1. 每一个数据包中包含若干个片段(Segment),片段分为三类:数据(Data)、确认(ACK)、心跳(Ping)。每个片段需要单独处理。
1. mKCP разбивает поток данных на несколько пакетов для отправки. Каждый поток данных имеет уникальный идентификатор, который используется для различения разных потоков данных. Каждый пакет данных в потоке данных несет один и тот же идентификатор.
2. У mKCP нет процесса рукопожатия. При получении пакета данных определяется, является ли это новым вызовом или текущим вызовом, на основе идентификатора потока данных, который он несет.
3. Каждый пакет данных содержит несколько сегментов (Segment), которые делятся на три типа: данные (Data), подтверждение (ACK) и пульс (Ping). Каждый сегмент обрабатывается отдельно.
## 数据格式
## Формат данных
### 数据包
### Пакет данных
| 4 字节 | 2 字节 | L 字节 |
| ---------- | ---------- | -------- |
| 认证信息 A | 数据长度 L | 片段部分 |
| 4 байта | 2 байта | L байт |
|---------------------------------|----------------|------------------|
| Информация для аутентификации A | Длина данных L | Сегментная часть |
其中:
Где:
- 认证信息 A = fnv(片段部分),big endian;
- 片段部分可能包含多个片段;
- Информация для аутентификации A = fnv(сегментная часть), big endian;
- Сегментная часть может содержать несколько сегментов;
### 数据片段
### Сегмент данных
| 2 字节 | 1 字节 | 1 字节 | 4 字节 | 4 字节 | 4 字节 | 2 字节 | Len 字节 |
| --------- | -------- | -------- | --------- | --------- | ---------------- | -------- | -------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 时间戳 Ts | 序列号 Sn | 未确认序列号 Una | 长度 Len | 数据 |
| 2 байта | 1 байт | 1 байт | 4 байта | 4 байта | 4 байта | 2 байта | Len байт |
|--------------------|-------------|-----------|--------------------|---------------------|---------------------------------------|-----------|----------|
| Идентификатор Conv | Команда Cmd | Опция Opt | Временная метка Ts | Порядковый номер Sn | Неподтвержденный порядковый номер Una | Длина Len | Данные |
其中:
Где:
- 标识 Conv: mKCP 数据流的标识
- 指令 Cmd: 常量 0x01
- 选项 Opt: 可选的值有:
- 0x00: 空选项
- 0x01: 对方已发出所有数据
- 时间戳 Ts: 当前片段从远端发送出来时的时间,big endian
- 序列号 Sn: 该数据片段时数据流中的位置,起始片段的序列号为 0,之后每个新片段按顺序加 1
- 未确认序列号 Una: 远端主机正在发送的,且尚未收到确认的最小的 Sn
- Идентификатор Conv: идентификатор потока данных mKCP
- Команда Cmd: константа 0x01
- Опция Opt: возможные значения:
- 0x00: пустая опция
- 0x01: другая сторона отправила все данные
- Временная метка Ts: время отправки текущего сегмента с удаленной стороны, big endian
- Порядковый номер Sn: позиция сегмента данных в потоке данных, порядковый номер начального сегмента равен 0, каждый последующий сегмент увеличивается на 1
- Неподтвержденный порядковый номер Una: минимальный Sn, который отправляется удаленным хостом и еще не получил подтверждение
### 确认片段
### Сегмент подтверждения
| 2 字节 | 1 字节 | 1 字节 | 4 字节 | 4 字节 | 4 字节 | 2 字节 | Len \* 4 字节 |
| --------- | -------- | -------- | -------- | ----------------- | --------- | -------- | -------------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 窗口 Wnd | 下一接收序列号 Sn | 时间戳 Ts | 长度 Len | 已收到的序列号 |
| 2 байта | 1 байт | 1 байт | 4 байта | 4 байта | 4 байта | 2 байта | Len * 4 байта |
|--------------------|-------------|-----------|----------|------------------------------------------|--------------------|-----------|----------------------------------|
| Идентификатор Conv | Команда Cmd | Опция Opt | Окно Wnd | Следующий порядковый номер для приема Sn | Временная метка Ts | Длина Len | Подтвержденные порядковые номера |
其中:
Где:
- 标识 Conv: mKCP 数据流的标识
- 指令 Cmd: 常量 0x00
- 选项 Opt: 同上
- 窗口 Wnd: 远端主机可以接收的最大序列号
- 下一接收序列号 Sn: 远端主机未收到的数据片段中的最小序列号
- 时间戳 Ts: 远端主机最新收到的数据片段的时间戳,可用于计算延迟
- 已收到的序列号: 每个 4 字节,表示此序列号的数据已经确认收到
- Идентификатор Conv: идентификатор потока данных mKCP
- Команда Cmd: константа 0x00
- Опция Opt: как указано выше
- Окно Wnd: максимальный порядковый номер, который может принять удаленный хост
- Следующий порядковый номер для приема Sn: минимальный порядковый номер сегмента данных, который не получил удаленный хост
- Временная метка Ts: временная метка последнего полученного сегмента данных удаленным хостом, может использоваться для расчета задержки
- Подтвержденные порядковые номера: каждые 4 байта указывают, что данные с этим порядковым номером получены и подтверждены
注释:
Комментарий:
- 远程主机期待收到序列号 [Sn, Wnd) 范围内的数据
- Удаленный хост ожидает получения данных с порядковыми номерами в диапазоне [Sn, Wnd)
### Сегмент пульса
| 2 байта | 1 байт | 1 байт | 4 байта | 4 байта | 4 байта |
|--------------------|-------------|-----------|---------------------------------------|------------------------------------------|--------------|
| Идентификатор Conv | Команда Cmd | Опция Opt | Неподтвержденный порядковый номер Una | Следующий порядковый номер для приема Sn | Задержка Rto |
Где:
- Идентификатор Conv: идентификатор потока данных mKCP
- Команда Cmd: возможные значения:
- 0x02: удаленный хост принудительно завершает сеанс
- 0x03: обычный пульс
- Опция Opt: как указано выше
- Неподтвержденный порядковый номер Una: тот же, что и Una в сегменте данных
- Следующий порядковый номер для приема Sn: тот же, что и Sn в сегменте подтверждения
- Задержка Rto: задержка, рассчитанная самим удаленным хостом
### 心跳片段
| 2 字节 | 1 字节 | 1 字节 | 4 字节 | 4 字节 | 4 字节 |
| --------- | -------- | -------- | ---------------- | ----------------- | -------- |
| 标识 Conv | 指令 Cmd | 选项 Opt | 未确认序列号 Una | 下一接收序列号 Sn | 延迟 Rto |
其中:
- 标识 Conv: mKCP 数据流的标识
- 指令 Cmd: 可选的值有
- 0x02: 远端主机强行终止会话
- 0x03: 正常心跳
- 选项 Opt: 同上
- 未确认序列号 Una: 同数据片段的 Una
- 下一接收序列号 Sn: 同确认片段的 Sn
- 延迟 Rto: 远端主机自己计算出的延迟
+86 -82
View File
@@ -1,129 +1,133 @@
# Mux.Cool 协议
# Протокол Mux.Cool
Mux.Cool 协议是一个多路复用传输协议,用于在一条已建立的数据流中传输多个各自独立的数据流。
Протокол Mux.Cool - это мультиплексирующий транспортный протокол, используемый для передачи нескольких независимых потоков данных по одному установленному потоку данных.
## 版本
## Версия
当前版本是 1 Beta。
Текущая версия - 1 Beta.
## 依赖
## Зависимости
### 底层协议
### Базовый протокол
Mux.Cool 必须运行在一个已建立的可靠数据流之上。
Mux.Cool должен работать поверх установленного надежного потока данных.
## 通讯过程
## Процесс коммуникации
一个 Mux.Cool 连接中可传输若干个子连接,每个子连接有一个独立的 ID 和状态。传输过程由帧(Frame)组成,每一帧用于传输一个特定的子连接的数据。
Одно соединение Mux.Cool может передавать несколько подсоединений, каждое из которых имеет свой собственный идентификатор и состояние. Процесс передачи состоит из кадров (Frame), каждый из которых используется для передачи данных определенного подсоединения.
### 客户端行为
### Поведение клиента
当有连接需求时并且没有现有可用的连接时,客户端向服务器发起一个新连接,以下称为“主连接”。
Когда требуется соединение и нет доступного существующего соединения, клиент инициирует новое соединение с сервером, которое далее называется "главным соединением".
1. 一个主连接可用于发送若干个子连接。客户端可自主决定主连接可承载的子连接数量。
1. 对于一个新的子连接,客户端必须发送状态`New`以通知服务器建立子连接,然后使用状态`Keep`来传送数据。
1. 当子连接结束时,客户端发送`End`状态来通知服务器关闭子连接。
1. 客户端可自行决定何时关闭主连接,但必须确定服务器也同时保持连接。
1. 客户端可使用 KeepAlive 状态来避免服务器关闭主连接。
1. Одно главное соединение может использоваться для отправки нескольких подсоединений. Клиент может самостоятельно определять количество подсоединений, которое может нести главное соединение.
2. Для нового подсоединения клиент должен отправить состояние `New`, чтобы уведомить сервер о создании подсоединения, а затем использовать состояние `Keep` для передачи данных.
3. Когда подсоединение завершается, клиент отправляет состояние `End`, чтобы уведомить сервер о закрытии подсоединения.
4. Клиент может самостоятельно решать, когда закрыть главное соединение, но должен убедиться, что сервер также поддерживает соединение.
5. Клиент может использовать состояние KeepAlive, чтобы предотвратить закрытие главного соединения сервером.
### 服务器端行为
### Поведение сервера
当服务器端接收到新的子连接时,服务器应当按正常的连接来处理。
Когда сервер получает новое подсоединение, он должен обрабатывать его как обычное соединение.
1. 当收到状态`End`时,服务器端可以关闭对目标地址的上行连接。
1. 在服务器的响应中,必须使用与请求相同的 ID 来传输子连接的数据。
1. 服务器不能使用`New`状态。
1. 服务器可使用 KeepAlive 状态来避免客户端关闭主连接。
1. При получении состояния `End` сервер может закрыть исходящее соединение с целевым адресом.
2. В ответе сервера для передачи данных подсоединения должен использоваться тот же идентификатор, что и в запросе.
3. Сервер не может использовать состояние `New`.
4. Сервер может использовать состояние KeepAlive, чтобы предотвратить закрытие главного соединения клиентом.
## 传输格式
## Формат передачи
Mux.Cool 使用对称传输格式,即客户端和服务器发送和接收相同格式的数据。
Mux.Cool использует симметричный формат передачи, то есть клиент и сервер отправляют и получают данные в одинаковом формате.
### 帧格式
### Формат кадра
| 2 字节 | L 字节 | X 字节 |
| ------------ | ------ | -------- |
| 元数据长度 L | 元数据 | 额外数据 |
| 2 байта | L байт | X байт |
|--------------------|------------|-----------------------|
| Длина метаданных L | Метаданные | Дополнительные данные |
### 元数据
### Метаданные
元数据有若干种类型。所有类型的元数据都包含 ID 和 Opt 两项,其含义为:
Существует несколько типов метаданных. Все типы метаданных содержат поля ID и Opt, которые означают следующее:
- ID: 子连接的唯一标识
- 对于一般 Mux 子连接,ID 由 1 开始累加
- 对于 Xray 实现的 [Single XUDP](https://github.com/XTLS/Xray-core/blob/main/common/xudp/xudp.go),ID 始终为 0
- ID: уникальный идентификатор подсоединения
- Для обычных подсоединений Mux ID начинается с 1 и увеличивается
- Для [Single XUDP](https://github.com/XTLS/Xray-core/blob/main/common/xudp/xudp.go), реализованного в Xray, ID всегда равен 0
- Opt:
- D(0x01): 有额外数据
- D(0x01): есть дополнительные данные
当选项 Opt(D) 开启时,额外数据格式如下:
Если опция Opt(D) включена, формат дополнительных данных следующий:
| 2 字节 | X-2 字节 |
| -------- | -------- |
| 长度 X-2 | 数据 |
| 2 байта | X-2 байта |
|-----------|-----------|
| Длина X-2 | Данные |
### 新建子连接 (New)
### Создание нового подсоединения (New)
| 2 字节 | 1 字节 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | A 字节 | 8 字节 |
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ | ---------------- |
| ID | 0x01 | 选项 Opt | 网络类型 N | 端口 | 地址类型 T | 地址 A | Global ID (XUDP) |
| 2 байта | 1 байт | 1 байт | 1 байт | 2 байта | 1 байт | A байт | 8 байт |
|---------|--------|-----------|------------|---------|--------------|---------|------------------|
| ID | 0x01 | Опция Opt | Тип сети N | Порт | Тип адреса T | Адрес A | Global ID (XUDP) |
其中:
Где:
- 网络类型 N:
- 0x01:TCP,表示当前子连接的流量应当以 TCP 的方式发送至目标。
- 0x02:UDP,表示当前子连接的流量应当以 UDP 的方式发送至目标。
- 地址类型 T:
- 0x01:IPv4
- 0x02:域名
- 0x03:IPv6
- 地址 A:
- 当 T = 0x01 时,A 为 4 字节 IPv4 地址;
- 当 T = 0x02 时,A 为 1 字节长度(L) + L 字节域名;
- 当 T = 0x03 时,A 为 16 字节 IPv6 地址;
- Global ID (XUDP):
- 客户端计算出 UDP 来源二元组的全局独特 ID,服务端用以确保当 XUDP 断线重连时,仍使用同一个端口与目标通信。
- Тип сети N:
- 0x01: TCP, указывает, что трафик текущего подсоединения должен быть отправлен на целевой адрес по TCP.
- 0x02: UDP, указывает, что трафик текущего подсоединения должен быть отправлен на целевой адрес по UDP.
- Тип адреса T:
- 0x01: IPv4
- 0x02: доменное имя
- 0x03: IPv6
- Адрес A:
- Если T = 0x01, A - это 4-байтовый адрес IPv4;
- Если T = 0x02, A - это 1 байт длины (L) + L байт доменного имени;
- Если T = 0x03, A - это 16-байтовый адрес IPv6;
- Global ID (XUDP):
- Клиент вычисляет глобально уникальный идентификатор исходного кортежа UDP, который сервер использует, чтобы гарантировать, что при переподключении XUDP будет использоваться тот же порт для связи с целевым адресом.
在新建子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
При создании нового подсоединения, если Opt(D) включена, данные, переносимые этим кадром, должны быть отправлены на целевой хост.
### 保持子连接 (Keep)
### Поддержание подсоединения (Keep)
TCP
| 2 字节 | 1 字节 | 1 字节 |
| ------ | ------ | -------- |
| ID | 0x02 | 选项 Opt |
| 2 байта | 1 байт | 1 байт |
|---------|--------|-----------|
| ID | 0x02 | Опция Opt |
UDP
| 2 字节 | 1 字节 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | A 字节 |
| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ |
| ID | 0x02 | 选项 Opt | 网络类型 N | 端口 | 地址类型 T | 地址 A |
| 2 байта | 1 байт | 1 байт | 1 байт | 2 байта | 1 байт | A байт |
|---------|--------|-----------|------------|---------|--------------|---------|
| ID | 0x02 | Опция Opt | Тип сети N | Порт | Тип адреса T | Адрес A |
在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
XUDP 在 Opt(D) 之后加 UDP 地址,格式同新建子连接,但没有 Global ID。
При поддержании подсоединения, если Opt(D) включена, данные, переносимые этим кадром, должны быть отправлены на целевой хост.
XUDP добавляет адрес UDP после Opt(D) в том же формате, что и при создании нового подсоединения, но без Global ID.
### 关闭子连接 (End)
### Закрытие подсоединения (End)
| 2 字节 | 1 字节 | 1 字节 |
| ------ | ------ | -------- |
| ID | 0x03 | 选项 Opt |
| 2 байта | 1 байт | 1 байт |
|---------|--------|-----------|
| ID | 0x03 | Опция Opt |
在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。
При поддержании подсоединения, если Opt(D) включена, данные, переносимые этим кадром, должны быть отправлены на целевой хост.
### 保持连接 (KeepAlive)
### Поддержание соединения (KeepAlive)
| 2 字节 | 1 字节 | 1 字节 |
| ------ | ------ | -------- |
| ID | 0x04 | 选项 Opt |
| 2 байта | 1 байт | 1 байт |
|---------|--------|-----------|
| ID | 0x04 | Опция Opt |
在保持连接时:
При поддержании соединения:
- Если Opt(D) включена, данные, переносимые этим кадром, должны быть отброшены.
- ID может быть случайным значением.
## Применение
Протокол Mux.Cool не зависит от базового протокола и теоретически может использовать любое надежное потоковое соединение для передачи данных протокола Mux.Cool.
В протоколах, ориентированных на целевой адрес, таких как Shadowsocks и VMess, при установлении соединения должен быть указан целевой адрес.
Для обеспечения совместимости протокол Mux.Cool определяет адрес "v1.mux.cool". То есть, если целевой адрес главного соединения совпадает с этим адресом, пересылка осуществляется в режиме Mux.Cool, в противном случае пересылка осуществляется традиционным способом. (Примечание: это внутренняя метка программы, VMess и VLESS не отправляют адрес "v1.mux.cool" в пакетах данных).
- 若 Opt(D) 开启,则这一帧所带的数据必须被丢弃。
- ID 可为随机值。
## 应用
Mux.Cool 协议与底层协议无关,理论上可以使用任何可靠的流式连接来传输 Mux.Cool 的协议数据。
在目标导向的协议如 Shadowsocks 和 VMess 协议中,连接建立时必须包含一个指定的地址。
为了保持兼容性,Mux.Cool 协议指定地址为“v1.mux.cool”。即当主连接的目标地址与之匹配时,则进行 Mux.Cool 方式的转发,否则按传统方式进行转发。(注:这是一个程序内的标记,VMess 和 VLESS 并不会在数据包中发送“v1.mux.cool”地址)
+51 -49
View File
@@ -1,91 +1,93 @@
# VLESS 协议
# Протокол VLESS
VLESS 是一个无状态的轻量传输协议,可以作为 Xray 客户端和服务器之间的桥梁。
VLESS - это легковесный, не сохраняющий состояние транспортный протокол, который может служить мостом между клиентом и сервером Xray.
## Request & Response
## Запрос и ответ
| 1 字节 | 16 字节 | 1 字节 | M 字节 | 1 字节 | 2 字节 | 1 字节 | S 字节 | X 字节 |
| -------- | --------- | -------------- | ----------------- | ------ | ------ | -------- | ------ | -------- |
| 协议版本 | 等价 UUID | 附加信息长度 M | 附加信息 ProtoBuf | 指令 | 端口 | 地址类型 | 地址 | 请求数据 |
| 1 байт | 16 байт | 1 байт | M байт | 1 байт | 2 байта | 1 байт | S байт | X байт |
|------------------|--------------------|-----------------------------------|------------------------------------|---------|---------|------------|--------|----------------|
| Версия протокола | Эквивалентный UUID | Длина дополнительной информации M | Дополнительная информация ProtoBuf | Команда | Порт | Тип адреса | Адрес | Данные запроса |
| 1 字节 | 1 字节 | N 字节 | Y 字节 |
| ---------------------- | -------------- | ----------------- | -------- |
| 协议版本,与请求的一致 | 附加信息长度 N | 附加信息 ProtoBuf | 响应数据 |
| 1 байт | 1 байт | N байт | Y байт |
|-----------------------------------------------|-----------------------------------|------------------------------------|---------------|
| Версия протокола, совпадает с версией запроса | Длина дополнительной информации N | Дополнительная информация ProtoBuf | Данные ответа |
VLESS 早在第二个测试版 ALPHA 2 时就已经是上述结构了(BETA 是第五个测试版):
Структура VLESS была такой же, как указано выше, еще во второй альфа-версии (ALPHA 2) (BETA - это пятая бета-версия):
> “响应认证”被替换为“协议版本”并移至最前,使 VLESS 可以升级换代,同时消除了生成伪随机数的开销。混淆相关结构被替换为附加信息(ProtoBuf)并前移,赋予协议本身可扩展性,相关开销也极小([gogo/protobuf](https://github.com/gogo/protobuf)),若无附加信息则无相关开销。
> "Аутентификация ответа" была заменена на "версию протокола" и перемещена в начало, что позволяет VLESS обновляться, одновременно устраняя накладные расходы на генерацию псевдослучайных чисел. Структура, связанная с обфускацией, была заменена на дополнительную информацию (ProtoBuf) и перемещена вперед, что придало самому протоколу расширяемость с минимальными накладными расходами ([gogo/protobuf](https://github.com/gogo/protobuf)). Если нет дополнительной информации, то нет и связанных с ней накладных расходов.
我一直觉得“响应认证”不是必要的,ALPHA 时为了提升生成随机数的性能,还用 math/rand 替换 crypto/rand,而现在都不需要了。
Я всегда считал, что "аутентификация ответа" не является обязательной. В версии ALPHA для повышения производительности генерации случайных чисел мы заменили crypto/rand на math/rand, а теперь в этом нет необходимости.
“协议版本”不仅能起到“响应认证”的作用,还赋予了 VLESS 无痛升级协议结构的能力,带来无限的可能性。
“协议版本”在测试版本中均为 0,正式版本中为 1,以后若有不兼容的协议结构变更则应升级版本。
"Версия протокола" не только выполняет функцию "аутентификации ответа", но и дает VLESS возможность безболезненно обновлять структуру протокола, открывая бесконечные возможности.
"Версия протокола" во всех бета-версиях равна 0, в официальной версии - 1, а в будущем, если будут несовместимые изменения в структуре протокола, версия должна быть обновлена.
VLESS 服务端的设计是 switch version,即同时支持所有 VLESS 版本。若需要升级协议版本(可能到不了这一步),推荐的做法是服务端提前一个月支持,一个月后再改客户端。VMess 请求也有协议版本,但它的认证信息在外面,指令部分则高度耦合且有固定加密,导致里面的协议版本毫无意义,服务端也没有进行判断,响应则没有协议版本。Trojan 的协议结构中没有协议版本。
Сервер VLESS спроектирован по принципу switch version, то есть он одновременно поддерживает все версии VLESS. Если требуется обновить версию протокола (что маловероятно), рекомендуется сначала добавить поддержку на сервере за месяц до обновления клиентов. Запрос VMess также имеет версию протокола, но его информация для аутентификации находится снаружи, а часть с командой сильно связана и имеет фиксированное шифрование, что делает версию протокола внутри бессмысленной. Сервер также не проверяет ее, а ответ не имеет версии протокола. В структуре протокола Trojan нет версии протокола.
接下来是 UUID,我本来觉得 16 字节有点长,曾经考虑过缩短它,但后来看到 Trojan 用了 56 个可打印字符(56 字节),就彻底打消了这个念头。服务端每次都要验证 UUID,所以性能也很重要:VLESS 的 Validator 经历了多次重构/升级,相较于 VMess,它十分简洁且耗资源很少,可以同时支持非常多的用户,性能也十分强悍,验证速度极快(sync.Map)。API 动态增删用户则更高效顺滑。
Далее идет UUID. Сначала я думал, что 16 байт - это многовато, и подумывал о его сокращении, но потом увидел, что Trojan использует 56 печатаемых символов (56 байт), и полностью отказался от этой идеи. Сервер каждый раз проверяет UUID, поэтому производительность также важна: валидатор VLESS прошел через несколько рефакторингов/обновлений, он очень прост и потребляет мало ресурсов по сравнению с VMess, может одновременно поддерживать очень большое количество пользователей, имеет очень высокую производительность и очень высокую скорость проверки (sync.Map). Динамическое добавление и удаление пользователей через API еще более эффективно и плавно.
https://github.com/XTLS/Xray-core/issues/158
引入 ProtoBuf 是一个创举,等下会详细讲解。“指令”到“地址”的结构目前与 VMess 完全相同,同样支持 Mux。
Внедрение ProtoBuf - это инновация, о которой мы подробнее поговорим ниже. Структура от "команды" до "адреса" в настоящее время полностью идентична VMess и также поддерживает Mux.
总体上,ALPHA 2 到 BETA 主要是:结构进化、清理整合、性能提升、更加完善。这些都是一点一滴的,详见 [VLESS Changes](https://github.com/rprx/v2ray-vless/releases)。
В целом, изменения от ALPHA 2 до BETA в основном заключаются в следующем: эволюция структуры, очистка и консолидация, повышение производительности и улучшение. Все это происходило постепенно, подробнее см. [VLESS Changes](https://github.com/rprx/v2ray-vless/releases).
## ProtoBuf
似乎只有 VLESS 可选内嵌 ProtoBuf,它是一种数据交换格式,信息被紧密编码成二进制,TLV 结构(Tag Length Value)。
Кажется, только VLESS опционально поддерживает встроенный ProtoBuf. Это формат обмена данными, в котором информация плотно упакована в двоичный код, структура TLV (Tag Length Value).
起因是我看到一篇文章称 SS 有一些缺点,如没有设计错误回报机制,客户端没办法根据不同的错误采取进一步的动作。
(但我并不认同所有错误都要回报,不然防不了主动探测。下一个测试版中,服务器可以返回一串自定义信息。)
于是想到一个可扩展的结构是很重要的,未来它也可以承载如动态端口指令。不止响应,请求也需要类似的结构。
本来打算自己设计 TLV,接着发觉 ProtoBuf 就是此结构、现成的轮子,完全适合用来做这件事,各语言支持等也不错。
Причиной этого послужила статья, в которой утверждалось, что у SS есть некоторые недостатки, например, отсутствие механизма сообщения об ошибках, что не позволяет клиенту предпринимать дальнейшие действия в зависимости от типа ошибки.
(Однако я не согласен с тем, что обо всех ошибках нужно сообщать, иначе нельзя будет предотвратить активное зондирование. В следующей бета-версии сервер сможет возвращать пользовательское сообщение об ошибке.)
Поэтому я подумал, что важно иметь расширяемую структуру, которая в будущем сможет нести, например, команды динамического порта. И не только в ответе, но и в запросе нужна подобная структура.
Сначала я хотел разработать TLV самостоятельно, но потом обнаружил, что ProtoBuf - это именно та структура, готовое решение, которое идеально подходит для этой задачи, и имеет хорошую поддержку различных языков программирования.
目前“附加信息”只有 Scheduler 和 SchedulerV,它们是 MessName 和 MessSeed 的替代者,**当你不需要它们时,“附加信息长度”为 0,也就不会有 ProtoBuf 序列化/反序列化的开销**。其实我更愿意称这个过程为“拼接”,因为 pb 实际原理上也只是这么做而已,相关开销极小。拼接后的 bytes 十分紧凑,和 ALPHA 的方案相差无几,有兴趣的可以分别输出并对比。
В настоящее время "дополнительная информация" содержит только Scheduler и SchedulerV, которые являются заменой MessName и MessSeed. **Если они вам не нужны, "длина дополнительной информации" будет равна 0, и не будет никаких накладных расходов на сериализацию/десериализацию ProtoBuf**. На самом деле я предпочитаю называть этот процесс "склейкой", поскольку pb по сути делает именно это, и накладные расходы минимальны. Склеенные байты очень компактны, практически не отличаются от решения в ALPHA. Желающие могут вывести их и сравнить.
为了指示对附加信息(Addons,也可以理解成插件,以后可以有很多个插件)的不同支持程度,下个测试版会在“附加信息长度”前新增“附加信息版本”。256 - 1 = 255 字节是够用且合理的(65535 就太多了,还可能有人恶意填充),现有的只用了十分之一,以后也不会同时有那么多附加信息,且大多数情况下是完全没有附加信息的。真不够用的话,可以升级 VLESS 版本。
Чтобы указать различный уровень поддержки дополнительной информации (Addons, которые можно рассматривать как плагины, в будущем их может быть много), в следующей бета-версии перед "длиной дополнительной информации" будет добавлена "версия дополнительной информации". 256 - 1 = 255 байт - это достаточно и разумно (65535 - это слишком много, и кто-то может злонамеренно заполнить их), существующие используют только десятую часть, и в будущем не будет такого количества дополнительной информации, а в большинстве случаев дополнительная информация вообще отсутствует. Если этого действительно не хватит, можно обновить версию VLESS.
为了减少逻辑判断等开销,暂定 Addons 不使用多级结构。一个月前出现过“可变协议格式”的想法,pb 是可以做到打乱顺序,但没必要,因为现代加密的设计不会让旁观者看出两次传输的头部相同。
Чтобы сократить накладные расходы на логические проверки и т.д., Addons пока не будут использовать многоуровневую структуру. Месяц назад появилась идея "изменяемого формата протокола", pb может переставлять поля, но в этом нет необходимости, поскольку современное шифрование не позволяет стороннему наблюдателю увидеть, что заголовки двух передач одинаковы.
下面介绍 Schedulers 和 Encryption 的构想,**它们都是可选的**,一个应对流量时序特征问题,一个应对密码学上的问题。
Ниже представлены концепции Schedulers и Encryption, **они обе являются необязательными**: одна решает проблему временных характеристик трафика, другая - криптографические проблемы.
## ~~Schedulers~~ Flow
~~中文名暂称:流量调度器~~(2020-09-03 更新:中文名确定为“流控”),指令由 ProtoBuf 承载,控制的是数据部分。
~~Предварительное китайское название: Планировщик трафика~~ (обновление от 03.09.2020: официальное китайское название - "Управление потоком"). Команда передается через ProtoBuf и управляет частью данных.
我之前发现,VMess 原有的 shake “元数据混淆”在 TLS 上完全不会带来有意义的改变,只会降低性能,所以 VLESS 弃用了它。并且,“混淆”这个表述容易被误解成伪装,也弃用了。顺便一提,我一直是不看好伪装的:做不到完全一样,那不就是强特征吗?做得到完全一样,那为什么不直接用伪装目标?我一开始用的是 SSR,后来发现它只是表面伪装骗运营商,就再也没用过了。
Я обнаружил, что оригинальная "обфускация метаданных" shake в VMess не вносит никаких значимых изменений при использовании TLS, а только снижает производительность, поэтому в VLESS от нее отказались. Кроме того, термин "обфускация" может быть неверно истолкован как маскировка, поэтому от него тоже отказались. Кстати, я всегда скептически относился к маскировке: если она не может быть полностью идентичной, то это же явная сигнатура? А если может быть полностью идентичной, то почему бы не использовать саму маскировку? Сначала я использовал SSR, но потом понял, что он маскирует трафик только для провайдера, и больше им не пользовался.
那么,“流量调度器”要解决什么问题?它影响的是宏观流量时序特征,而不是微观特征,后者是加密要解决的事情。流量时序特征可以是协议带来的,比如 Socks5 over TLS 时的 Socks5 握手 ,TLS 上不同的这种特征对于监测者来说就是不同的协议,此时无限 Schedulers 就相当于无限协议(重新分配每次发送的数据量大小等)。流量时序特征也可以是行为带来的,比如访问 Google 首页时加载了多少文件、顺序、每个文件的大小,多套一层加密并不能有效掩盖这些信息。
Так какую же проблему решает "Планировщик трафика"? Он влияет на макроскопические временные характеристики трафика, а не на микроскопические, которые должны решаться шифрованием. Временные характеристики трафика могут быть обусловлены протоколом, например, рукопожатием Socks5 при использовании Socks5 over TLS. Для наблюдателя различные временные характеристики в TLS соответствуют разным протоколам, поэтому бесконечное количество планировщиков эквивалентно бесконечному количеству протоколов (перераспределение размера отправляемых данных и т.д.). Временные характеристики трафика также могут быть обусловлены поведением, например, сколько файлов загружается при посещении главной страницы Google, в каком порядке и какого размера. Добавление еще одного уровня шифрования не может эффективно скрыть эту информацию.
Schedulers 没必要像下面的 Encryption 一样整个套在外面,因为头部的一丁点数据相对于后面的数据量来说太微不足道了。
Schedulers не нужно располагать снаружи, как Encryption, поскольку небольшое количество данных в заголовке ничтожно мало по сравнению с объемом данных, которые следуют за ними.
BETA 2 预计推出两个初级的 Scheduler:Zstd 压缩、数据量动态扩充。进阶操作才是从宏观层面来控制、分配,暂时咕咕。
В BETA 2 планируется выпустить два базовых планировщика: сжатие Zstd и динамическое увеличение объема данных. Более продвинутые операции - это управление и распределение на макроскопическом уровне, которые пока отложены.
## Encryption
与 VMess 的高度耦合不同,VLESS 的服务端、客户端不久后可以提前约定好加密方式,仅在外面套一层加密。这有点类似于使用 TLS,不影响承载的任何数据,也可以理解成底层就是从 TLS 换成预设约定加密。相对于高度耦合,这种方式更合理且灵活:一种加密方式出了安全性问题,直接扔掉并换用其它的就行了,十分方便。VLESS 服务端还会允许不同的加密方式共存。
В отличие от VMess, где шифрование жестко связано с протоколом, в VLESS сервер и клиент смогут заранее договариваться о методе шифрования и шифровать только внешний слой. Это похоже на использование TLS, которое не влияет на передаваемые данные, и можно рассматривать как замену TLS на заранее согласованное шифрование. По сравнению с жесткой связью такой подход более рационален и гибок: если в одном методе шифрования обнаруживается проблема безопасности, его можно просто заменить на другой. Сервер VLESS также позволит использовать разные методы шифрования одновременно.
对比 VMess,VLESS 相当于把 security 换成 encryption,把 disableInsecureEncryption 换成 decryption,就解决了所有问题。目前 encryption 和 decryption 只接受 \"none\" 且不能留空(即使以后有连接安全性检查),详见 [VLESS 配置文档](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md)。encryption 并不需要往外移一级,一是因为无法复用很多代码,二是因为会影响控制粒度,看未来的应用就明白了。
По сравнению с VMess, в VLESS достаточно заменить security на encryption, а disableInsecureEncryption на decryption, чтобы решить все проблемы. В настоящее время encryption и decryption принимают только значение "none" и не могут быть пустыми (даже если в будущем будет добавлена проверка безопасности соединения), подробнее см. [документацию по настройке VLESS](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md). Encryption не нужно перемещать на уровень выше, во-первых, потому что это не позволит повторно использовать много кода, во-вторых, потому что это повлияет на степень контроля, что станет понятно в будущем.
加密支持两类形式,一类是加密完全独立,需要额外密码,适合私用,另一类是结合已有的 UUID 来加密,适合公用。
(若用第一类加密形式,且密码是以某种形式公开的,比如多人共用,那么中间人攻击就不远了)
重新设计的动态端口可能会随加密同时推出,指令由 ProtoBuf 承载,具体实现和 VMess 的动态端口也会有很多不同。
Поддерживаются два типа шифрования: одно - полностью независимое шифрование, требующее дополнительного пароля, подходит для личного использования, другое - шифрование с использованием существующего UUID, подходит для общего использования.
(Если используется первый тип шифрования и пароль раскрывается каким-либо образом, например, при совместном использовании несколькими людьми, то атака "человек посередине" не за горами.)
Переработанный динамический порт может быть выпущен вместе с шифрованием, команда будет передаваться через ProtoBuf, реализация будет отличаться от динамического порта VMess.
套现成加密是件很简单的事情,也就多一层 writer & reader。BETA 3 预计支持 SS 的 aes-128-gcm 和 chacha20-ietf-poly1305:
客户端的 encryption 可以填 “auto: ss_aes-128-gcm_0_123456, ss_chacha20-ietf-poly1305_0_987654”,auto 会选择最适合当前机器的,0 代表测试版,最后的是密码。服务端的 decryption 也是类似填法,收到请求时会逐一尝试解密。
Добавить готовое шифрование довольно просто, нужно добавить всего лишь один уровень writer & reader. В BETA 3 планируется добавить поддержку aes-128-gcm и chacha20-ietf-poly1305 из SS:
в encryption на клиенте можно указать "auto: ss_aes-128-gcm_0_123456, ss_chacha20-ietf-poly1305_0_987654", auto выберет наиболее подходящий для текущей машины, 0 означает бета-версию, а в конце указывается пароль. В decryption на сервере указывается аналогичная строка, и при получении запроса сервер будет пытаться расшифровать его с помощью каждого указанного метода.
并不是所有组合都需逐一尝试:VMess 的加密分为三段,第一段是认证信息,结合了 UUID、alterId、时间因素,第二段是指令部分,以固定算法加密,指令中含有数据部分使用的加密算法,第三段才是重要的数据部分。可以看出,VMess 的加解密方式实际上是多对一(服务端适配),而不仅是结合 UUID。但仅是结合 UUID 来加密也是件相对麻烦的事情,短时间内不会出,鉴于我们现在有 VMessAEAD 可用,也并不着急。若 VLESS 推出了结合 UUID 的加密方式,相当于重构了整个 VMess。
Не все комбинации нужно перебирать: шифрование в VMess состоит из трех частей, первая часть - это информация для аутентификации, которая включает UUID, alterId и временной фактор, вторая часть - это часть с командой, которая шифруется с помощью фиксированного алгоритма, команда содержит информацию об алгоритме шифрования, используемом для части с данными, третья часть - это сами данные. Как видно, VMess фактически использует шифрование "многие ко одному" (адаптация на стороне сервера), а не просто шифрование с помощью UUID. Но шифрование только с помощью UUID тоже довольно сложно, и в ближайшее время оно не будет реализовано, учитывая, что у нас уже есть VMessAEAD. Если в VLESS будет реализован способ шифрования с помощью UUID, это будет означать переработку всего VMess.
## UDP issues
## Проблемы с UDP
[XUDP:VLESS & VMess & Mux UDP FullCone NAT](https://github.com/XTLS/Xray-core/discussions/252)
[XUDP: VLESS & VMess & Mux UDP FullCone NAT](https://github.com/XTLS/Xray-core/discussions/252)
## 客户端开发指引
## Руководство по разработке клиентов
1. VLESS 协议本身还会有不兼容升级,但客户端配置文件参数基本上是只增不减的。iOS 客户端的协议实现则需紧跟升级。
2. **视觉标准:UI 标识请统一用 VLESS**,而不是 VLess / Vless / vless,配置文件不受影响,代码内则顺其自然。
3. `encryption` 应做成输入框而不是选择框,新配置的默认值应为 `none`,若用户置空则应代填 `none`。
1. Протокол VLESS сам по себе будет обновляться несовместимым образом, но параметры конфигурации клиента в основном будут только добавляться. Реализация протокола в iOS-клиенте должна обновляться соответствующим образом.
2. **Визуальный стандарт: используйте VLESS в качестве идентификатора в пользовательском интерфейсе**, а не VLess / Vless / vless. Настройки в файле конфигурации не затрагиваются, в коде используйте естественный стиль.
3. `encryption` должен быть реализован в виде текстового поля, а не выпадающего списка. Значение по умолчанию для новых настроек должно быть `none`. Если пользователь оставит поле пустым, туда должно быть автоматически подставлено `none`.
## VLESS 分享链接标准
## Стандарт общих ссылок VLESS
Спасибо <img src="https://avatars2.githubusercontent.com/u/7822648?s=32" width="32px" height="32px" alt="a"/> [@DuckSoft](https://github.com/DuckSoft) за предложение!
Подробнее см. [Предложение по стандарту общих ссылок VMessAEAD / VLESS](https://github.com/XTLS/Xray-core/issues/91)
感谢 <img src="https://avatars2.githubusercontent.com/u/7822648?s=32" width="32px" height="32px" alt="a"/> [@DuckSoft](https://github.com/DuckSoft) 的提案!
详情请见 [VMessAEAD / VLESS 分享链接标准提案](https://github.com/XTLS/Xray-core/issues/91)
+128 -125
View File
@@ -1,175 +1,178 @@
# VMess 协议
# Протокол VMess
VMess 是一个加密传输协议,可以作为 Xray 客户端和服务器之间的桥梁。
VMess - это зашифрованный транспортный протокол, который может служить мостом между клиентом и сервером Xray.
## 版本
## Версия
当前版本号为 1。
Текущая версия протокола - 1.
## 依赖
## Зависимости
### 底层协议
### Базовый протокол
VMess 是一个基于 TCP 的协议,所有数据使用 TCP 传输。
VMess - это протокол, основанный на TCP, все данные передаются по TCP.
### 用户 ID
### Идентификатор пользователя
ID 等价于 [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier),是一个 16 字节长的随机数,它的作用相当于一个令牌(Token)。
一个 ID 形如:de305d54-75b4-431b-adb2-eb6b9e546014,几乎完全随机,可以使用任何的 UUID 生成器来生成,比如[这个](https://www.uuidgenerator.net/)。
ID эквивалентен [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) - это 16-байтовое случайное число, которое действует как токен.
ID выглядит следующим образом: de305d54-75b4-431b-adb2-eb6b9e546014, он практически полностью случаен и может быть сгенерирован с помощью любого генератора UUID, например [этого](https://www.uuidgenerator.net/).
用户 ID 可在[配置文件](../../config)中指定。
Идентификатор пользователя можно указать в [файле конфигурации](../../config).
### 函数
### Функции
- MD5: [MD5 函数](https://en.wikipedia.org/wiki/MD5)
- 输入参数为任意长度的 byte 数组
- 输出为一个 16 byte 的数组
- HMAC: [HMAC 函数](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
- 输入参数为:
- H:散列函数
- K:密钥,任意长度的 byte 数组
- M:消息,任意长度的 byte 数组
- Shake: [SHA3-Shake128 函数](https://en.wikipedia.org/wiki/SHA-3)
- 输入参数为任意长度的字符串
- 输出为任意长度的字符串
- MD5: функция [MD5](https://en.wikipedia.org/wiki/MD5)
- Входные данные: массив байтов произвольной длины
- Выходные данные: массив из 16 байтов
- HMAC: функция [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
- Входные данные:
- H: хэш-функция
- K: ключ, массив байтов произвольной длины
- M: сообщение, массив байтов произвольной длины
- Shake: функция [SHA3-Shake128](https://en.wikipedia.org/wiki/SHA-3)
- Входные данные: строка произвольной длины
- Выходные данные: строка произвольной длины
## 通讯过程
## Процесс коммуникации
VMess 是一个无状态协议,即客户端和服务器之间不需要握手即可直接传输数据,每一次数据传输对之前和之后的其它数据传输没有影响。
VMess - это протокол без сохранения состояния, то есть клиент и сервер могут передавать данные напрямую без рукопожатия, и каждая передача данных не влияет на предыдущие или последующие передачи.
VMess 的客户端发起一次请求,服务器判断该请求是否来自一个合法的客户端。如验证通过,则转发该请求,并把获得的响应发回给客户端。
Клиент VMess отправляет запрос, а сервер проверяет, исходит ли этот запрос от легитимного клиента. Если проверка пройдена, сервер пересылает запрос и отправляет полученный ответ клиенту.
VMess 使用非对称格式,即客户端发出的请求和服务器端的响应使用了不同的格式。
VMess использует асимметричный формат, то есть запрос, отправляемый клиентом, и ответ сервера имеют разные форматы.
## 客户端请求
## Запрос клиента
| 16 字节 | X 字节 | 余下部分 |
| -------- | -------- | -------- |
| 认证信息 | 指令部分 | 数据部分 |
| 16 байт | X байт | Оставшаяся часть |
|-------------------------------|------------------|------------------|
| Информация для аутентификации | Часть с командой | Часть с данными |
### 认证信息
### Информация для аутентификации
认证信息是一个 16 字节的哈希(hash)值,它的计算方式如下:
Информация для аутентификации - это 16-байтовое хэш-значение, которое вычисляется следующим образом:
- H = MD5
- K = 用户 ID (16 字节)
- M = UTC 时间,精确到秒,取值为当前时间的前后 30 秒随机值(8 字节, Big Endian)
- K = идентификатор пользователя (16 байт)
- M = время UTC с точностью до секунды, случайное значение в диапазоне ±30 секунд от текущего времени (8 байт, Big Endian)
- Hash = HMAC(H, K, M)
### 指令部分
### Часть с командой
指令部分经过 AES-128-CFB 加密:
Часть с командой шифруется с помощью AES-128-CFB:
- Key:MD5(用户 ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
- IV:MD5(X + X + X + X),X = []byte(认证信息生成的时间) (8 字节, Big Endian)
- Ключ: MD5(идентификатор пользователя + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
- Вектор инициализации: MD5(X + X + X + X), X = []byte(время генерации информации для аутентификации) (8 байт, Big Endian)
| 1 字节 | 16 字节 | 16 字节 | 1 字节 | 1 字节 | 4 位 | 4 位 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | N 字节 | P 字节 | 4 字节 |
| :--------: | :---------: | :----------: | :--------: | :------: | :----: | :----------: | :----: | :------: | :-------: | :--------: | :----: | :----: | :----: |
| 版本号 Ver | 数据加密 IV | 数据加密 Key | 响应认证 V | 选项 Opt | 余量 P | 加密方式 Sec | 保留 | 指令 Cmd | 端口 Port | 地址类型 T | 地址 A | 随机值 | 校验 F |
| 1 байт | 16 байт | 16 байт | 1 байт | 1 байт | 4 бита | 4 бита | 1 байт | 1 байт | 2 байта | 1 байт | N байт | P байт | 4 байта |
|------------------|--------------------------------------------|----------------------------|-------------------------|-----------|-----------|----------------------|-----------------|-------------|-----------|--------------|---------|------------------|---------------------|
| Номер версии Ver | Вектор инициализации для шифрования данных | Ключ для шифрования данных | Аутентификация ответа V | Опция Opt | Остаток P | Метод шифрования Sec | Зарезервировано | Команда Cmd | Порт Port | Тип адреса T | Адрес A | Случайные данные | Контрольная сумма F |
选项 Opt 细节:(当某一位为 1 时,表示该选项启用)
Подробности опции Opt: (если бит равен 1, опция включена)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
| X | X | X | X | X | M | R | S |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| X | X | X | X | X | M | R | S |
其中:
Где:
- 版本号 Ver:始终为 1;
- 数据加密 IV:随机值;
- 数据加密 Key:随机值;
- 响应认证 V:随机值;
- 选项 Opt:
- S (0x01):标准格式的数据流(建议开启);
- R (0x02):客户端期待重用 TCP 连接(Xray 2.23+ 弃用);
- 只有当 S 开启时,这一项才有效;
- M (0x04):开启元数据混淆(建议开启);
- 只有当 S 开启时,这一项才有效;
- 当其项开启时,客户端和服务器端需要分别构造两个 Shake 实例,分别为 RequestMask = Shake(请求数据 IV), ResponseMask = Shake(响应数据 IV)。
- X:保留
- 余量 P:在校验值之前加入 P 字节的随机值;
- 加密方式:指定数据部分的加密方式,可选的值有:
- 0x00:AES-128-CFB;
- 0x01:不加密;
- 0x02:AES-128-GCM;
- 0x03:ChaCha20-Poly1305;
- 指令 Cmd:
- 0x01:TCP 数据;
- 0x02:UDP 数据;
- 端口 Port:Big Endian 格式的整型端口号;
- 地址类型 T:
- 0x01:IPv4
- 0x02:域名
- 0x03:IPv6
- 地址 A:
- 当 T = 0x01 时,A 为 4 字节 IPv4 地址;
- 当 T = 0x02 时,A 为 1 字节长度(L) + L 字节域名;
- 当 T = 0x03 时,A 为 16 字节 IPv6 地址;
- 校验 F:指令部分除 F 外所有内容的 FNV1a hash;
- Номер версии Ver: всегда равен 1;
- Вектор инициализации для шифрования данных: случайное значение;
- Ключ для шифрования данных: случайное значение;
- Аутентификация ответа V: случайное значение;
- Опция Opt:
- S (0x01): стандартный формат потока данных (рекомендуется включать);
- R (0x02): клиент ожидает повторного использования TCP-соединения (устарело в Xray 2.23+);
- Действительна только при включенной опции S;
- M (0x04): включить обфускацию метаданных (рекомендуется включать);
- Действительна только при включенной опции S;
- Если эта опция включена, клиент и сервер должны создать два экземпляра Shake: RequestMask = Shake(вектор инициализации для шифрования данных запроса), ResponseMask = Shake(вектор инициализации для шифрования данных ответа).
- X: зарезервировано
- Остаток P: добавить P байт случайных данных перед контрольной суммой;
- Метод шифрования: указывает метод шифрования для части с данными, возможные значения:
- 0x00: AES-128-CFB;
- 0x01: без шифрования;
- 0x02: AES-128-GCM;
- 0x03: ChaCha20-Poly1305;
- Команда Cmd:
- 0x01: данные TCP;
- 0x02: данные UDP;
- Порт Port: номер порта в формате Big Endian;
- Тип адреса T:
- 0x01: IPv4
- 0x02: доменное имя
- 0x03: IPv6
- Адрес A:
- Если T = 0x01, A - это 4-байтовый адрес IPv4;
- Если T = 0x02, A - это 1 байт длины (L) + L байт доменного имени;
- Если T = 0x03, A - это 16-байтовый адрес IPv6;
- Контрольная сумма F: хэш FNV1a всей части с командой, кроме F;
### 数据部分
### Часть с данными
当 Opt(S) 开启时,数据部分使用此格式。实际的请求数据被分割为若干个小块,每个小块的格式如下。服务器校验完所有的小块之后,再按基本格式的方式进行转发。
Если Opt(S) включена, для части с данными используется следующий формат. Фактические данные запроса разбиваются на несколько блоков, каждый из которых имеет следующий формат. После проверки всех блоков сервер пересылает их в соответствии с базовым форматом.
| 2 字节 | L 字节 |
| :----: | :----: |
| 长度 L | 数据包 |
| 2 байта | L байт |
|---------|--------------|
| Длина L | Пакет данных |
其中:
Где:
- 长度 L:Big Endian 格式的整型,最大值为 2^14;
- 当 Opt(M) 开启时,L 的值 = 真实值 xor Mask。Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte();
- 数据包:由指定的加密方式加密过的数据包;
- Длина L: целое число в формате Big Endian, максимальное значение 2^14;
- Если Opt(M) включена, значение L = истинное значение xor Mask. Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte();
- Пакет данных: пакет данных, зашифрованный указанным методом шифрования;
在传输结束之前,数据包中必须有实际数据,即除了长度和认证数据之外的数据。当传输结束时,客户端必须发送一个空的数据包,即 L = 0(不加密) 或认证数据长度(有加密),来表示传输结束。
До завершения передачи в пакете данных должны быть фактические данные, то есть данные, отличные от длины и данных аутентификации. При завершении передачи клиент должен отправить пустой пакет данных, то есть L = 0 (без шифрования) или длину данных аутентификации (с шифрованием), чтобы сигнализировать о завершении передачи.
按加密方式不同,数据包的格式如下:
Формат пакета данных зависит от метода шифрования:
- 不加密:
- L 字节:实际数据;
- AES-128-CFB:整个数据部分使用 AES-128-CFB 加密
- 4 字节:实际数据的 FNV1a hash;
- L - 4 字节:实际数据;
- AES-128-GCM:Key 为指令部分的 Key,IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增,每个数据包加 1;IV 为 指令部分 IV 的第 3 至第 12 字节。
- L - 16 字节:实际数据;
- 16 字节:GCM 认证信息
- ChaCha20-Poly1305:Key = MD5(指令部分 Key) + MD5(MD5(指令部分 Key)),IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增,每个数据包加 1;IV 为 指令部分 IV 的第 3 至第 12 字节。
- L - 16 字节:实际数据;
- 16 字节:Poly1305 认证信息
- Без шифрования:
- L байт: фактические данные;
- AES-128-CFB: вся часть с данными шифруется с помощью AES-128-CFB
- 4 байта: хэш FNV1a фактических данных;
- L - 4 байта: фактические данные;
- AES-128-GCM: ключ - это ключ из части с командой, вектор инициализации = count (2 байта) + IV (10 байт). count начинается с 0 и увеличивается на 1 для каждого пакета данных; IV - это байты с 3 по 12 из вектора инициализации части с командой.
- L - 16 байт: фактические данные;
- 16 байт: данные аутентификации GCM
- ChaCha20-Poly1305: ключ = MD5(ключ из части с командой) + MD5(MD5(ключ из части с командой)), вектор инициализации = count (2 байта) + IV (10 байт). count начинается с 0 и увеличивается на 1 для каждого пакета данных; IV - это байты с 3 по 12 из вектора инициализации части с командой.
- L - 16 байт: фактические данные;
- 16 байт: данные аутентификации Poly1305
## 服务器应答
## Ответ сервера
应答头部数据使用 AES-128-CFB 加密,IV 为 MD5(数据加密 IV),Key 为 MD5(数据加密 Key)。实际应答数据视加密设置不同而不同。
Данные заголовка ответа шифруются с помощью AES-128-CFB, вектор инициализации - MD5(вектор инициализации для шифрования данных), ключ - MD5(ключ для шифрования данных). Фактические данные ответа зависят от настроек шифрования.
| 1 字节 | 1 字节 | 1 字节 | 1 字节 | M 字节 | 余下部分 |
| ---------- | -------- | -------- | ---------- | -------- | ------------ |
| 响应认证 V | 选项 Opt | 指令 Cmd | 指令长度 M | 指令内容 | 实际应答数据 |
| 1 байт | 1 байт | 1 байт | 1 байт | M байт | Оставшаяся часть |
|-------------------------|-----------|-------------|-----------------|--------------------|---------------------------|
| Аутентификация ответа V | Опция Opt | Команда Cmd | Длина команды M | Содержимое команды | Фактические данные ответа |
其中:
Где:
- 响应认证 V:必须和客户端请求中的响应认证 V 一致;
- 选项 Opt:
- 0x01:服务器端准备重用 TCP 连接(Xray 2.23+ 弃用);
- 指令 Cmd:
- 0x01:动态端口指令
- 实际应答数据:
- 如果请求中的 Opt(S) 开启,则使用标准格式,否则使用基本格式。
- 格式均和请求数据相同。
- 当 Opt(M) 开启时,长度 L 的值 = 真实值 xor Mask。Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte();
- Аутентификация ответа V: должна совпадать с аутентификацией ответа V в запросе клиента;
- Опция Opt:
- 0x01: сервер готов повторно использовать TCP-соединение (устарело в Xray 2.23+);
- Команда Cmd:
- 0x01: команда динамического порта
- Фактические данные ответа:
- Если Opt(S) в запросе включена, используется стандартный формат, в противном случае используется базовый формат.
- Формат такой же, как и у данных запроса.
- Если Opt(M) включена, значение длины L = истинное значение xor Mask. Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte();
### 动态端口指令
### Команда динамического порта
| 1 字节 | 2 字节 | 16 字节 | 2 字节 | 1 字节 | 1 字节 |
| ------ | --------- | ------- | ------- | -------- | ---------- |
| 保留 | 端口 Port | 用户 ID | AlterID | 用户等级 | 有效时间 T |
| 1 байт | 2 байта | 16 байт | 2 байта | 1 байт | 1 байт |
|-----------------|-----------|----------------------------|---------|----------------------|------------------|
| Зарезервировано | Порт Port | Идентификатор пользователя | AlterID | Уровень пользователя | Время действия T |
其中:
Где:
- 端口 Port:Big Endian 格式的整型端口号;
- 有效时间 T:分钟数;
- Порт Port: номер порта в формате Big Endian;
- Время действия T: количество минут;
Когда клиент получает команду динамического порта, сервер уже открыл новый порт для связи, и клиент может отправлять данные на этот новый порт. Через T минут этот порт станет недействительным, и клиент должен будет снова использовать основной порт для связи.
## Примечания
- Для обеспечения обратной совместимости все зарезервированные поля должны иметь значение 0.
客户端在收到动态端口指令时,服务器已开放新的端口用于通信,这时客户端可以将数据发往新的端口。在 T 分钟之后,这个端口将失效,客户端必须重新使用主端口进行通信。
## 注释
- 为确保向前兼容性,所有保留字段的值必须为 0。