VLESS 协议
VLESS 是 Xray-core 的旗舰协议——一种轻量级、可扩展的代理协议,不内置加密(依赖传输层安全)。它支持 TCP、UDP、多路复用、XTLS Vision(直接 TLS 透传)、XUDP(逐包 UDP 寻址)以及反向代理。
源码:proxy/vless/、proxy/vless/encoding/、proxy/vless/inbound/、proxy/vless/outbound/
线路格式
请求头
+----------+------+----------+-----------+---------+------+----------+
| Version | UUID | Addon | Command | Port | Addr | Addr |
| (1 byte) | (16) | Len+Data | (1 byte) | (2 BE) | Type | Value |
+----------+------+----------+-----------+---------+------+----------+
Version: 0x00 (always 0)
UUID: 16 bytes (user identity, raw bytes of UUID)
Addon: 1 byte length + protobuf Addons message (or 0x00 for no addons)
Command: 0x01=TCP, 0x02=UDP, 0x03=Mux, 0x04=Reverse
Port: 2 bytes big-endian (omitted for Mux/Reverse)
AddrType: 0x01=IPv4, 0x02=Domain, 0x03=IPv6
Address: 4 bytes (IPv4), 1+N bytes (domain length+domain), 16 bytes (IPv6)响应头
+----------+----------+
| Version | Addon |
| (1 byte) | Len+Data |
+----------+----------+
Version: echoes request version (0x00)
Addon: 1 byte length + protobuf (or 0x00)Addons(Protobuf)
message Addons {
string Flow = 1; // e.g., "xtls-rprx-vision"
bytes Seed = 2; // padding seed
}当 Flow 为空/无时,addon 长度为 0(单个零字节)。当 Flow 被设置时(如 Vision),addons 以 protobuf 编码。
消息体编码
| 模式 | 消息体格式 |
|---|---|
| TCP(无 Vision) | 原始流(无帧封装) |
| UDP | 长度前缀数据包:[2B length BE][payload] |
| Mux | 标准 mux 帧封装(参见 Mux) |
| Vision | Vision 包装流(参见 XTLS Vision) |
| Mux + XUDP | XUDP 帧封装(参见 XUDP) |
请求编码(encoding.go:30)
func EncodeRequestHeader(writer, request, requestAddons) error {
buffer.WriteByte(request.Version) // 1 byte: version
buffer.Write(account.ID.Bytes()) // 16 bytes: UUID
EncodeHeaderAddons(&buffer, requestAddons) // addons
buffer.WriteByte(byte(request.Command)) // 1 byte: command
if command != Mux && command != Rvs {
addrParser.WriteAddressPort(&buffer, addr, port) // address
}
writer.Write(buffer.Bytes())
}请求解码(encoding.go:64)
func DecodeRequestHeader(isfb, first, reader, validator) (userSentID, request, addons, isFallback, error) {
// Read version (1 byte)
// Read UUID (16 bytes)
// Validate user: validator.Get(id)
// If user not found and fallback enabled: return isFallback=true
// Read addons (protobuf)
// Read command (1 byte)
// Read address based on command type
}isfb 参数控制是否启用回落。如果 UUID 与任何用户都不匹配且配置了回落,连接将被移交给回落服务器。
入站处理器
处理器结构(inbound/inbound.go:74)
type Handler struct {
inboundHandlerManager feature_inbound.Manager
policyManager policy.Manager
stats stats.Manager
validator vless.Validator // UUID→user mapping
decryption *encryption.ServerInstance // ML-KEM-768 (optional)
outboundHandlerManager outbound.Manager
defaultDispatcher routing.Dispatcher
fallbacks map[string]map[string]map[string]*Fallback // name→alpn→path
}处理流程(inbound/inbound.go:267)
flowchart TB
Conn([连接]) --> Decrypt{ML-KEM-768<br/>解密?}
Decrypt -->|是| Handshake["后量子握手"]
Decrypt -->|否| ReadFirst
Handshake --> ReadFirst["读取首字节"]
ReadFirst --> Decode["DecodeRequestHeader()"]
Decode --> Valid{UUID 有效?}
Valid -->|是| SetUser["在上下文中设置用户"]
Valid -->|否,回落开启| Fallback["回落处理器"]
Valid -->|否,回落关闭| Reject["拒绝连接"]
SetUser --> CheckMux{是 Mux?}
CheckMux -->|"Mux + XUDP"| XUDP["XUDP:分发<br/>每个 UDP 数据包"]
CheckMux -->|"Mux(非 XUDP)"| Mux["Mux:分发<br/>多路复用流"]
CheckMux -->|"TCP/UDP"| Dispatch["dispatcher.Dispatch(ctx, dest)"]
Dispatch --> Copy["双向拷贝:<br/>客户端 ↔ 管道"]
Fallback --> DetectSNI["获取 TLS SNI + ALPN"]
DetectSNI --> LookupFB["查找回落:<br/>name → alpn → path"]
LookupFB --> ProxyFB["代理到回落<br/>目标(附带首字节)"]Mux 与 XUDP 检测(inbound/inbound.go:176)
func isMuxAndNotXUDP(request, first) bool {
if request.Command != protocol.RequestCommandMux {
return false
}
if first.Len() < 7 {
return true // not enough data, assume regular mux
}
firstBytes := first.Bytes()
// XUDP: session ID = 0, network type = UDP (2)
return !(firstBytes[2] == 0 && // ID high
firstBytes[3] == 0 && // ID low
firstBytes[6] == 2) // Network type: UDP
}XUDP 的检测方式是检查第一个 mux 帧的会话 ID 是否为 0 且网络类型是否为 UDP。
回落系统
回落系统是一个多级路由机制,用于处理不匹配 VLESS 的连接:
fallbacks[name][alpn][path] → Fallback{Dest, Xver}第一级 — 服务器名称(SNI):
- 来自 TLS/REALITY 连接状态
- 允许同一端口上承载多个域名
第二级 — ALPN:
- 来自 TLS 协商的协议(
h2、http/1.1) - 可将 HTTP/2 与 HTTP/1.1 路由到不同目标
第三级 — HTTP 路径:
- 从第一个 HTTP 请求行中解析
- 可将不同路径路由到不同后端
回落目标可以是:
- TCP 地址(
127.0.0.1:80) - Unix 套接字(
/dev/shm/nginx.sock) - 抽象套接字(
@name)
可通过 Xver 设置在前面添加 PROXY 协议头(v1/v2)。
出站处理器
处理流程(outbound/outbound.go:136)
func (h *Handler) Process(ctx, link, dialer) error {
// 1. Dial transport connection (with optional pre-connect pool)
conn, _ := dialer.Dial(ctx, rec.Destination)
// 2. Determine command
if target.Network == UDP { command = UDP }
if target.Address == "v1.mux.cool" { command = Mux }
// 3. Handle XUDP: for UDP with cone NAT, convert to mux
if command == UDP && (flow == XRV || cone) {
command = Mux
address = "v1.mux.cool"
port = 666
}
// 4. Encode request header
EncodeRequestHeader(conn, request, requestAddons)
// 5. Create body writer (may be Vision or XUDP)
serverWriter = EncodeBodyAddons(conn, request, requestAddons, ...)
if command == Mux && port == 666 {
serverWriter = xudp.NewPacketWriter(serverWriter, target, globalID)
}
// 6. Upload: link.Reader → serverWriter
// 7. Download: serverReader → link.Writer
task.Run(ctx, postRequest, getResponse)
}XTLS Vision 检测(出站)
对于 Vision 流控,出站通过 unsafe.Pointer 访问 TLS 内部状态:
// Access Go TLS internal fields
t = reflect.TypeOf(tlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(tlsConn.Conn))
i, _ := t.FieldByName("input") // *bytes.Reader
r, _ := t.FieldByName("rawInput") // *bytes.Buffer
input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))这些 TLS 内部缓冲区可以揭示内层 TLS 握手何时完成,从而允许 Vision 切换到直接透传模式。详情参见 XTLS Vision。
UDP 处理
非 XUDP(简单长度前缀)
对于不使用 cone NAT 的直接 UDP:
[2B length BE][UDP payload]
[2B length BE][UDP payload]
...由 MultiLengthPacketWriter 写入,由 LengthPacketReader 读取。
XUDP(cone NAT)
对于支持 cone NAT 的 UDP,UDP 被包装在带有逐包寻址的 mux 帧中。出站设置如下:
request.Command = Mux
request.Address = "v1.mux.cool"
request.Port = 666 // magic port indicating XUDP帧格式详见 XUDP。
预连接池
出站可以维护预先建立的连接以降低延迟:
if h.testpre > 0 {
// Launch N goroutines that continuously dial and buffer connections
// Connections expire after 2 minutes
h.preConns = make(chan *ConnExpire)
for range h.testpre {
go func() {
for {
conn := dialer.Dial(ctx, dest)
h.preConns <- &ConnExpire{Conn: conn, Expire: time.Now().Add(2*time.Minute)}
time.Sleep(200ms)
}
}()
}
}ML-KEM-768 后量子加密
VLESS 支持通过 ML-KEM-768(前身为 Kyber)实现可选的后量子加密:
// Server side (inbound)
if h.decryption != nil {
connection, err = h.decryption.Handshake(connection, nil)
}
// Client side (outbound)
if h.encryption != nil {
conn, err = h.encryption.Handshake(conn)
}这在 VLESS 协议之前添加了一个密钥封装层,提供独立于传输层的抗量子安全性。
反向代理
VLESS 通过 mux 支持双向反向代理:
- 客户端(出站):使用命令
Rvs(0x04)建立与服务器的 mux 连接 - 服务器(入站):检测反向用户,创建
Reverse出站处理器 - 桥接 worker 通过反向连接多路复用流量
// Server creates reverse outbound on-demand
r := &Reverse{tag: a.Reverse.Tag, picker: picker, client: muxClient}
outboundManager.AddHandler(ctx, r)实现说明
UUID 为原始字节:不是字符串。16 字节的 UUID 直接发送,而非以十六进制或 base64 字符串形式发送。
Addons 编码:当 Flow 为空时,写入单个
0x00字节。当设置了 Flow 时,对 Addons 消息进行 protobuf 编码并以其长度(1 字节)为前缀。回落是连接级别的:如果 UUID 不匹配,整个连接(包括已读取的字节)将被转发到回落目标。如果配置了代理协议头(PROXY v1/v2),则会在前面添加。
XUDP 魔术端口:
port == 666且地址为v1.mux.cool表示 XUDP 模式。服务端必须检测此标记以使用 XUDP 帧封装。Vision 不可移植:通过
unsafe.Pointer技巧读取 TLS 内部状态仅适用于特定的 Go TLS 实现。其他语言需要替代方案(如自定义 TLS 实现并暴露内部状态)。连接复用:预连接池和 mux 允许连接复用。不使用 mux 时,每个 TCP 流对应一个 VLESS 连接。