编程语言琐碎

个人觉得需要深入学习的几个编程语言,即 C/C++、Java、Shell、Perl,当然还有 Linux,我还琢磨着什么时候将电脑的系统换为 Linux 呢。

深入学习的几个语言

  • C 语言(网络编程)
  • Java 语言(吃饭用的)
  • Shell(sed、awk、perl)

关于透明代理

Linux 中有两种透明代理方式(只讨论 TCP 和 UDP):REDIRECTTPROXY

  • REDIRECT:本质还是 DNAT,即修改数据包的 DST_ADDR、DST_PORT。
  • TPROXY:不涉及 NAT,但要求监听套接字设置 IP_TRANSPARENT 选项。

REDIRECT 方式注定只能透明代理 TCP,因为 UDP 在经过 DNAT 之后,无法使用 SO_ORIGINAL_DST 套接字选项来获取原始的目的地址和目的端口。目前的解决办法是,通过 TPROXY 来代理 UDP,使用此方法代理 UDP 时,需要先为监听套接字设置 IP_RECVORIGDSTADDR 选项,然后在收到 UDP 包之后(使用 recvmsg() 而不是 recvfrom()),获取原始目的地址信息,进行透明代理。

解释一下 IP_TRANSPARENT 选项的作用,它的作用有两个:

  1. 可以让套接字进行透明代理
  2. 可以让套接字绑定非本地地址

所以要使用 TPROXY 进行透明代理,IP_TRANSPARENT 套接字选项是必不可少的。

REDIRECT TCP 透明代理思路

  1. 首先,像往常一样,监听一个 TCP 端口(如 60080),然后等待新连接的到来。
  2. 设置 iptables REDIRECT 规则,将需要代理的 TCP 流量重定向至该监听端口。
  3. 新连接到来之后,使用 SO_ORIGINAL_DST socket 选项获取原始目的地址信息。
  4. 新建 TCP 套接字,地址为刚才的真实目的地址,发起 TCP 连接,然后转发数据。

TPROXY TCP 透明代理思路

  1. 首先,创建监听套接字,设置 IP_TRANSPARENT 选项,然后再绑定要监听地址。
  2. 设置 iptables TPROXY 规则,将需要代理的 TCP 流量路由到本地的监听端口。
  3. 新连接到来之后,通过 getsockname() 获取目的地址(即 self-side 地址)。
  4. 新建 TCP 套接字,目的地址为刚才获取的目的地址,发起 TCP 连接,转发数据。

TPROXY UDP 透明代理思路

  1. 创建套接字,设置 IP_TRANSPARENTIP_RECVORIGDSTADDR 选项,绑定要监听地址。
  2. 设置 iptables TPROXY 规则,将需要代理的 UDP 流量重定向或路由到本地监听端口。
  3. 使用 recvmsg() 接收 UDP 包,获取原始地址信息,然后发送该 UDP 数据给目的主机。
  4. 收到响应包后创建新套接字并设置 IP_TRANSPARENT 选项,bind 原始地址再发给客户端。

但是,如果按照上面的 TPROXY UDP 实现思路,那么它将只能用于透明代理 request-response 类型的 UDP 上层协议,如 DNS,但是类似 QUIC 这样的非 request-response 类型的 UDP 上层协议会出现问题,因为端口号变了,导致它们依靠端口号来识别用户的策略失效,即 QUIC 协议无法正常工作。

解决方法:在代理程序内部维护一个 hash table,手动维护一个状态信息,防止端口变化。

Full-Cone NAT(完全锥形 NAT)
一旦内部地址 Iaddr:Iport 成功映射到外部地址 Eaddr:Eport,那么:
所有 src=Iaddr:Iport 的 UDP 包均被 SNAT 替换为 src=Eaddr:Eport
所有 dst=Eaddr:Eport 的 UDP 包均被 DNAT 替换为 dst=Iaddr:Iport

Iaddr:Iport 不需要网关操心,它由内网主机进程自己保持和管理。
Eaddr:Eport 须保持打开状态,因为要随时准备接收外部发来的包。

注意,NAT 环境下,必须由 Iaddr:Iport 先发送数据包才能打开 UDP 隧道!

tls-client 内部的 hashtable 数据结构
key(iaddr:iport) -> value(eport)

tls-server 内部的 hashtable 数据结构
key(eport) -> value(esock)

tls-client 发送的 UDP 请求格式
iaddr:iport:raddr:rport:eport:data\r\n

tls-server 发送的 UDP 响应格式
iaddr:iport:raddr:rport:eport:data\r\n