Skip to content

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)

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
VisionVision 包装流(参见 XTLS Vision
Mux + XUDPXUDP 帧封装(参见 XUDP

请求编码(encoding.go:30

go
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

go
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

go
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

mermaid
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

go
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 协商的协议(h2http/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

go
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 内部状态:

go
// 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 帧中。出站设置如下:

go
request.Command = Mux
request.Address = "v1.mux.cool"
request.Port = 666  // magic port indicating XUDP

帧格式详见 XUDP

预连接池

出站可以维护预先建立的连接以降低延迟:

go
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)实现可选的后量子加密:

go
// 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 通过反向连接多路复用流量
go
// Server creates reverse outbound on-demand
r := &Reverse{tag: a.Reverse.Tag, picker: picker, client: muxClient}
outboundManager.AddHandler(ctx, r)

实现说明

  1. UUID 为原始字节:不是字符串。16 字节的 UUID 直接发送,而非以十六进制或 base64 字符串形式发送。

  2. Addons 编码:当 Flow 为空时,写入单个 0x00 字节。当设置了 Flow 时,对 Addons 消息进行 protobuf 编码并以其长度(1 字节)为前缀。

  3. 回落是连接级别的:如果 UUID 不匹配,整个连接(包括已读取的字节)将被转发到回落目标。如果配置了代理协议头(PROXY v1/v2),则会在前面添加。

  4. XUDP 魔术端口port == 666 且地址为 v1.mux.cool 表示 XUDP 模式。服务端必须检测此标记以使用 XUDP 帧封装。

  5. Vision 不可移植:通过 unsafe.Pointer 技巧读取 TLS 内部状态仅适用于特定的 Go TLS 实现。其他语言需要替代方案(如自定义 TLS 实现并暴露内部状态)。

  6. 连接复用:预连接池和 mux 允许连接复用。不使用 mux 时,每个 TCP 流对应一个 VLESS 连接。

用于重新实现目的的技术分析。