diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index e549814d0..0ebf7354a 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -52,6 +52,11 @@ export default defineUserConfig<DefaultThemeOptions>({ "进阶技巧", "/document/level-2/" ), + "/development/": sidebar.getDevelopmentSidebar( + "开发指南", + "协议详解", + "/development/" + ), }, navbar: navbar.hans, }, diff --git a/docs/.vuepress/config/sidebar.ts b/docs/.vuepress/config/sidebar.ts index 62d22651e..552ce2efe 100644 --- a/docs/.vuepress/config/sidebar.ts +++ b/docs/.vuepress/config/sidebar.ts @@ -143,4 +143,30 @@ export function getDocumentLv2Sidebar( ]; } -export const a = ""; +export function getDevelopmentSidebar( + title: string, + protocols: string, + path: string +): SidebarConfigArray { + return [ + { + text: title, + isGroup: true, + children: [ + path + "intro/compile.md", + path + "intro/design.md", + path + "intro/guide.md", + { + text: protocols, + isGroup: true, + children: [ + path + "protocols/vless.md", + path + "protocols/vmess.md", + path + "protocols/muxcool.md", + path + "protocols/mkcp.md", + ], + }, + ], + }, + ]; +} diff --git a/docs/development/README.md b/docs/development/README.md new file mode 100644 index 000000000..0b0bc3928 --- /dev/null +++ b/docs/development/README.md @@ -0,0 +1,45 @@ +--- +sidebar: auto +--- + +# 开发指南 + +## 编译文档 + +Xray 支持各种平台, 您可以在多种平台上自行进行交叉编译。 + +请点击[编译文档](./intro/compile.md)以查看具体编译相关内容。 + +## 设计思路 + +Xray 内核提供了一个平台,在其之上可以进二次开发。 + +这个章节阐述了 Xray 的设计目标和架构。 + +请点击[设计思路](./intro/design.md)以了解 Xray 的设计目标和架构。 + +## 开发规范 + +这个章节阐述了获取代码,进行开发,提交 PR 的流程中需要遵循的准则, 以及相关的编码规范。 + +请点击[开发规范](./intro/guide.md)查看 Xray 开发中应遵循的准则。 + +## 协议详解 + +Xray 用到了很多种协议, 您可以通过各种途径获得协议的详细描述。 + +### [mKCP 协议](./protocols/vless.md) + +VLESS 是一个无状态的轻量传输协议,可以作为 Xray 客户端和服务器之间的桥梁。 + +### [VMess 协议](./protocols/vmess.md) + +VMess 是一个加密传输协议,可以作为 Xray 客户端和服务器之间的桥梁。 + +### [Mux.Cool 协议](./protocols/muxcool.md) + +Mux.Cool 协议是一个多路复用传输协议,用于在一条已建立的数据流中传输多个各自独立的数据流。 + +### [mKCP 协议](./protocols/mkcp.md) + +mKCP 是流式传输协议,由 [KCP 协议](https://github.com/skywind3000/kcp)修改而来,可以按顺序传输任意的数据流。 diff --git a/docs/development/build.md b/docs/development/intro/compile.md similarity index 79% rename from docs/development/build.md rename to docs/development/intro/compile.md index 3cd9f71d6..720528c6a 100644 --- a/docs/development/build.md +++ b/docs/development/intro/compile.md @@ -46,6 +46,14 @@ CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main 运行以上命令会在目录下生成 xray 可执行文件。 +::: tip +如果需要编译可以进行 debug 的程序,即可以用 dlv 附加到运行的程序进行调试, 请去掉 ldflags 中的 '-w -s' 选项. + +-w 禁止生成 debug 信息。使用该选项后,将无法使用 gdb 进行调试。 +-s 禁用符号表 +PS:其实用 vscode 或其他 IDE 调试似乎更方便。 +::: + ## 交叉编译: 这里以在 Windows(Powershell) 环境中,编译到 Linux 服务器为例: @@ -66,7 +74,7 @@ go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main ## 可复现构建: -按照上述步骤,能够编译出与 Release 中完全相同的二进制文件。 +按照上述步骤,能够编译与 Release 中完全相同的二进制文件。 ::: warning 请先确认您使用的 Golang 版本与编译 Release 的一致。 diff --git a/docs/development/intro/design.md b/docs/development/intro/design.md new file mode 100644 index 000000000..fdb6aed47 --- /dev/null +++ b/docs/development/intro/design.md @@ -0,0 +1,43 @@ +# 设计目标 + +- Xray 内核提供了一个平台,支持必要的网络代理功能,在其之上可以进二次开发,以提供更好的用户体验; +- 以跨平台为首要原则,以减少二次开发的成本; + +## 架构 + + + +内核分为三层:应用层、代理层和传输层。 + +每一层内包含数个模块,模块间互相独立,同类型的模块可无缝替换。 + +### 应用层 + +应用层包含一些代理层中常用的功能,这些功能被抽象出来,以便在不同的代理模块中复用。 + +应用层的模块应为纯软件实现,与硬件或平台相关的技术无关。 + +重要模块列表: + +- Dispatcher: 用于把入站代理所接收到的数据,传送给出站代理; +- Router: 路由模块,详见 [路由配置](../../config/routing.md); +- DNS: 内置的 DNS 服务器模块; +- Proxy Manager: 代理管理器; + +### 代理层 + +代理层分为两部分:入站代理(Inbound Proxy)和出站代理(Outbound Proxy)。 + +两部分相互独立,入站代理不依赖于某个特定的出站代理,反之亦然。 + +#### 入站代理 + +- 实现 [proxy.Inbound](https://github.com/xtls/Xray-core/blob/main/proxy/proxy.go) 接口; + +#### 出站代理 + +- 实现 [proxy.Outbound](https://github.com/xtls/Xray-core/blob/main/proxy/proxy.go) 接口; + +### 传输层 + +传输层提供一些网络数据传输相关的工具模块。 diff --git a/docs/development/intro/framework.png b/docs/development/intro/framework.png new file mode 100644 index 000000000..194dff8c5 Binary files /dev/null and b/docs/development/intro/framework.png differ diff --git a/docs/development/intro/guide.md b/docs/development/intro/guide.md new file mode 100644 index 000000000..58bea2241 --- /dev/null +++ b/docs/development/intro/guide.md @@ -0,0 +1,134 @@ +# 开发规范 + +## 基本 + +### 版本控制 + +project X 的代码被托管在 github 上: + +- xray 核心 [xray-core](https://github.com/XTLS/Xray-core) +- xray-flutter [xray-flutter](https://github.com/XTLS/Xray-flutter) +- 安装脚本 [Xray-install](https://github.com/XTLS/Xray-install) +- 数据文件 [Xray-rules-dat](https://github.com/XTLS/Xray-rules-dat) +- 配置模板 [Xray-examples](https://github.com/XTLS/Xray-examples) +- xray 文档 [XTLS.github.io](https://github.com/XTLS/XTLS.github.io) + +您可以使用 [Git](https://git-scm.com/) 来获取代码. + +### 分支(Branch) + +本项目的主干分支为 main, main 分支也是发布时所使用的代码分支, 因此需要确保 master 在任一时刻都是可编译可使用的。 + +如果需要开发新的功能 + +- 请新开分支进行开发, 在开发完成并且经过充分测试后, 合并回主干分支. +- 新开分支如没有必要再存在时, 可以去除. + +### 发布(Release) + +<Badge text="WIP" type="warning"/> + +- 建立尝鲜版本和稳定版本两个发布通道 + - 临时版本, 主要用于特定情况的测试(比如从分支 build 的), 于 TG 群内/issue 回复等渠道 发布特定版本 + - 尝鲜版本可以为 daily build , 用于尝鲜和获得即时反馈和再改进. + - 稳定版本为定时更新(比如周更), 合并稳定的修改并发布. + +### 引用其它项目 + +- Golang + - 产品代码建议使用 Golang 标准库和 [golang.org/x/](https://pkg.go.dev/search?q=golang.org%2Fx) 下的库; + - 如需引用其它项目,请事先创建 issue 讨论; +- 其它 + - 不违反双方的协议,且对项目有帮助的工具,都可以使用。 + +## 开发流程 + +### 写代码之前 + +发现任何问题,或对项目有任何想法,请创建 Issue 讨论以减少重复劳动和消耗在代码上的时间。 + +### 修改代码 + +- Golang + - 请参考 [Effective Go](https://golang.org/doc/effective_go.html); + - 每一次 push 之前,请运行:`go fmt ./...` 和 `go fmt -s -l -e -w $(find . -type f -name "*.go" ! -name "*.pb.go")`; + - 每一次 push 之前,请确保测试通过:`go test ./...`; + - 提交 pull request 之前,请确保新增代码有超过 70% 的代码覆盖率(code coverage); +- 其它 + - 请注意代码的可读性。 + +### Pull Request + +- 提交 PR 之前,请先运行 `git pull https://github.com/xray/xray-core.git` 以确保 merge 可顺利进行; +- 一个 PR 只做一件事,如有对多个 bug 的修复,请对每一个 bug 提交一个 PR; +- 由于 Golang 的特殊需求(Package path),Go 项目的 PR 流程和其它项目有所不同 ,建议流程如下: + 1. 先 Fork 本项目,创建自己的 `github.com/your/Xray-core` 仓库; + 2. 克隆自己的 Xray 仓库到本地:`git clone https://github.com/your/Xray-core.git`; + 3. 基于 `main` 分支创建新的分支; + 4. 在自行创建的分支上作修改并提交修改(commit); + 5. 在推送(push)修改完成的分支到自己的仓库前,先切换到 `main` 分支,运行 `git pull https://github.com/v2fly/Xray-core.git` 拉取最新的远端代码; + 6. 如果上一步拉取得到了新的远端代码,则切换到之前自己创建的分支,运行 `git rebase master` 执行分支合并操作。如遇到文件冲突,则需要解决冲突; + 7. 上一步处理完毕后,就可以把自己创建的分支推送到自己的仓库:`git push -u origin your-branch` + 8. 最后,把自己仓库的新推送的分支往 `xtls/Xray-core` 的 `main` 分支发 PR 即可; + 9. 请在 PR 的标题和正文中,完整表述此次 PR 解决的问题 / 新增的功能 / 代码所做的修改的用意等; + 10. 耐心等待开发者的回应。 + +### 对代码的修改 + +#### 功能性问题 + +请提交至少一个测试用例(Test Case)来验证对现有功能的改动。 + +#### 性能相关 + +请提交必要的测试数据来证明现有代码的性能缺陷,或是新增代码的性能提升。 + +#### 新功能 + +- 如果新增功能对已有功能不影响,请提供可以开启/关闭的开关(如 flag),并使新功能保持默认关闭的状态; +- 大型新功能(比如增加一个新的协议)开发之前,请先提交一个 issue,讨论完毕之后再进行开发。 + +#### 其它 + +视具体情况而定。 + +## Xray 编码规范 + +以下内容适用于 Xray 中的 Golang 代码。 + +### 代码结构 + +``` +Xray-core +├── app // 应用模块 +│ ├── router // 路由 +├── common // 公用代码 +├── proxy // 通讯协议 +│ ├── blackhole +│ ├── dokodemo-door +│ ├── freedom +│ ├── socks +│ ├── vmess +├── transport // 传输模块 +``` + +### 编码规范 + +基本与 Golang 官方所推荐做法一致,有一些例外。写在这里以方便大家熟悉 Golang。 + +#### 命名 + +- 文件和目录名尽量使用单个英文单词,比如 hello.go; + - 如果实在没办法,则目录使用连接线/文件名使用下划线连接两个(或多个单词),比如 hello-world/hello_again.go; + - 测试代码使用 \_test.go 结尾; +- 类型使用 Pascal 命名法,比如 ConnectionHandler; + - 对缩写不强制小写,即 HTML 不必写成 Html; +- 公开成员变量也使用 Pascal 命名法; +- 私有成员变量使用 [小驼峰式命名法](https://zh.wikipedia.org/wiki/%E9%A7%9D%E5%B3%B0%E5%BC%8F%E5%A4%A7%E5%B0%8F%E5%AF%AB) ,如 `privateAttribute` ; +- 为了方便重构,方法建议全部使用 Pascal 命名法; + - 完全私有的类型放入 `internal` 。 + +#### 内容组织 + +- 一个文件包含一个主要类型,及其相关的私有函数等; +- 测试相关的文件,如 Mock 等工具类,放入 testing 子目录。 diff --git a/docs/development/protocols/mkcp.md b/docs/development/protocols/mkcp.md new file mode 100644 index 000000000..17fde9838 --- /dev/null +++ b/docs/development/protocols/mkcp.md @@ -0,0 +1,92 @@ +# mKCP 协议 + +mKCP 是流式传输协议,由 [KCP 协议](https://github.com/skywind3000/kcp) 修改而来,可以按顺序传输任意的数据流。 + +## 版本 + +mKCP 没有版本号,不保证版本之间兼容性。 + +## 依赖 + +### 底层协议 + +mKCP 是一个基于 UDP 的协议,所有通讯使用 UDP 传输。 + +### 函数 + +- 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)。每个片段需要单独处理。 + +## 数据格式 + +### 数据包 + +| 4 字节 | 2 字节 | L 字节 | +| ---------- | ---------- | -------- | +| 认证信息 A | 数据长度 L | 片段部分 | + +其中: + +- 认证信息 A = fnv(片段部分),big endian; +- 片段部分可能包含多个片段; + +### 数据片段 + +| 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 + +### 确认片段 + +| 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 字节,表示此序列号的数据已经确认收到 + +注释: + +- 远程主机期待收到序列号 [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: 远端主机自己计算出的延迟 diff --git a/docs/development/protocols/muxcool.md b/docs/development/protocols/muxcool.md new file mode 100644 index 000000000..42c3e6cf3 --- /dev/null +++ b/docs/development/protocols/muxcool.md @@ -0,0 +1,116 @@ +# Mux.Cool 协议 + +Mux.Cool 协议是一个多路复用传输协议,用于在一条已建立的数据流中传输多个各自独立的数据流。 + +## 版本 + +当前版本是 1 Beta。 + +## 依赖 + +### 底层协议 + +Mux.Cool 必须运行在一个已建立的可靠数据流之上。 + +## 通讯过程 + +一个 Mux.Cool 连接中可传输若干个子连接,每个子连接有一个独立的 ID 和状态。传输过程由帧(Frame)组成,每一帧用于传输一个特定的子连接的数据。 + +### 客户端行为 + +当有连接需求时并且没有现有可用的连接时,客户端向服务器发起一个新连接,以下称为“主连接”。 + +1. 一个主连接可用于发送若干个子连接。客户端可自主决定主连接可承载的子连接数量。 +1. 对于一个新的子连接,客户端必须发送状态`New`以通知服务器建立子连接,然后使用状态`Keep`来传送数据。 +1. 当子连接结束时,客户端发送`End`状态来通知服务器关闭子连接。 +1. 客户端可自行决定何时关闭主连接,但必须确定服务器也同时保持连接。 +1. 客户端可使用 KeepAlive 状态来避免服务器关闭主连接。 + +### 服务器端行为 + +当服务器端接收到新的子连接时,服务器应当按正常的连接来处理。 + +1. 当收到状态`End`时,服务器端可以关闭对目标地址的上行连接。 +1. 在服务器的响应中,必须使用与请求相同的 ID 来传输子连接的数据。 +1. 服务器不能使用`New`状态。 +1. 服务器可使用 KeepAlive 状态来避免客户端关闭主连接。 + +## 传输格式 + +Mux.Cool 使用对称传输格式,即客户端和服务器发送和接收相同格式的数据。 + +### 帧格式 + +| 2 字节 | L 字节 | X 字节 | +| ------------ | ------ | -------- | +| 元数据长度 L | 元数据 | 额外数据 | + +### 元数据 + +元数据有若干种类型,由状态 S 来区分。所有类型的元数据都包含 ID 和 Opt 两项,其含义为: + +- ID: 子连接的唯一标识 +- Opt: + - D(0x01): 有额外数据 + +当选项 Opt(D) 开启时,额外数据格式如下: + +| 2 字节 | L 字节 | +| ------ | ------ | +| 长度 L | 数据 | + +### 新建子连接 (New) + +| 2 字节 | 1 字节 | 1 字节 | 1 字节 | 2 字节 | 1 字节 | X 字节 | +| ------ | ------ | -------- | ---------- | ------ | ---------- | ------ | +| ID | 0x01 | 选项 Opt | 网络类型 N | 端口 | 地址类型 T | 地址 A | + +其中: + +- 网络类型 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 地址; + +在新建子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。 + +### 保持子连接 (Keep) + +| 2 字节 | 1 字节 | 1 字节 | +| ------ | ------ | -------- | +| ID | 0x02 | 选项 Opt | + +在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。 + +### 关闭子连接 (End) + +| 2 字节 | 1 字节 | 1 字节 | +| ------ | ------ | -------- | +| ID | 0x03 | 选项 Opt | + +在保持子连接时,若 Opt(D) 开启,则这一帧所带的数据需要被发往目标主机。 + +### 保持连接 (KeepAlive) + +| 2 字节 | 1 字节 | 1 字节 | +| ------ | ------ | -------- | +| ID | 0x04 | 选项 Opt | + +在保持连接时: + +- 若 Opt(D) 开启,则这一帧所带的数据必须被丢弃。 +- ID 可为随机值。 + +## 应用 + +Mux.Cool 协议与底层协议无关,理论上可以使用任何可靠的流式连接来传输 Mux.Cool 的协议数据。 + +在目标导向的协议如 Shadowsocks 和 VMess 协议中,连接建立时必须包含一个指定的地址。 +为了保持兼容性,Mux.Cool 协议指定地址为“v1.mux.cool”。即当主连接的目标地址与之匹配时,则进行 Mux.Cool 方式的转发,否则按传统方式进行转发。 diff --git a/docs/development/protocols/vless.md b/docs/development/protocols/vless.md new file mode 100644 index 000000000..9756068bb --- /dev/null +++ b/docs/development/protocols/vless.md @@ -0,0 +1,5 @@ +# VLESS 协议 + +VLESS 是一个无状态的轻量传输协议,可以作为 Xray 客户端和服务器之间的桥梁。 + +<Badge text="WIP" type="warning"/> diff --git a/docs/development/protocols/vmess.md b/docs/development/protocols/vmess.md new file mode 100644 index 000000000..1a1ee8d01 --- /dev/null +++ b/docs/development/protocols/vmess.md @@ -0,0 +1,175 @@ +# VMess 协议 + +VMess 是一个加密传输协议,可以作为 Xray 客户端和服务器之间的桥梁。 + +## 版本 + +当前版本号为 1。 + +## 依赖 + +### 底层协议 + +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 可在[配置文件](../../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) + - 输入参数为任意长度的字符串 + - 输出为任意长度的字符串 + +## 通讯过程 + +VMess 是一个无状态协议,即客户端和服务器之间不需要握手即可直接传输数据,每一次数据传输对之前和之后的其它数据传输没有影响。 + +VMess 的客户端发起一次请求,服务器判断该请求是否来自一个合法的客户端。如验证通过,则转发该请求,并把获得的响应发回给客户端。 + +VMess 使用非对称格式,即客户端发出的请求和服务器端的响应使用了不同的格式。 + +## 客户端请求 + +| 16 字节 | X 字节 | 余下部分 | +| -------- | -------- | -------- | +| 认证信息 | 指令部分 | 数据部分 | + +### 认证信息 + +认证信息是一个 16 字节的哈希(hash)值,它的计算方式如下: + +- H = MD5 +- K = 用户 ID (16 字节) +- M = UTC 时间,精确到秒,取值为当前时间的前后 30 秒随机值(8 字节, Big Endian) +- Hash = HMAC(H, K, M) + +### 指令部分 + +指令部分经过 AES-128-CFB 加密: + +- Key:MD5(用户 ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21')) +- IV: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 | + +选项 Opt 细节:(当某一位为 1 时,表示该选项启用) + +| 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; + +### 数据部分 + +当 Opt(S) 开启时,数据部分使用此格式。实际的请求数据被分割为若干个小块,每个小块的格式如下。服务器校验完所有的小块之后,再按基本格式的方式进行转发。 + +| 2 字节 | L 字节 | +| :----: | :----: | +| 长度 L | 数据包 | + +其中: + +- 长度 L:Big Endian 格式的整型,最大值为 2^14; + - 当 Opt(M) 开启时,L 的值 = 真实值 xor Mask。Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte(); +- 数据包:由指定的加密方式加密过的数据包; + +在传输结束之前,数据包中必须有实际数据,即除了长度和认证数据之外的数据。当传输结束时,客户端必须发送一个空的数据包,即 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 认证信息 + +## 服务器应答 + +应答头部数据使用 AES-128-CFB 加密,IV 为 MD5(数据加密 IV),Key 为 MD5(数据加密 Key)。实际应答数据视加密设置不同而不同。 + +| 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(); + +### 动态端口指令 + +| 1 字节 | 2 字节 | 16 字节 | 2 字节 | 1 字节 | 1 字节 | +| ------ | --------- | ------- | ------- | -------- | ---------- | +| 保留 | 端口 Port | 用户 ID | AlterID | 用户等级 | 有效时间 T | + +其中: + +- 端口 Port:Big Endian 格式的整型端口号; +- 有效时间 T:分钟数; + +客户端在收到动态端口指令时,服务器已开放新的端口用于通信,这时客户端可以将数据发往新的端口。在 T 分钟之后,这个端口将失效,客户端必须重新使用主端口进行通信。 + +## 注释 + +- 为确保向前兼容性,所有保留字段的值必须为 0。 diff --git a/docs/document/install.md b/docs/document/install.md index 6ba2548ae..66eda7dca 100644 --- a/docs/document/install.md +++ b/docs/document/install.md @@ -23,7 +23,7 @@ Xray 在以下平台中可用: Xray 提供两种验证方式: - ZIP 压缩包的 SHA1 / SHA256 摘要 -- 可复现构建:请参照 [编译 Xray](../development/build.html) +- 可复现构建:请参照 [编译 Xray](../development/intro/compile.md) ## Windows 安装方式