HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 代理技术全栈手册

    • 代理技术全栈手册 - HiHuo
    • 原理篇

      • 第01章 代理是什么:正向 / 反向 / 透明 / 隧道的统一模型
      • 第02章 代理与网络层级:L3 / L4 / L5 / L7 在哪里截断流量
      • 第03章 一个请求穿过代理的一生:连接生命周期全景
    • 协议篇

      • 第04章 HTTP 代理协议:绝对 URI、CONNECT 隧道、转发头与连接池
      • 第05章 HTTPS 与 TLS 代理:终止 / 透传 / MITM / SNI / mTLS
      • 第06章 SOCKS 协议:SOCKS4/4a/5 与 UDP ASSOCIATE 报文级解析
      • 第07章 HTTP/2、gRPC 与 HTTP/3(QUIC) 代理的挑战
      • 第08章 代理自动配置:PAC / WPAD / 系统代理 / NO_PROXY
    • 层级与转发篇

      • 第09章 L4 代理:TCP/UDP 转发与连接级负载均衡
      • 第10章 L7 代理:协议感知与基于内容的路由
      • 第11章 透明代理:iptables REDIRECT/DNAT、TPROXY 与 eBPF 劫持
      • 第12章 数据搬运的艺术:splice / sendfile / 零拷贝 / io_uring
    • 组件横评篇

      • 第13章 Nginx / OpenResty:反向代理、upstream 与 Lua 可编程
      • 第14章 HAProxy:L4/L7、ACL、健康检查与 stick table
      • 第15章 Envoy:xDS 动态配置与 filter chain,为何是云原生数据面
      • 第16章 Traefik / Caddy:自动服务发现与自动 HTTPS
      • 第17章 Squid 与正向/缓存代理:企业出网、缓存与审计
      • 第18章 mitmproxy:抓包、改包、脚本化调试
      • 第19章 内网穿透与隧道:frp / gost / SSH 隧道 / ngrok
      • 第20章 科学上网生态的技术原理(技术中立)
    • 多语言手写篇

      • 第21章 Go:100 行手写 HTTP/CONNECT + SOCKS5 代理
      • 第22章 Rust:基于 tokio 的高性能 TCP 代理
      • 第23章 Python:asyncio 实现,适合调试与脚本
      • 第24章 C:epoll 裸写与零拷贝,及语言选型对比
    • 容器与K8s篇

      • 第25章 Docker 里的代理:HTTP_PROXY、build/pull 与 daemon 配置
      • 第26章 Sidecar 与流量劫持:Istio init-container 的 iptables 原理
      • 第27章 Ingress 与南北流量:Ingress-nginx 与 Gateway API
      • 第28章 Egress 与出网治理:出口网关、registry mirror、审计
      • 第29章 Service Mesh 数据面:Envoy Sidecar 全链路
    • 进阶篇

      • 第30章 可编程代理:Lua / Wasm / eBPF / xDS,代理的"软件定义"
      • 第31章 性能调优:并发模型、连接池、超时与重试、压测
      • 第32章 排错决策树:502 / 504 / 握手失败 / 环路 / 泄漏
      • 第33章 代理安全:开放代理、SSRF、凭证泄漏与攻击面
    • 底层机制篇

      • 第34章 代理的背压与流控:一个代理最难的部分
      • 第35章 socket 与 TCP 状态机:半关闭、超时、连接生命周期
      • 第36章 HTTP/2 帧、流控与 HPACK:h2 代理的内部机制
      • 第37章 负载均衡算法推导与韧性状态机
      • 第38章 Capstone:把玩具代理改造成生产级骨架
    • 综合实战篇

      • 第39章 企业多跳转发链:拓扑、协议矩阵与贯穿性难题
      • 第40章 端到端实战:把 6 类流量全代理通
      • 第41章 更刁钻的流量:gRPC、长轮询、WebRTC、大文件、双向流
      • 第42章 可落地完整参考实现:一套能跑的多协议转发栈
    • 附录

      • 附录 A:代理协议报文速查(HTTP / SOCKS / PAC / PROXY protocol)
      • 附录 B:组件选型决策树
      • 附录 C:抓包与命令速查

第06章 SOCKS 协议:SOCKS4/4a/5 与 UDP ASSOCIATE 报文级解析

学习目标

  • 逐字节读懂 SOCKS5 握手:方法协商、认证、请求、应答的完整报文
  • 分清 SOCKS4 / SOCKS4a / SOCKS5 的能力边界(IPv6、域名、UDP、认证)
  • 理解 UDP ASSOCIATE 如何中继 UDP,及它为何对 DNS/QUIC/游戏重要
  • 用 ssh -D 一行起一个真 SOCKS5 代理,抓包对照 第21章 的实现

前置知识

  • 第04章 HTTP 代理协议(与之对比)、第21章 Go 手写 SOCKS5(本章是其协议依据)
  • RFC 1928 (SOCKS5)、RFC 1929 (用户名密码认证)

原理

SOCKS 是什么、特殊在哪

SOCKS(SOCKet Secure)是一个**会话层(L5)**代理协议(第02章)。和 HTTP 代理最大的不同:

SOCKS 不关心你传的是什么应用协议。 它只负责"帮我建立一条到 host:port 的连接",之后就盲转字节。所以同一个 SOCKS 代理能代理 HTTP、HTTPS、SSH、SMTP、数据库、BitTorrent——任意 TCP,外加 UDP。

这种"协议无关 + 能远端解析 DNS"的组合,是 SOCKS 在隧道/科学上网场景统治地位的原因。

三个版本的能力演进

特性SOCKS4SOCKS4aSOCKS5
目标用 IPv4✅✅✅
目标用域名(远端 DNS)❌(只能传 IP)✅✅
目标用 IPv6❌❌✅
认证仅 userid仅 userid✅ 无/用户密码/GSSAPI
UDP❌❌✅ UDP ASSOCIATE
BIND(反向连接)✅✅✅

现代几乎只用 SOCKS5,下面以它为主。

SOCKS5 完整握手(四步)

┌────────── 阶段1:方法协商 ──────────┐
Client → Proxy:  VER | NMETHODS | METHODS...
                 05  |   01     |  00              "我支持 1 种方法:无认证(00)"
Proxy → Client:  VER | METHOD
                 05  |  00                          "就用无认证(00)"

┌────────── 阶段2:认证(若选了 0x02 用户密码,RFC 1929)──────────┐
Client → Proxy:  VER | ULEN | UNAME | PLEN | PASSWD
                 01  |  05  | alice |  06  | secret
Proxy → Client:  VER | STATUS
                 01  |  00                          "00=成功,非0=失败"

┌────────── 阶段3:建立连接请求 ──────────┐
Client → Proxy:  VER | CMD | RSV | ATYP | DST.ADDR      | DST.PORT
                 05  | 01  | 00  |  03  | 0b "httpbin.org" | 00 50
                       │CONNECT      │域名 长度11             │端口80
Proxy → Client:  VER | REP | RSV | ATYP | BND.ADDR | BND.PORT
                 05  | 00  | 00  |  01  | 0.0.0.0  |  0000     "00=成功"

┌────────── 阶段4:盲转 ──────────┐
此后这条 TCP 连接成为隧道,双向裸转字节(HTTP/TLS/SSH 任意)

字段字典(写代理/抓包时对着查)

CMD(命令):

  • 0x01 CONNECT:最常用,建立到目标的 TCP 连接
  • 0x02 BIND:让代理监听一个端口,等目标回连(FTP 主动模式等,罕用)
  • 0x03 UDP ASSOCIATE:建立 UDP 中继

ATYP(地址类型):

  • 0x01 IPv4(4 字节)
  • 0x03 域名(1 字节长度 + N 字节,远端 DNS)
  • 0x04 IPv6(16 字节)

REP(应答码)——排错时极有用:

值含义值含义
0x00成功0x05连接被拒绝
0x01一般性失败0x06TTL 过期
0x02规则不允许0x07命令不支持
0x03网络不可达0x08地址类型不支持
0x04主机不可达

对照 第21章 的代码:那里 reply(client, 0x07) 正是"命令不支持",0x08 是"地址类型不支持",0x05 是"连接被拒"——现在你知道每个魔数的来历了。

认证方法(阶段1 的 METHOD):

  • 0x00 无需认证
  • 0x01 GSSAPI
  • 0x02 用户名/密码(RFC 1929)
  • 0xFF 无可接受方法(代理拒绝)

UDP ASSOCIATE:SOCKS 如何中继 UDP

TCP 之外,SOCKS5 还能转 UDP——这对 DNS 查询、QUIC/HTTP3、游戏、VoIP 至关重要。机制比 TCP 绕:

1. 客户端用 TCP 连代理,发 CMD=0x03 (UDP ASSOCIATE)
2. 代理回应里给出一个"UDP 中继地址 BND.ADDR:BND.PORT"
3. 客户端把要发的 UDP 数据包,加上 SOCKS UDP 头,发到这个中继地址
4. 那条 TCP 连接保持存活 = UDP 关联存活;TCP 断 = UDP 中继释放

客户端发往中继的每个 UDP 包,前面要加这个头:

+-----+------+------+----------+----------+----------+
| RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+-----+------+------+----------+----------+----------+
|  2  |  1   |  1   |  变长    |    2     |   变长   |
+-----+------+------+----------+----------+----------+
  全0  分片号  地址类型 目标地址   目标端口   真实载荷

注意:很多 SOCKS 代理只实现了 TCP(CONNECT),不支持 UDP ASSOCIATE。ssh -D 的 SOCKS 代理就不支持 UDP。这是排查"SOCKS 代理下 DNS/QUIC 不通"的头号原因。

SOCKS vs HTTP 代理:怎么选

维度HTTP 代理SOCKS5 代理
协议层L7(明文 HTTP)/ L4(CONNECT 隧道)L5(会话层)
能代理什么HTTP 直转;其它靠 CONNECT 隧道任意 TCP + UDP
看得懂应用内容明文 HTTP 看得懂完全看不懂(纯隧道)
能改写/缓存 HTTP✅❌
远端 DNS✅(默认)✅(域名 ATYP)
认证Basic/Digest(407)无/用户密码/GSSAPI
典型用途网页代理、缓存、企业出网审计通用隧道、SSH 动态转发、科学上网

一句话:要在 HTTP 层做文章用 HTTP 代理;要通用透明地隧道任意流量用 SOCKS5。


️ 实现 / 命令

实验一:ssh -D 一行起一个真 SOCKS5 代理

SSH 自带 SOCKS5 服务端,这是最方便的真代理:

# -D 1080:在本地 1080 起一个 SOCKS5 代理,流量经 SSH 隧道从 jump-host 出去
ssh -D 1080 -N -C user@jump-host &

# 用它(远端 DNS,不泄漏)
curl --socks5-hostname 127.0.0.1:1080 https://httpbin.org/ip
# {"origin": "<jump-host 的公网IP>"}   ← 源站看到的是跳板机 IP

这条命令把 第01章(隐藏客户端)、第03章(远端 DNS 不泄漏)、本章(SOCKS5)全串起来了。

实验二:抓包对照报文图

# 一边抓 1080,一边发请求
sudo tcpdump -i lo -n -X 'tcp port 1080' &
curl --socks5-hostname 127.0.0.1:1080 http://httpbin.org/ip -s -o /dev/null

在抓包的十六进制里,你会逐字节看到上面的报文图:

05 01 00                              ← 阶段1:VER=5, NMETHODS=1, METHOD=00
05 00                                 ← 代理回:选 00 无认证
05 01 00 03 0b 68 74 74 70 ...        ← 阶段3:CONNECT, 域名, 长度0b(11), "httpbin..."
   ... 00 50                          ← 端口 0x0050 = 80
05 00 00 01 00 00 00 00 00 00         ← 代理回:REP=00 成功

把这段十六进制和 第21章 的 handle() 函数并排看,协议就真正刻进脑子了。

实验三:验证 SOCKS 不支持 UDP 时 DNS 怎么办

# ssh -D 的 SOCKS 不支持 UDP ASSOCIATE。用 --socks5(本地DNS)时 DNS 走本地 UDP(没经代理)
# 用 --socks5-hostname 时,DNS 由"代理远端"用 TCP 帮你查(绕开了 UDP 限制)
# 这就是为什么科学上网客户端要么用支持 UDP 的代理,要么强制 DNS-over-TCP/远端解析
curl --socks5-hostname 127.0.0.1:1080 https://example.com/ -s -o /dev/null -w "%{http_code}\n"
# 200(域名由远端解析,绕开了本地 UDP DNS)

排错

现象REP / 错误根因
curl: (97) SOCKS5 ... Host unreachableREP=0x04代理连不上目标主机
curl: (97) ... connection refusedREP=0x05目标端口没监听
... command not supportedREP=0x07用了 BIND/UDP 但代理只支持 CONNECT
SOCKS 下 DNS/QUIC 不通—代理不支持 UDP ASSOCIATE(如 ssh -D)
想隐藏访问目标却被本地 DNS 看到—用了 --socks5(本地 DNS);改 --socks5-hostname
认证失败阶段2 STATUS≠0用户名/密码错,或代理要 GSSAPI
代理回 0xFF 后断开METHOD=0xFF客户端给的认证方法代理都不接受

本章小结

  • SOCKS 是会话层、协议无关的代理:建好到 host:port 的连接后只盲转字节,能转任意 TCP/UDP。
  • SOCKS5 握手四步:方法协商 → 认证 → CONNECT 请求(ATYP) → 应答(REP),全是紧凑二进制。
  • UDP ASSOCIATE 中继 UDP(DNS/QUIC/游戏),但很多代理不实现,是不通的常见原因。
  • 选型:HTTP 层做文章用 HTTP 代理;通用隧道任意流量用 SOCKS5。ssh -D 是最顺手的真 SOCKS5。

下一章 第07章,我们回到 HTTP 世界的前沿:HTTP/2 的多路复用、gRPC 的流、HTTP/3(QUIC) 的连接迁移,给代理带来了哪些全新挑战。

Prev
第05章 HTTPS 与 TLS 代理:终止 / 透传 / MITM / SNI / mTLS
Next
第07章 HTTP/2、gRPC 与 HTTP/3(QUIC) 代理的挑战