RFC文档

首先要实现这个特性,那么必不可少的,需要去查看这个定义此特性的RFC1928

这里要吐槽一下,由于此RFC文档又使用了传统艺能 – xxx MAY xxx,所以文档对于此功能的描述是模糊的,很多相关问题并没有详细的说明,所以可能各人的实现方法也会有些许的不同,但是大体上还是有一个共识,不会过分影响其核心的功能

通过阅读此RFC,可以得出主要的几个要点:

  • UDP Associate中的每一个UDP Listener生存周期必须与其最初协商时所使用的tcp链接相关,即一个UDP Associate请求最终会对应一条tcp链接和一个UDP Listener,当tcp链接断开时,UDP Listener也将会退出,反之亦然
  • 在udp客户端发出的UDP Associate请求中应当带上自己期望发送数据的源端口及源地址,而socks5服务器可以选择是否基于此来对数据来源进行限制(RFC原文:The UDP ASSOCIATE request is used to establish an association within the UDP relay process to handle UDP datagrams. The DST.ADDR and DST.PORT fields contain the address and port that the client expects to use to send UDP datagrams on for the association. The server MAY use this information to limit access to the association. If the client is not in possesion of the information at the time of the UDP ASSOCIATE, the client MUST use a port number and address of all zeros.)
  • UDP Associate中的所有udp请求都应当带上一个标准的包头,标示此udp包的目的地(供socks5 server解析)以及标示此包的响应者(供UDP Client解析)
  • socks5服务器可以选择不处理分片的udp请求,UDP Client应当自己负责重传丢包的问题
  • UDP Associate的协商是在最初的TCP连接上实现的,即要用UDP Associate,必须先像TCP Connect一样先打开一个tcp链接来与socks5服务器通讯,完成相关协商后,再打开UDP通讯信道进行实际的UDP包relay

在以上的几个要点中,第二点有个问题就是很多支持socks5代理的udp客户端并没有严格执行这一标准,在UDP Associate请求包里携带的仍然是目标ip及port,为了保持对这种客户端的兼容,我在具体实现的过程中以第一个向协商后的udp端口发送数据的源ip+port作为标准(因为一般在UDP Associate后,客户端会在极短的时间内向协商好的端口发送数据),放弃了以UDP Associate包作为标准的尝试。

另外,虽然RFC文档中没有说明是否一个UDP Associate链接应当只对应一个目的IP+port,但是基于其第三条要点,推测其大概率允许一个UDP Associate所创建的listener对应多个目的IP+port,只要在标准的包头中定义好此包的目的IP+port即可

实现过程

由于网上开源的socks5服务器99%都是单独的服务器,不会涉及多跳的问题,故而我的工具实现起来会较复杂一些

首先agent需要在接收到client发来的UDP Associate请求后,现在自身机器上打开一个UDP Listener,之后向admin提交启动UDP Associate的消息,admin在收到此消息后也在本地打开一个UDP listener,并且回复agent,告知自身listener的详细信息,agent在收到此消息后,将此详细信息中包含的listener地址+端口以标准的socks5响应回复给client。此时client就可以向admin的此listener发送数据了,admin会将这些数据打包后发送给对应的agent,agent解析并保存要点三中的标头,将数据包发送给对应IP+port。agent在收到相关udp包的回复包后,查找存储的标头,用此标头重新封装回复包,打包返回给admin,admin在收到此数据包后,查找对应的UDP Listener,将数据包由对应的UDP Listener发送给client,从而完成通讯

后记

实际上UDP Associate在实际应用中非常小众,一方面是应用场景不够多,另一方面就是性能也不够好(一个UDP Associate需要消耗一个UDP Listener和一个TCP Conn),还有就是对于client要求也比较高,要求client自身拥有处理丢包重传的能力,socks5服务器是不管理丢包重传方面的问题的。