切换深浅色
第 18 章 VLESS的设计思想
18.1 先把代理请求与承载、安全层分开
VLESS首先是一种代理请求协议:客户端告诉服务器自己使用哪份凭据,希望执行什么命令,以及连接哪个目标;服务器识别这些控制信息后,把后续业务交给相应出站路径。它不是IP协议、TCP替代品,也不要求浏览器直接理解VLESS。浏览器可以继续使用HTTP代理、本地SOCKS或由TUN入口接入,本地代理软件再形成VLESS请求。
从本章起,涉及Xray实现的结论固定到Xray-core提交0888beb38a9ba45440557175e71ce21cf8ba3b13,提交时间2026-10-14。它是源码提交基线,不冒充某个未经核对的发行版本;其他客户端及历史版本的细节可能不同。标准化基础算法与Xray自定义协议分别注明来源,不能把实现字段称为RFC统一规范。
在普通TCP代理场景,客户端的VLESS请求包含协议版本、16字节ID、可选附加信息、命令和目标地址端口。服务器从流中解析这些字段,检查账号与模式,再把目标交给路由与出站处理。目标网站通常只接收到服务器转发的业务内容,不接收这一套VLESS请求头。这正是第8章“每一跳消费自己的控制信息”的具体实现。
要看清它如何走到网络上,需要分成几类职责:
| 层次或机制 | 处理的问题 | 典型状态或材料 |
|---|---|---|
| VLESS基本请求 | 谁在请求、什么命令、哪个目标 | ID、命令、地址、端口、附加信息 |
| 承载方式 | 怎样把双向字节送到对端 | RAW、WebSocket、gRPC、XHTTP等各自连接和帧 |
| 安全通道 | 怎样保护、认证传输的相关内容 | TLS、REALITY或另行启用的VLESS Encryption |
| Vision流处理 | 怎样识别与调整适用数据路径 | flow附加信息、识别状态、填充与转发条件 |
| Xray路由与出站 | 接收后由谁连接实际目标 | 规则、目标解析、直连或另一代理出站 |
这里是职责划分,不表示这些层可以任意排列、任意同时启用,也不表示所有组合都具有相同优化。实际调用顺序和兼容条件在第20—25章核对。尤其是Vision,它不是一个可以用名字替代完整安全通道的加密算法;XHTTP也不是在每条HTTP请求里自动附加一种新的密码学身份认证。
基本VLESS头没有在每个字段旁自动放一份AEAD标签。如果配置明确禁用VLESS Encryption,那么这份基本请求是否受保护,需要看承载外面的TLS或REALITY等实际安全路径。另一方面,当前Xray已经有专门的VLESS Encryption实现,不能拿早期“VLESS本身不加密”的简介直接概括所有现代组合。
源码顺序也证明它们是可区分的处理阶段。客户端出站先通过dialer取得指向代理服务端的连接对象;若启用VLESS Encryption,先用该对象完成其握手包装,再由EncodeRequestHeader写VLESS请求。服务器入站在启用对应解密时,先经过其Handshake包装,再调用DecodeRequestHeader。外层承载与安全连接可以在更早的dialer或listener阶段建立,不能仅因本函数拿到一个Conn就认定底下是裸TCP。
对应证据为固定提交中的出站Process、入站Process及请求编码器。这里仅确认调用边界,握手内部的算法、记录和PFS将在第22章追踪,不能由函数名补出未读机制。
基础请求还可以与首批业务字节连续发送,不需要先像SOCKS方法协商那样再交换一组能力消息。但这只是代理请求层的组织:外层TCP、TLS或其他承载可能仍需要自己的握手;先发送请求也不保证目标连接成功。把“基本代理头可以一并发送”称为“整条访问完全0-RTT”,会混淆不同起点的往返计算。
后续先固定身份、命令与目标的含义,再逐字节解析基本请求和响应。只有这些边界清楚以后,才容易判断加密层究竟保护了哪些控制字段,Vision在何处接管数据,以及XHTTP怎样把同一份代理语义分配到HTTP的上下行。
18.2 UUID、账号查找与认证的实际含义
VLESS基本请求里的用户ID占16字节。配置中常写成带连字符的UUID,例如本书虚构凭据00112233-4455-4677-8899-aabbccddeeff;编码时发送的是16个实际字节,不是36个ASCII字符。连字符用于表示与阅读,不在线上占4字节。具体偏移在下一章展开。
UUID这种格式本身不保证保密或随机。在VLESS请求中,它却承担类似持有者凭据的作用:服务器根据收到的值找到被允许的账号;谁能复制可接受凭据,谁就可能被当作该账号。它没有自动携带签名、有效期或每次连接的新挑战。保护它的传输、防止未授权复用及服务器身份检查,要靠实际安全层和账号管理完成。
固定提交的DecodeRequestHeader先读取版本和16字节ID,随后调用validator.Get。找到账号后继续读附加信息、命令和地址;找不到则返回认证相关错误。由此可知基本检查不是“客户端证明知道一个从不发送的私钥”,而是服务器对输入标识作本地认可。TLS可以保护这个凭据经过网络时的内容,不能把它改称公钥签名认证。
还存在一个必须按版本说明的细节:本书固定的Xray提交不是逐位比较全部16字节。其MemoryValidator与ProcessUUID在存入账号及查找时,都把从0开始的字节6、7清零后作为映射键。因此两份ID若只在这两个字节不同,会进入同一个匹配范围。
text
配置ID字节:00 11 22 33 44 55 46 77 88 99 aa bb cc dd ee ff
另一输入: 00 11 22 33 44 55 40 12 88 99 aa bb cc dd ee ff
查找键: 00 11 22 33 44 55 00 00 88 99 aa bb cc dd ee ff这个构造展示源码的匹配关系,不是建议为不同用户分配这样两份凭据。该实现存储用户时使用映射Store,不能假定这种相同处理键会成为两个独立认证主体。其他实现和历史版本是否采用同样规则,必须另查,不能把本节当成所有VLESS客户端的通用标准。
被略去的两字节在该版本并非完全丢弃。入站保存实际收到的userSentID,并把其字节6—7按大端取成VlessRoute,交给会话及路由上下文;路由条件可以匹配这一值。因此“用于认证查找的处理后ID”与“客户端实际发送的16字节ID”需要区分。实际发送值还被交给后续流量状态,不能在解析后随意换回配置原值。入站元数据赋值、路由条件。
由此也不能直接声称“16字节认证等于128位随机强度”。若凭据按标准随机UUIDv4生成,版本与变体已有固定位置;本版本查找又忽略两个字节。只针对这种均匀随机生成和处理规则,剩余匹配随机位为110,而不是128或通常完整UUIDv4的122。真实安全性还受凭据来源、泄漏、尝试限制和实现影响,这个计数不是对所有配置的攻击成本测量。
Xray还允许某些短字符串经确定性映射形成UUID。固定提交common/uuid的ParseString对允许范围的非标准短输入,使用零命名空间字节与文本进行SHA-1映射,取16字节并设置相应版本和变体。它提供稳定的文本到ID转换,不会增加原字符串的秘密熵,也不是专门的慢速口令派生。如果字符串易猜,映射后的UUID看起来随机也不能消除猜测空间。
账号的email与level主要属于本地管理及策略元数据。基本VLESS请求并没有把一份email字符串或level整数附在ID后面一起发送;服务器从已找到的MemoryUser取得这些属性。客户端的同名设置也不能通过这份基本头自行提高服务器授权级别。flow等真正进入附加信息的字段,则还要接受服务器账号和模式检查。
阅读公共ID类型时也需要看调用点。common/protocol.ID还保存其他协议使用的派生材料,但VLESS编码器在这里调用的是ID.Bytes,写出原始ID字节。不能因为公共构造器计算过某个散列,就在VLESS线上格式里添加一个并不存在的认证摘要。协议字段由实际写入和读出路径确定。
后续讨论加密机制时,将把这个账号检查与外层服务器认证分开:前者决定是否认可此请求凭据,后者决定客户端在把凭据交出去之前确认了谁。完整组合可以同时需要二者,但二者使用的材料、失败状态和泄露后果都不同。
18.3 命令和目标把一条代理连接交给正确的处理路径
客户端连接代理P的地址,与请求P代连的目标W,是两份不同信息。比如外层连接203.0.113.10:443,VLESS请求的目标却是example.com:443;两个端口都为443不代表同一条连接。外层地址让网络找到P,内层目标让P知道接下来为业务做什么。
固定提交在common/protocol/headers.go定义四个请求命令,VLESS编码器按它们决定是否继续编码目标:
| 命令值 | 含义 | 基本VLESS头是否紧接目标地址端口 |
|---|---|---|
| 01 | TCP目标请求 | 是 |
| 02 | UDP目标请求 | 是 |
| 03 | Mux多路复用入口 | 否,实际子目标在后续复用协议中表达 |
| 04 | 该版本的反向代理入口Rvs | 否,进入相应反向复用处理 |
前三个字节值与SOCKS5的CONNECT、BIND、UDP ASSOCIATE不能按数字互换。VLESS的02不是SOCKS5 BIND,03也不是把后面直接当SOCKS UDP数据。命令命名空间已经由外层协议确定,相同一字节取值并不携带跨协议的共同含义。
普通TCP和UDP请求的目标先写2字节大端端口,再写地址类型和地址。地址类型在本实现是01表示IPv4、02表示域名、03表示IPv6;这又与SOCKS5的地址编号不同。域名方式携带名字,IP方式携带解析结果,不能从某个IP请求恢复它原先来自哪个域名。
域名请求把解析机会留给服务器后续路径,却不规定“必须由VLESS解析器立刻查DNS”。VLESS解析阶段主要恢复地址对象,之后路由器和选定出站可能使用自己的DNS策略,也可能继续把域名交给另一代理。客户端如果已经发送IP,则后续只有这份IP目标,除非另有明确的域名元数据或嗅探机制提供信息。DNS隐私边界仍按真正发出查询的位置判断。
Mux命令在解码器里被赋予内部地址标记v1.mux.cool,用于交给Xray复用处理。这是内部调度约定,不是要求公网DNS解析该名称并连接一个真实站点。入口复用器随后从子会话控制信息中取得各个目标并调度,因而一条外层连接可以有多个逻辑目的地;不能用单一普通TCP头的字段表去读全部后续字节。编码器、复用分发入口。
Rvs命令则进入该提交的反向代理处理。入站检查相应账号配置,取得反向管理对象并建立复用工作连接。它复用既有连接方向提供另一种服务关系,不能仅因为名称“反向”就当作HTTP反向代理;也不表示普通用户自动有权获得任意反向入口。源码还限制配置为反向用途的账号转而使用普通正向命令,体现身份认可之后仍有命令授权。
UDP命令表示服务器为目标提供数据报业务,不表示C—P外层Socket必然是UDP。基本UDP承载可在有序流里为每份内层数据报添加长度;丢失外层TCP段仍可能让后续数据报等待恢复。Vision、XUDP等组合还有另外的封装路径,后文按实际分支说明。只看到用户希望访问UDP目标,无法直接推断外层使用QUIC或不可靠DATAGRAM。
恢复请求后,入站把包含方向和目标的链接交给dispatch。常规分发器结合会话元数据、路由规则和出站对象决定最终如何连接;它可以直连,也可以继续建立另一代理请求。目标限制、用户策略和路由因此不必写进基本VLESS头,属于服务器对已认证请求的处理。入站Process、默认分发器。
目标也不等于HTTP Host或外层TLS SNI。一次请求可以由域名、IP、应用内部名字分别承担路由、服务器选择或网站虚拟主机语义,是否一致需要具体业务判断。VLESS不会因为目标端口443就替浏览器验证目标网站证书,代理服务器也不会天然成为网站TLS身份认证的终点。
最后,请求解析成功只说明控制信息足够且通过相应检查,不保证网络可达。目标拒绝、DNS失败、路由拒绝与承载断开可以发生在不同位置。VLESS的基本响应格式也没有SOCKS5那张结果码表;下一章会说明怎样从实际字段和关闭行为判断其能力边界,而不添加不存在的成功码。
18.4 与VMess的区别在于职责安排
VLESS和VMess都能表达代理目标,也都可能使用相同的外层承载。它们的差别却不只是名称、默认端口或分享链接前缀。基本VLESS选择较小的代理请求结构,把若干安全任务交给独立通道;现代VMess AEAD则把认证头、会话材料和负载保护更紧密地放进自身协议。
这里比较的VMess依据与本章相同的Xray提交,具体为请求编码及AuthID处理。VMess的历史非AEAD格式不混入下表;完整格式与算法将在第27章展开。
| 问题 | 基本VLESS路径 | 本提交的VMess AEAD路径 |
|---|---|---|
| 请求中的用户识别 | 写入16字节ID并查本地账号 | 由账号派生材料处理AuthID与受保护请求头 |
| 时间关联 | 基本头没有时间戳 | AuthID带时间材料并有服务器接受窗口 |
| 会话负载key/IV | 基本头不发送一对专属负载key/IV | 客户端生成会话负载key/IV并放入受保护请求 |
| 基本头的处理 | 顺序解析版本、ID、附加信息、目标 | 经过AEAD头处理再解析请求内容 |
| 持续负载 | 按命令、flow及实际独立安全层处理 | 按所选负载AEAD、分块与选项处理 |
VLESS这一列特意限定为基本路径,不能据此否认当前可选的VLESS Encryption。它另有握手和记录,位于基本头读写之前;其秘密来源和PFS也不能靠VMess的key/IV处理类推。二者都出现AEAD,不表示认证材料、派生标签和线上布局相同。
本提交VMess客户端生成16字节请求负载key、16字节IV及一个响应关联字节,再把目标、选项和这些材料组成请求内容。AuthID内部包含时间、随机材料和结构校验,并用账号派生的密码材料变换;服务端核对时间差和重放记录。VLESS基本头没有对应这一组字段,因此不能对其UUID后面的字节照VMess格式读取。
VMess AuthID的CRC32用于该内部结构的检查,不能脱离其带密钥处理与后续AEAD,独自解释成密码学身份认证。类似地,在源码中看到AES就称“所有负载都用同一把AES密钥”,也会忽略头、长度和正文的不同派生路径。第27章将把这些边界逐一拆开。
安全任务的位置改变后,工程要求也随之改变。基本VLESS的服务端不需要为了这一请求头执行VMess AuthID的时间匹配;但外层TLS证书、REALITY或可选Encryption仍可能有各自的时间与状态要求。不能从“基本头没时间戳”推出整套系统完全不需要正确时钟,更不能省去防重放和凭据保密的分析。
把安全保护交给经过认证的TLS,在某些架构中可以减少自定义重复机制,并利用已成熟的证书与记录体系;若移除了该TLS,又没有有效替代,原本依赖的保证也会消失。保留自有加密则增加相应协议字段、密钥生命周期和实现责任。设计取舍要围绕整套组合,而不是仅问哪个名字“更先进”。
这两种安排也不能直接推出谁在所有网络上更快。需要分别计算请求尺寸、承载帧、握手往返、每条记录的认证工作和实际转发路径。下一节先量化VLESS基本头的成本,避免把结构精简误写成未经测量的吞吐或延迟优势。
18.5 轻量结构的成本要按层计算
称一个协议轻量,至少可能指请求头短、没有额外协商往返、状态较少、密码运算少或实现路径直接。这些含义相关,却不是同一指标。VLESS基本TCP头可以从实际写入顺序直接计算,但不能由此得到整个访问过程的流量或速度。
限定无附加信息、无Vision、无VLESS Encryption且为普通TCP命令,固定部分为版本1字节、ID16字节、附加信息长度1字节、命令1字节和端口2字节,共21字节;后面再加地址类型1字节及地址内容。
| 目标表示 | 地址部分 | 基本请求总长 |
|---|---|---|
| IPv4 | 类型1+地址4 | 26 |
| 11字节域名example.com | 类型1+域名长度1+域名11 | 34 |
| IPv6 | 类型1+地址16 | 38 |
基本响应在附加信息为空时为2字节。这个尺寸没有包含任何承载、安全记录、外层握手或目标网站请求,也不包含ID之外的账号管理成本。Mux与Rvs不发送这里的基本目标,不能直接套用这三行作为所有命令尺寸。
请求头通常在一条相应代理会话开始时发送一次,而不是每个TCP段前再写一遍。业务数据分成十个TCP段,不会自动把26字节乘十;相反,在多条独立代理连接上分别发送头,才需要逐连接计数。若上层复用建立多条子会话,还应加入子会话自己的控制开销。
再构造一个只用于尺寸说明的发送批次:26字节IPv4请求头后接10字节业务内容,总内容36字节。若直接放进一条无填充、16字节标签的TLS1.3应用记录,线上TLS层是36+1+16+5=58字节。若先把同样36字节作为一个客户端WebSocket二进制帧,短长度情况下加6字节头与掩码,得到42字节,再装进同条件TLS记录则为64字节。
这些数值由所列假设确定,不表示每个库都刚好这样合并。实现可以把头和业务分开写,不同写入又可能被缓冲合并;WebSocket消息可分片,TLS也可分记录。一次应用写入、一个承载帧、一条TLS记录和一个线上TCP段不具有固定一一对应关系。实际封装必须回到对应版本的数据路径。
UDP基本模式还需要逐报文长度,Vision有自己的填充结构,Encryption有握手和加密记录,XHTTP又有HTTP层的请求、响应、流及管理信息。因此基本头的短小只减少一部分固定成本,不能在计算总流量时把后续机制视为不存在。
同样,请求不额外等一轮代理方法选择,并不让DNS、TCP、TLS、KEM或目标连接的等待消失。复用已经建立的外层连接,可能改变启动成本;首次新建时则要计入全部实际阶段。比较两个方案时应指定从哪个事件计时到哪个事件,避免一边统计首次连接、一边只统计已经复用后的数据发送。
实现成本还包括账号查找、缓存、路由、系统调用、内存拷贝和密码处理。短头可以让解析简单,却不能证明CPU或吞吐一定优于另一协议;如果主要瓶颈是链路丢包或应用等待,减少几字节控制字段可能几乎不改变结果。第30章将把这些层次放进统一工程分析,这里只给出能够从格式严格推导的尺寸。
18.6 承载、安全层与flow各有自己的配置作用域
同一份配置里多次出现none、tls或flow,并不意味着它们共同设置一个全局状态。需要先看每个字段由哪一层解析,再看运行时生成哪一种连接对象。
| 字段位置或语义 | 所属职责 | 改变什么 |
|---|---|---|
| streamSettings.network | 承载选择 | RAW、WebSocket、gRPC、XHTTP等实际路径 |
| streamSettings.security | 该承载的安全包装 | 无额外包装、TLS或REALITY等 |
| VLESS出站encryption | VLESS Encryption选择 | 基本请求前是否建立相应加密包装 |
| VLESS入站settings.decryption | 对应服务器解密处理 | 接收时是否先运行Encryption握手 |
| 账号flow及请求附加信息 | VLESS流处理模式 | 是否请求并允许Vision等处理 |
出站encryption可以出现在该版本支持的用户结构或简化出站结构中,入站decryption却是VLESS入站settings字段,不是把用户列表中的encryption原样改个方向。固定提交的配置构建器明确拒绝在入站用户里填encryption,说明字段作用域也属于兼容性条件。VLESS配置构建器。
当VLESS的encryption为none、而streamSettings.security为tls时,含义是没有该可选VLESS Encryption包装,基本请求仍可位于TLS保护之内。反过来,security为none不直接说明完全明文,还要看是否启用了VLESS Encryption。两个none只关闭各自对应机制,必须沿完整路径确定最终保护。
TLS与REALITY在streamSettings.security上是一个选择,不是把同一字段写成二者后自动叠加。XHTTP是network承载选择,不能直接当成另一个security值。当前构建器还对REALITY承载作限制,允许内部协议名tcp、splithttp和grpc所对应的路径;这不是所有承载任意组合的清单。StreamConfig.Build。
Vision通过xtls-rprx-vision这个flow表达。旧security值xtls在本提交已经被构建器作为移除功能处理,不能把旧名称重新填回去就获得Vision。现代Vision既有TLS/REALITY连接分支,也有与Encryption连接对象相配合的分支;进一步是否能够直接拷贝,要继续看实际包装类型和底层承载,而不是以错误提示中的一个历史短句代替全部代码路径。
请求flow也要与服务器账号允许值一致。入站收到Vision请求时检查账号Flow;账号要求Vision而客户端在相关普通TCP请求中不带flow,同样可能被拒绝。因此flow不是“客户端随便写、服务器尽力接受”的装饰,也不是一个可以仅改一端的任意性能提示。附加信息在线上怎样编码、空响应为何不回显同一flow,将在第19、23章解释。
配置接受与运行时满足条件仍需分开。配置解析可能通过,后续却因实际协商成旧TLS、收到不支持命令、包装对象不适用或握手验证失败而终止。反过来,某些优化条件不满足时,程序也可能继续提供正常转发,只禁用那一条优化路径。必须确认具体分支采取拒绝还是回退,不能统一概括为“配置可用”或“功能失效”。
本提交还在出站构建时检查VLESS是否具有TLS等安全包装或有效Encryption;对于它的私有地址分类器判定为非私有的服务端地址,没有这些保护会被拒绝。这是一项特定实现的配置政策,不改变基本VLESS字节结构,也不能据此把所有版本都说成允许运行同样的裸公网配置。出站安全验证。
所以后续解释真实组合时,将同时检查配置作用域、构建限制、握手结果和数据路径。只说“VLESS+某几个名称”不足以确定所有层的顺序、身份、明文位置与优化条件;第25章会以固定源码实际支持的完整配置把这些关系连接起来。
18.7 一次普通VLESS代理会话怎样流过两端
现在选择一条明确路径:浏览器B经本地代理C,使用VLESS over RAW over TLS连接服务器P,访问HTTPS网站W。VLESS Encryption为none,flow为空,不使用Mux或Vision。客户端正确认证P的TLS身份,浏览器另行正确认证W。这里分析固定源码中普通处理的机制,不声称已经在生产机器部署这份场景。
正在绘制图表…
查看图表原文
sequenceDiagram
participant B as 浏览器B
participant C as 本地代理C
participant P as VLESS服务器P
participant W as 网站W
B->>C: 本地入口请求连接网站目标
C->>P: 建立承载及外层TLS,认证P
B->>C: 网站TLS ClientHello字节
C->>P: 外层TLS内:VLESS请求头及首批网站TLS字节
Note over P: 解开外层,查账号、flow和目标,选择出站
P->>W: 建立到网站的独立TCP连接并转发ClientHello
W-->>P: 网站TLS握手字节
P-->>C: 外层TLS内:VLESS响应头及网站回复
C-->>B: 消费代理响应头后,交付网站TLS字节
B->>C: 网站TLS保护的HTTP请求
C->>P: 外层TLS再保护这些网站密文
P->>W: 解外层后转发网站密文
W-->>P: 网站TLS保护的HTTP响应
P-->>C: 外层TLS保护的下行
C-->>B: 解除外层与代理处理,交付网站密文
本地入口怎样向浏览器报告连接状态,取决于该入口协议与实现,不由基本VLESS格式规定。图中箭头也不规定每次写入单独占一条TLS记录。客户端可能把VLESS头与首批业务一并放进缓冲,再一起送出;如果尚无首批业务,也能先发基本请求,随后继续双向转发。
在C侧,出站已经知道代理服务端地址以及最终目标,把目标放入RequestHeader。普通TCP路径为EncodeRequestHeader写控制头,随后持续把本地link.Reader字节送到相应writer;另一个方向同时DecodeResponseHeader,再把剩余字节交回本地link.Writer。这两个方向在任务结构中并行处理,不是等上行全部结束才开始接收回复。
P的外层TLS解密先产生VLESS字节。入站通过缓冲读取版本、ID、附加信息、命令与目标,恢复账号及会话上下文,再为上下行链接配置对应读取和写入路径。目标被交给分发器,而不是让VLESS解析器自己假扮浏览器发一个新HTTP请求。
这一步消耗了代理控制头。假设请求头为34字节,随后立即接网站ClientHello,转给W的流从网站ClientHello开始,不把34字节UUID与地址当作网站握手的一部分。响应方向也一样:P编码VLESS响应头,C把它解析掉,浏览器从网站TLS字节开始继续处理。代理协议因此能够对浏览器和网站保持透明的业务字节转发。
服务器编码响应头的位置还值得精确说明。固定提交先把响应头写进缓冲,设置后续刷新行为,再调用DispatchLink;这不代表当时就已经在线发送,更不能当成SOCKS式“目标连接成功”结果码。客户端读到合法响应格式,也应结合后续业务和错误状态判断具体进展。第19章会逐字节解释那两字节究竟表达什么。
浏览器和W的TLS消息穿过C与P。P可以知道代理请求目标及内层公开握手材料,并观察时序,却没有因解除外层TLS就自动得到网站应用明文。外层TLS的端点是C与P,内层TLS的端点是B与W,两套密钥、记录序号和身份检查独立。P若改写受保护的网站内容,正确的内层认证应使浏览器或W检测失败;P仍能阻断或延迟转发。
若把目标换成明文HTTP,P解除外层后就能看到HTTP内容,这不是VLESS头发生变化,而是内层业务不再有网站TLS保护。若P的出站又选择另一代理,P还会生成下一跳自己的控制信息,原VLESS头并不一路传给所有后续节点。这两种变化都能用前面已经建立的端点和逐跳模型解释。
以上路径依据固定提交中的outbound.Process和inbound.Process。它为后续复杂组合提供基准:新增机制必须指出自己插入哪个读写边界,改变哪些字节和状态,而不能只在图上增加一个名字。
18.8 请求失败由哪一层解释
一次连接失败,可能发生在任何已经建立的边界。把它们全部称为“VLESS认证失败”,会误判协议,也容易把无关字段放到一起解释。沿数据进入顺序,可以区分以下情况:
| 失败位置 | 此时已经完成的部分 | 尚不能推出的结论 |
|---|---|---|
| 外层TCP或承载无法建立 | 可能只完成地址解析或部分运输握手 | 没有证据表明VLESS解析器已收到请求 |
| TLS或REALITY身份/握手检查失败 | 网络路径可能已经连通 | 未必已进入VLESS账号检查 |
| 可选Encryption握手失败 | 底层连接对象已取得 | 不能认定UUID有误,因为基本头尚未解析 |
| VLESS版本或账号检查失败 | 对应基本头前缀已被读取 | 目标地址可能尚未消费 |
| flow、命令或地址不满足要求 | 账号可能已经找到 | 账号认可不等于这个动作被允许 |
| 路由、解析或目标连接失败 | 基本请求可能已完整接受 | 不表示此前安全握手失效 |
客户端只得到一次连接中断时,往往无法从网络现象唯一判断属于哪一行。服务器记录的处理位置、握手状态与明确错误会提供更多证据;这些是实际实现的观察,不是基本响应头中隐藏的一组通用错误码。
回落进一步说明“未按VLESS接受”不等于“所有字节必然丢弃”。固定提交允许在没有启用VLESS Encryption的相关入站中配置fallbacks,把适合回落的早期非VLESS输入交给事先设置的服务。例如外层TLS已经建立,一个普通HTTP客户端发送网站请求,VLESS检查未将其识别为有效代理请求,却可把该流交给配置的网页服务。这提供同一入口的服务分流,不能解释成该HTTP客户端通过了VLESS账号认证。
回落目的地由服务器配置及匹配逻辑选择,不是从一个未认证VLESS请求里任意取目标来开放代理。即使某份无效凭据触发了回落,它得到的也是所配置服务路径;不能仅凭“仍有响应”声称UUID校验被绕过。反过来,fallback也不能作为所有解析错误都能自动恢复的承诺,源码区分哪些早期错误仍可回落,以及哪些后续格式或模式错误直接结束。
启用回落时,固定实现还观察首次读取缓冲,并对过短前缀采取直接回落分支。这是该实现具体的入口判定,不是TCP保证一次Read必有完整请求头。下一章分析ReadFull与缓冲时会说明这种特殊路径,避免把通用流解析模型和回落优化混成一条规则。
VLESS层fallback发生在这个入站接收的安全或解密边界之后;REALITY对未通过其握手认证的连接如何处理,则属于更早的另一套状态机。二者可能在配置界面都被称作“回落”,但触发输入、持有秘密、可见字节和转发目的并不相同。第21章将单独追踪REALITY的非授权握手路径,不能从本节的HTTP回落推导它的全部行为。
本提交配置构建器还明确禁止把非none的decryption与fallbacks同时设置。这是一条可以直接由源码核对的组合约束,不能为了画出一张层次图,就拼出实现拒绝的完整配置。后续案例将同时检查配置构建与实际读写路径,不把逻辑上可以设想的组合冒充支持状态。
协议的设计职责由此落到可检查的操作:基本头如何编码与解析,账号查找接受哪些输入,命令和flow如何限制使用,路由怎样接管,以及回落在哪个边界触发。下一章会用完整十六进制报文逐字段确认这些操作,再把它们同加密、填充和承载产生的额外字节区分开。