-
在线客服
工作日:9:00-24:00
-
商务合作
15366085265
-
QQ联系方式
1872421339
-
大客户经理
宋经理
本文从工程师视角深入解析 SOCKS5 UDP 转发机制,包括协议结构、封包格式、地址类型解析、代理端实现原理。
很多开发者以为 SOCKS5 的 UDP 就是简单的“把客户端 UDP 包转发给服务器”。这是误解。
SOCKS5 UDP 工作机制:
1. 必须先走 TCP 通道进行认证与 UDP ASSOCIATE 协商;
2. 客户端需要等待代理服务器返回 UDP 转发表地址;
3. 客户端发送的每一个 UDP 数据包都必须采用 SOCKS5 UDP 封包格式,而不是原始 UDP 包。
SOCKS5 UDP 转发使用固定格式封包,结构如下:
+----+----+----+----+------+------+------+
|RSV | RSV| FRAG | ATYP | DST.ADDR ... |
+----+----+----+----+------+------+------+
| DST.PORT | DATA ...
+----------+-------------------------------+
结构说明:
RSV:2 字节保留字段,应为 0x0000
FRAG:分片标志,一般为 0
ATYP:地址类型(IPv4 / 域名 / IPv6)
DST.ADDR:目标地址
DST.PORT:目标端口
DATA:真实业务数据
ATYP 决定 DST.ADDR 字段的解析方式:
ATYP = 0x01 → IPv4(4 字节)
ATYP = 0x03 → 域名(1 字节长度 + N 字节域名)
ATYP = 0x04 → IPv6(16 字节)
UDP ASSOCIATE 是客户端向 SOCKS5 代理声明:“我要使用 UDP 代理。”代理服务器会返回一个地址供客户端发送 UDP 封包。
TCP → UDP ASSOCIATE
代理 → 返回 UDP 监听地址
客户端 → 将 SOCKS5 UDP 封包发往该地址
以下代码展示完整的 SOCKS5 UDP 包解析过程(含 IPv4 / 域名 / IPv6):
def parse_socks5_udp(data):
rsv, frag, atyp = struct.unpack("!HBB", data[:4])
if rsv != 0 or frag != 0:
return None
offset = 4
if atyp == 0x01: # IPv4
addr = socket.inet_ntoa(data[offset:offset+4])
offset += 4
elif atyp == 0x03: # 域名
domain_len = data[offset]
offset += 1
addr = data[offset:offset+domain_len].decode()
offset += domain_len
elif atyp == 0x04: # IPv6
addr = socket.inet_ntop(socket.AF_INET6, data[offset:offset+16])
offset += 16
port = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
payload = data[offset:]
return addr, port, payload
1. 客户端通过 TCP 完成认证与 UDP ASSOCIATE。
2. 客户端将包含 SOCKS5 头的 UDP 包发送给代理。
3. 代理解析头部,从 DATA 中取出真实数据。
4. 代理使用原始 UDP 套接字将 payload 发往 DST.ADDR:DST.PORT。
5. 服务端返回的数据也需要重新封装 SOCKS5 UDP 包,回发给客户端。
✓ 客户端直接发送原始 UDP(未封装 SOCKS5 头)
✓ DNS ATYP 长度字段错误
✓ IPv6 地址未按 16 字节处理
✓ FRAG ≠ 0 被丢弃
✓ 客户端忽略代理返回的转发地址,导致 NAT 失败
SOCKS5 UDP 不是简单 UDP 转发,而是必须带封包格式的代理模式。
ATYP 决定如何解析地址,DATA 承载真实内容。
理解封包结构与代理逻辑,是实现 SOCKS5 UDP 的核心。