Skip to content

Socks5 server: More standard UDP ASSOCIATE (RFC 1928)#6149

Merged
RPRX merged 17 commits into
mainfrom
socks5-udp
May 28, 2026
Merged

Socks5 server: More standard UDP ASSOCIATE (RFC 1928)#6149
RPRX merged 17 commits into
mainfrom
socks5-udp

Conversation

@Fangliding
Copy link
Copy Markdown
Member

本来不想弄的 但是想了想监听随机UDP端口才是RFC规定的行为 那就搓一个吧
优势:没有鉴权问题 可以user路由 同端口不再有UDP监听
劣势:
性能会比以前更差
UDP生命周期和TCP绑定在一起(RFC规定)

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 17, 2026

sockopt 处理了吗

@Fangliding
Copy link
Copy Markdown
Member Author

Fangliding commented May 17, 2026

没处理 为了把这些破参数都丢进去已经很麻烦了

@Fangliding
Copy link
Copy Markdown
Member Author

我看了一下除非有人附加customsockopt不然现有的sockopt里好像也没针对udp listen的部分

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 17, 2026

interface 会影响的吧

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 17, 2026

另外 Xray Socks5 客户端的实现能连上这个吧

@Fangliding
Copy link
Copy Markdown
Member Author

interface 会影响的吧

不会 因为它绑死监听的具体地址(socks5特性)

@Fangliding
Copy link
Copy Markdown
Member Author

另外 Xray Socks5 客户端的实现能连上这个吧

客户端实现是标准的 有test可以跑通

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 23, 2026

看了下代码,貌似没限制 remoteAddr 必须相同,最好还是限制一下?

@Fangliding
Copy link
Copy Markdown
Member Author

看了下代码,貌似没限制 remoteAddr 必须相同,最好还是限制一下?

它会认第一个到的包作为远端 理论上从 udp associate 验证成功到客户端第一个包到有微弱的窗口可以take over这个转发请求 不过能看到这个过程的中间人一般也能直接看到连接密码

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 23, 2026

依稀记得 Socks5 RFC 似乎客户端可以声明要用哪个地址来发 UDP 包?可以只监听那个地址,虽然一般都留空

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 23, 2026

忘了 listen 能不能实现这个,总之第一个包还是限制下 remote IP 吧

@Fangliding
Copy link
Copy Markdown
Member Author

昨晚上弄了忘了push上来了

@RPRX RPRX changed the title Socks5 udp Socks5 server: More standard UDP ASSOCIATE (RFC 1928) May 24, 2026
@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 24, 2026

依稀记得 Socks5 RFC 似乎客户端可以声明要用哪个地址来发 UDP 包?

确实是这样,实现一下,优先限制为指定的来源二元组(端口为 0 的话先不限端口),否则限制为 TCP conn 的 remoteAddr

@Fangliding
Copy link
Copy Markdown
Member Author

根据我的研究市面上99%的实现都是填0 因为nat存在

@Fangliding
Copy link
Copy Markdown
Member Author

现在这样按源限制跟之前的udp filter是一样的 也没见到任何complain

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 24, 2026

那肯定是啊,但迟早有人就这个问题开个 issue,不如直接实现了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 24, 2026

之前 udp filter 没实现这个的主要考虑是防止有密码的用户往服务端 map 塞垃圾,毕竟那个简单实现没清理机制

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 24, 2026

端口为 0 的话先不限端口

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

@Fangliding 上面那个实现一下

@Fangliding
Copy link
Copy Markdown
Member Author

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.

没有说可以分开

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

猜到了但还是想实现一下,这个挺实用的,虽然公网不该用 Socks

@Fangliding
Copy link
Copy Markdown
Member Author

Fangliding commented May 26, 2026

这实用在哪 都找不到一个客户端实现 这么改还非标
像ss之类选择uou的都改成仅udp协商了 破事少很多

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

TCP 有密码时是不是没实现粘包发送?不过这个无所谓,现在与 RFC 1928 相比似乎就上述两个非标行为,可以在文档中标出

Comment thread proxy/socks/protocol.go Outdated
@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

@Fangliding 看下 review,其它的你再看下没啥问题我就合了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

@Fangliding 判断写反了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

@Fangliding Read() 里 remote 改成放外面定义,然后把第一个 return 和第二个 continue 都改成 break(判断要取反)

@Fangliding
Copy link
Copy Markdown
Member Author

不还是你用网页版改一下吧 这机枪往左移五米我怕又改岔了(

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

本来我还在想这里的 race 问题,后来才发现不对啊就不该有 store 的 race,反而现在的代码有极短的时间窗口会被夺舍

@Fangliding 改成 NewTempUDPConn 包揽 Listen,而 remote 在之前就准备好,Read() 内也无需考虑 store 的 race 了

@Fangliding
Copy link
Copy Markdown
Member Author

Fangliding commented May 26, 2026

udp listen在外面的原因是要它自动分配的端口给还给客户端 改地方无非就是把本来传udp conn的地方改成传地址 完了再从这个tempUDPConn取出来 不能省多少地方
至于这个 "而 remote 在之前就准备好,Read() 内也无需考虑 store 的 race 了" 我没看懂是啥

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

我的意思是并不需要 CompareAndSwap,且现在的代码由于 Read() 发生在 tempUDPConn 完全准备好后所以其实不会被夺舍

我刚改了下 Read(),“而 remote 在之前就准备好”指的是 remote 在 NewTempUDPConn 前就确定,改成明确的串行会更清晰

@Fangliding
Copy link
Copy Markdown
Member Author

Fangliding commented May 26, 2026

remote要等远程发来的第一个包怎么在NewTempUDPConn之前确定
如果说先发来的的那种情况又要在后面加参数 一般而言构建函数只要求必要的东西 有 nullable 的参数会显得很冗长 不如在后面追加 这种东西的完整解决办法是在最后放数个可变长的opt函数 像之前那个 tls.WithNextProto 一样 要么传个config进去(类似tls config) 但是这地方又不要扩展性很明显没必要

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 26, 2026

@Fangliding 我指的就是先发来的那种情况,“改成明确的串行会更清晰”,不是为了省代码

@KobeArthurScofield 话说这个 push & pr 同时存在时跑两次 test 能不能改成只跑 push 的

@KobeArthurScofield
Copy link
Copy Markdown
Contributor

话说这个 push & pr 同时存在时跑两次 test 能不能改成只跑 push 的

有空看看, 不知道能不能弄优雅

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 28, 2026

想到了一个更好的方式,直接写了一下:反正一定有 expected IP,一开始就存进 ExpectedRemote(允许 port 留空),简化了代码,串行顺序也清晰了且无需 nil 判断了,RemoteAddr() 还能读半个,明确是 net.UDPAddr 无需取 string,对比效率更高

@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 28, 2026

更精确的 c.Timer.Update(),暂时没别的问题了准备合了

@RPRX RPRX merged commit e26f5e9 into main May 28, 2026
78 checks passed
@RPRX
Copy link
Copy Markdown
Member

RPRX commented May 28, 2026

优先限制为指定的来源二元组(端口为 0 的话先不限端口)

脑洞了一下,比如说想套娃,翻墙后使用国外的 Socks5 代理,然而客户端有某种分流导致 TCP/UDP 走了不同的节点

由于 Xray Socks5 服务端默认要求 UDP 来源 IP 与 TCP 相同,所以 Socks5 客户端需要明确指定 UDP 来源 IP

@KobeArthurScofield
Copy link
Copy Markdown
Contributor

话说这个 push & pr 同时存在时跑两次 test 能不能改成只跑 push 的

#6090

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants