Socks5『未必』可代理UDP
Socks5(下面就简称为s5),掐指一算,岁数已经比站长大不少了,现在成为了被众多设备支持的代理协议之一(另一个是HTTP CONNECT)
s5支持代理UDP,这对于游戏党以及做跨境电商需要直播带货的朋友来说是个福音。但是很多人未必清楚的是:即使是服务端开启了支持,S5也未必可以代理UDP流量
欲知后事如何,请听下面分解
太长不看版
除非特殊实现,否则Socks5无法在同一个端口上面处理TCP和UDP流量。他对于UDP流量的处理实际上是另开一个端口去传输UDP流量
在某些情境下,这种方法会传输失败——比如说服务器防火墙仅开放了s5本身的代理端口,比如说s5服务器位于NAT之后且没做映射,比如说不正确的Socks5 over TLS实现
详解
s5的定义位于RFC1928,我们直接看原文
In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR fields indicate the port number/address where the client MUST send UDP request messages to be relayed.
说人话就是,Socks5在建立连接之后,默认情况下并不是通过原监听端口(比如1080)去接收UDP流量,而是服务端另起一个端口来监听(端口号随机)并告知客户端,客户端把UDP流量发到这个地址上,服务端再把收到的数据转发到指定目标。之后的话,默认处理方式是一直转发,直到原监听端口的TCP连接断开为止
明眼人应该已经看出来问题在哪了:这个随机选择的端口其实是不能预料到的,虽然部分s5代理服务器支持限定端口范围以方便防火墙控制什么的,但是本质上还是一个治标不治本的处理方式,毕竟还是涉及到了『另开端口』
而且不知读者发现没有,规定并没有说明这个地址一定要是原服务器,换句话说,服务端大可指定另外一台机器来专门转发UDP流量,然后让自己事不关己高高挂起
影响?
为方便理解起见,下面场景假设:有一台支持代理UDP(正式说法是UDP Associate)的s5服务器,监听端口1080
场景一:防火墙控制
此时,在服务器的防火墙放通1080,那么客户端就可以连上并代理TCP流量了
但如果此时客户端发起UDP请求,服务器就去指定端口(比如4170)监听UDP,**问题是,防火墙并不知情,因此阻断了4170UDP入站流量,导致客户端流量发不过去,自然也就无法代理
场景二:错误配置的NAT
如果这个S5服务器:
- 是内网机器,而入口只做了1080端口映射
- 是NAT VPS,只能开放有限的端口
以上两个分场景均属于这种情况
和防火墙差不多,『没有设置映射』的请求,因为不知道要转发到哪,会被NAT直接丢弃,更何谈流量代理呢
场景三:错误配置的隧道
早期科学上网教程里面有一种相对少用(但其实好用)的方式:先用Stunnel建立SSL隧道,然后隧道内传输HTTP代理(也就是众所周知的Squid+Stunnel翻墙)
用SSL指代SSL/TLS
这么干其实问题不大,Stunnel的SSL隧道可以提供足够的安全性,因此底层跑的协议可以随意些(不过HTTP代理被主动探测到时特征很明显,不是407 Auth required就是200 Conn ESDT,很容易被干)
所以嘛,就有些聪明人(褒义,毕竟也算不上是大聪明)在想,HTTP可以,Socks5是否可以呢?说干就干,把Stunnel设置为指向1080端口,客户端也相应修改,一连,成了!
.....吗?
此时你会发现,还是和上面的例子一样,TCP正常,UDP不一定行,因为平时上网页流量TCP压了大头,而且s5支持远程域名解析,所以平时使用压根看不出异常,只有在游戏打不了或者跨境电商视频电话打不通,才开始挠头
很显然,此时SSL是没问题的,成功的保护了原始链接。但是当客户端想要访问UDP内容时候,根据s5工作原理,实际上UDP流量并没有经过Stunnel,而是直接发给了目标服务器,换句话说,UDP在裸奔
裸奔,很多情况下,可能能连上,不过虽然说地址交换过程已经在之前的TCP连接(被SSL保护)中完成,但是后续呢?经研究发现,UDP流量的目标地址其实是在流量前面拼接包头来实现的,换句话说,中间人也能抓到目标地址和实际传输内容,如果UDP内容有那么点功夫王不是很喜欢的内容的话......
常见解决方案
要想解决这个问题,最普遍的方案就是换协议,就s5的协议结构来言,进行深度魔改不太适合
经典解决方案。比如shadowsocks,采用的是TCP和UDP各监听一个端口来解决问题,因为它的udp流量会在开头拼接上目标地址,所以也就不存在说需要通过TCP连接来维持udp的情况(删除原因:研究发现:无实际关联)
因为他们本来就是不同的东西
只是他们恰好都有一个东西叫端口
而且恰巧取值范围都是1到65535罢了
但是这种方案的话,如果要对udp流量进行加密等处理,SSL就不太适合了,需要考虑DTLS
(这也就是为什么ss插件很多对udp流量都是直通处理,因为他根本就没办法处理)
(而且这大概也就是为什么人们在手机上装了ss之后,发现节点很快会挂:像这样TCP和UDP分开不同协议(不同混淆)却是同一个端口的流量,特征其实很大)
另外的一个解决方案就是采用UDP over TCP(UoT),直接把所有UDP流量都当成TCP来看待,也就能顺便把一些针对TCP的措施(比如SSL)都加上去,OpenVPN和Vmess和Trojan差不多都是这样
写在最后
水文章一篇
不过我也好久没更过博客了,权当发表一下个人想法吧
(完)