# VLESS 协议

VLESS 是一个无状态的轻量传输协议，可以作为 Xray 客户端和服务器之间的桥梁。

## Request & Response

| 1 字节   | 16 字节   | 1 字节         | M 字节            | 1 字节 | 2 字节 | 1 字节   | S 字节 | X 字节   |
| -------- | --------- | -------------- | ----------------- | ------ | ------ | -------- | ------ | -------- |
| 协议版本 | 等价 UUID | 附加信息长度 M | 附加信息 ProtoBuf | 指令   | 端口   | 地址类型 | 地址   | 请求数据 |

| 1 字节                 | 1 字节         | N 字节            | Y 字节   |
| ---------------------- | -------------- | ----------------- | -------- |
| 协议版本，与请求的一致 | 附加信息长度 N | 附加信息 ProtoBuf | 响应数据 |

VLESS 早在第二个测试版 ALPHA 2 时就已经是上述结构了（BETA 是第五个测试版）：

> “响应认证”被替换为“协议版本”并移至最前，使 VLESS 可以升级换代，同时消除了生成伪随机数的开销。混淆相关结构被替换为附加信息（ProtoBuf）并前移，赋予协议本身可扩展性，相关开销也极小（[gogo/protobuf](https://github.com/gogo/protobuf)），若无附加信息则无相关开销。

我一直觉得“响应认证”不是必要的，ALPHA 时为了提升生成随机数的性能，还用 math/rand 替换 crypto/rand，而现在都不需要了。

“协议版本”不仅能起到“响应认证”的作用，还赋予了 VLESS 无痛升级协议结构的能力，带来无限的可能性。
“协议版本”在测试版本中均为 0，正式版本中为 1，以后若有不兼容的协议结构变更则应升级版本。

VLESS 服务端的设计是 switch version，即同时支持所有 VLESS 版本。若需要升级协议版本（可能到不了这一步），推荐的做法是服务端提前一个月支持，一个月后再改客户端。VMess 请求也有协议版本，但它的认证信息在外面，指令部分则高度耦合且有固定加密，导致里面的协议版本毫无意义，服务端也没有进行判断，响应则没有协议版本。Trojan 的协议结构中没有协议版本。

接下来是 UUID，我本来觉得 16 字节有点长，曾经考虑过缩短它，但后来看到 Trojan 用了 56 个可打印字符（56 字节），就彻底打消了这个念头。服务端每次都要验证 UUID，所以性能也很重要：VLESS 的 Validator 经历了多次重构/升级，相较于 VMess，它十分简洁且耗资源很少，可以同时支持非常多的用户，性能也十分强悍，验证速度极快（sync.Map）。API 动态增删用户则更高效顺滑。
https://github.com/XTLS/Xray-core/issues/158

引入 ProtoBuf 是一个创举，等下会详细讲解。“指令”到“地址”的结构目前与 VMess 完全相同，同样支持 Mux。

总体上，ALPHA 2 到 BETA 主要是：结构进化、清理整合、性能提升、更加完善。这些都是一点一滴的，详见 [VLESS Changes](https://github.com/rprx/v2ray-vless/releases)。

## ProtoBuf

似乎只有 VLESS 可选内嵌 ProtoBuf，它是一种数据交换格式，信息被紧密编码成二进制，TLV 结构（Tag Length Value）。

起因是我看到一篇文章称 SS 有一些缺点，如没有设计错误回报机制，客户端没办法根据不同的错误采取进一步的动作。
（但我并不认同所有错误都要回报，不然防不了主动探测。下一个测试版中，服务器可以返回一串自定义信息。）
于是想到一个可扩展的结构是很重要的，未来它也可以承载如动态端口指令。不止响应，请求也需要类似的结构。
本来打算自己设计 TLV，接着发觉 ProtoBuf 就是此结构、现成的轮子，完全适合用来做这件事，各语言支持等也不错。

目前“附加信息”只有 Scheduler 和 SchedulerV，它们是 MessName 和 MessSeed 的替代者，**当你不需要它们时，“附加信息长度”为 0，也就不会有 ProtoBuf 序列化/反序列化的开销**。其实我更愿意称这个过程为“拼接”，因为 pb 实际原理上也只是这么做而已，相关开销极小。拼接后的 bytes 十分紧凑，和 ALPHA 的方案相差无几，有兴趣的可以分别输出并对比。

为了指示对附加信息（Addons，也可以理解成插件，以后可以有很多个插件）的不同支持程度，下个测试版会在“附加信息长度”前新增“附加信息版本”。256 - 1 = 255 字节是够用且合理的（65535 就太多了，还可能有人恶意填充），现有的只用了十分之一，以后也不会同时有那么多附加信息，且大多数情况下是完全没有附加信息的。真不够用的话，可以升级 VLESS 版本。

为了减少逻辑判断等开销，暂定 Addons 不使用多级结构。一个月前出现过“可变协议格式”的想法，pb 是可以做到打乱顺序，但没必要，因为现代加密的设计不会让旁观者看出两次传输的头部相同。

下面介绍 Schedulers 和 Encryption 的构想，**它们都是可选的**，一个应对流量时序特征问题，一个应对密码学上的问题。

## ~~Schedulers~~ Flow

~~中文名暂称：流量调度器~~（2020-09-03 更新：中文名确定为“流控”），指令由 ProtoBuf 承载，控制的是数据部分。

我之前发现，VMess 原有的 shake “元数据混淆”在 TLS 上完全不会带来有意义的改变，只会降低性能，所以 VLESS 弃用了它。并且，“混淆”这个表述容易被误解成伪装，也弃用了。顺便一提，我一直是不看好伪装的：做不到完全一样，那不就是强特征吗？做得到完全一样，那为什么不直接用伪装目标？我一开始用的是 SSR，后来发现它只是表面伪装骗运营商，就再也没用过了。

那么，“流量调度器”要解决什么问题？它影响的是宏观流量时序特征，而不是微观特征，后者是加密要解决的事情。流量时序特征可以是协议带来的，比如 Socks5 over TLS 时的 Socks5 握手 ，TLS 上不同的这种特征对于监测者来说就是不同的协议，此时无限 Schedulers 就相当于无限协议（重新分配每次发送的数据量大小等）。流量时序特征也可以是行为带来的，比如访问 Google 首页时加载了多少文件、顺序、每个文件的大小，多套一层加密并不能有效掩盖这些信息。

Schedulers 没必要像下面的 Encryption 一样整个套在外面，因为头部的一丁点数据相对于后面的数据量来说太微不足道了。

BETA 2 预计推出两个初级的 Scheduler：Zstd 压缩、数据量动态扩充。进阶操作才是从宏观层面来控制、分配，暂时咕咕。

## Encryption

与 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 并不需要往外移一级，一是因为无法复用很多代码，二是因为会影响控制粒度，看未来的应用就明白了。

加密支持两类形式，一类是加密完全独立，需要额外密码，适合私用，另一类是结合已有的 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 也是类似填法，收到请求时会逐一尝试解密。

并不是所有组合都需逐一尝试：VMess 的加密分为三段，第一段是认证信息，结合了 UUID、alterId、时间因素，第二段是指令部分，以固定算法加密，指令中含有数据部分使用的加密算法，第三段才是重要的数据部分。可以看出，VMess 的加解密方式实际上是多对一（服务端适配），而不仅是结合 UUID。但仅是结合 UUID 来加密也是件相对麻烦的事情，短时间内不会出，鉴于我们现在有 VMessAEAD 可用，也并不着急。若 VLESS 推出了结合 UUID 的加密方式，相当于重构了整个 VMess。

## UDP issues

[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`。

## 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)
