MetaCubeX / mihomo

A simple Python Pydantic model for Honkai: Star Rail parsed data from the Mihomo API.
https://wiki.metacubex.one
MIT License
15.78k stars 2.58k forks source link

[Feature] 支持 Hysteria 2 的 port hopping 功能 #982

Open keakon opened 8 months ago

keakon commented 8 months ago

Verify steps

Description

mihomo 目前支持 hysteria 1 代的 port hopping,却不支持 2 代。我看了 issue 列表,在这个回复中得到了不会支持的答复。

阅读代码后我发现对 hysteria 2 的支持是基于 sing-quic,而后者的开发者表示「暂时没有计划添加这种服务器只能使用特定 Linux 系统 + 特权运行的功能。」。 对于这种服务端不支持就不允许客户端支持的做法,我也也只能选择绕过它来实现了。

Possible Solution

目前我的实现(#981)是在 DialContext() 时判断是否需要更换端口,如果需要,则重新创建一个 hysteria2.Client

由于只读了半天源码,不确定是否有遗漏的地方,需要帮忙看看。

此外,考虑到效率问题,我并没有去加锁,但是经过几小时的测试,并未遇到异常。如果需要加锁的话,应该在调用 h.client.DialConn() 时加个读锁,确保获取到未关闭的 client 即可。

wwqgtxx commented 8 months ago

@keakon 重新创建client会导致一个和原版不一致的现象,即hop的时候源端口号也会发生变化,实际上这会导致quic的整个握手重新发生,建议你还是想办法去修改sing-quic库,反正meta这边的库也是fork的,并不一定要和上游保持完全一致

keakon commented 8 months ago

@wwqgtxx 我 copy 了 hysteria 的源码,目前 TCP 是正常的,UDP 我平时用不到,不确定这么改是否正确,需要帮忙看下。这2天得了流感,实在研究不动了。

990

keakon commented 8 months ago

写了个UDP服务来测试,没遇到问题。但是我并没有用到 ctx、opts 这 2 个参数,不清楚是否对于控制超时、连接有什么特殊用处。

keakon commented 8 months ago

这个实现在与 ClashMetaForAndroid 一起使用时会遇到 hop 后无法继续使用的 bug,原因未知。并且还发现 ticker 在未到期时多次触发的 bug,也不知道原因。

keakon commented 8 months ago

修改了 hop 的实现,不去复制 packets 就正常了。

tkizm1 commented 8 months ago
Hysteria2Option{
Up             string   `proxy:"up,omitempty"`
Down           string   `proxy:"down,omitempty"`
}

up和down还是允许为空吧,和以前的格式不兼容了

tkizm1 commented 8 months ago

初始化的时候,proxy-providers中的proxies某一个不可用,会导致整个group都无效

proxy 12: connect error: timeout: no recent network activity
keakon commented 8 months ago
Hysteria2Option{
Up             string   `proxy:"up,omitempty"`
Down           string   `proxy:"down,omitempty"`
}

up和down还是允许为空吧,和以前的格式不兼容了

已修复

sqkam commented 7 months ago

大佬你好,我魔改了sing-quic库,newClient的时候调用client 的offerNew()函数,然后也是通过ticker触发NewClient给他创建新的client 实际使用很完美,"端口跳跃"了,新的client中conn可以直接使用,不会断流,核心思想就是到了切换时间,先建立连接,再切换client,不需要等到流量到了client.offer出现错误的时候再去建立连接,我愿称他为伪端口跳跃,这个实现方式怎么样?

keakon commented 7 months ago

大佬你好,我魔改了sing-quic库,newClient的时候调用client 的offerNew()函数,然后也是通过ticker触发NewClient给他创建新的client 实际使用很完美,"端口跳跃"了,新的client中conn可以直接使用,不会断流,核心思想就是到了切换时间,先建立连接,再切换client,不需要等到流量到了client.offer出现错误的时候再去建立连接,我愿称他为伪端口跳跃,这个实现方式怎么样?

我不是大佬啊。 看上去可能会有额外的开销,你可以看看 offerNew() 里的后半段,创建了一个 http3.RoundTripper 对象来发起认证请求。 开销最低的方式就是我现在的方案,直接复用之前的连接,往其他端口写,但这不符合原版实现。 其次是创建一个新的 UDP 连接,但是需要同时监听和读写两个连接的数据,说实话这更适合用 epoll 来写(不过不跨平台)。

sqkam commented 7 months ago

大佬你好,我魔改了sing-quic库,newClient的时候调用client 的offerNew()函数,然后也是通过ticker触发NewClient给他创建新的client 实际使用很完美,"端口跳跃"了,新的client中conn可以直接使用,不会断流,核心思想就是到了切换时间,先建立连接,再切换client,不需要等到流量到了client.offer出现错误的时候再去建立连接,我愿称他为伪端口跳跃,这个实现方式怎么样?

我不是大佬啊。 看上去可能会有额外的开销,你可以看看 offerNew() 里的后半段,创建了一个 http3.RoundTripper 对象来发起认证请求。 开销最低的方式就是我现在的方案,直接复用之前的连接,往其他端口写,但这不符合原版实现。 其次是创建一个新的 UDP 连接,但是需要同时监听和读写两个连接的数据,说实话这更适合用 epoll 来写(不过不跨平台)。

我这个实现把offerNew改了新增一个方法只获得conn,后面的loopMessage等到实际切换的时候再调用,确实存在这个重新连接,不过好在切换的连接都是可以直接发包的,切换的时候并没有卡顿,体验一晚上感觉很不错,有空学习一下你的代码😁

sqkam commented 7 months ago

这个实现在与 ClashMetaForAndroid 一起使用时会遇到 hop 后无法继续使用的 bug,原因未知。并且还发现 ticker 在未到期时多次触发的 bug,也不知道原因。

我这个用ClashMetaForAndroid很爽哦,学校公共网20mbps,配置文件写最高15mbps流畅看1080,最开始一个版本用ticker改client,手机会出现触摸失灵,后面改了,不用这个ticker就好了

sqkam commented 7 months ago

写了个UDP服务来测试,没遇到问题。但是我并没有用到 ctx、opts 这 2 个参数,不清楚是否对于控制超时、连接有什么特殊用处。

ctx控制gorountine退出的opots应该提供拓展空间吧

MMMMMoris commented 6 months ago

这个实现在与 ClashMetaForAndroid 一起使用时会遇到 hop 后无法继续使用的 bug,原因未知。并且还发现 ticker 在未到期时多次触发的 bug,也不知道原因。

我这个用ClashMetaForAndroid很爽哦,学校公共网20mbps,配置文件写最高15mbps流畅看1080,最开始一个版本用ticker改client,手机会出现触摸失灵,后面改了,不用这个ticker就好了

你好,请问在 ClashMetaForAndroid 怎么实现端口跳跃?需要用最新版代码手动编译吗?配置文件的 hysteria 端口跳跃部分该怎么写?看如今这个功能应该是开发完成了,但我在 PC 也不知道该如何配置端口跳跃

MMMMMoris commented 6 months ago

看了下代码做了些猜测,应该配置是这样的

ports: 2333-23333,
hop-interval: 45,

CMFA也是一样的配置,测试没有报错,但不知实际是否用上了端口跳跃

keakon commented 6 months ago

看了下代码做了些猜测,应该配置是这样的

ports: 2333-23333,
hop-interval: 45,

CMFA也是一样的配置,测试没有报错,但不知实际是否用上了端口跳跃

ports 启用时,忽略 port 的设置(但是 port 仍然必填)。 hop-interval 默认为 30 秒。 设置 log-level: debug 就可以看到端口跳跃的日志了。

MMMMMoris commented 6 months ago

看了下代码做了些猜测,应该配置是这样的

ports: 2333-23333,
hop-interval: 45,

CMFA也是一样的配置,测试没有报错,但不知实际是否用上了端口跳跃

ports 启用时,忽略 port 的设置(但是 port 仍然必填)。 hop-interval 默认为 30 秒。 设置 log-level: debug 就可以看到端口跳跃的日志了。

那请问 ports 里面的内容格式是怎么样的?我能这样写吗?ports: {2000,2333-23333}

wwqgtxx commented 6 months ago

看了下代码做了些猜测,应该配置是这样的

ports: 2333-23333,
hop-interval: 45,

CMFA也是一样的配置,测试没有报错,但不知实际是否用上了端口跳跃

ports 启用时,忽略 port 的设置(但是 port 仍然必填)。 hop-interval 默认为 30 秒。 设置 log-level: debug 就可以看到端口跳跃的日志了。

那请问 ports 里面的内容格式是怎么样的?我能这样写吗?ports: {2000,2333-23333}

https://github.com/MetaCubeX/mihomo/blob/81c832ef9ef97265da9e59298b3642554ca17fe7/common/utils/ranges.go#L17 https://github.com/MetaCubeX/mihomo/blob/81c832ef9ef97265da9e59298b3642554ca17fe7/common/utils/ranges.go#L23

wwqgtxx commented 6 months ago

@keakon 另外 https://github.com/MetaCubeX/mihomo/commit/012e4485621939af60bbd8708c4d15c8a04039e9 已经去掉了ports启用时,port必填的需求了

MMMMMoris commented 6 months ago

看了下代码做了些猜测,应该配置是这样的

ports: 2333-23333,
hop-interval: 45,

CMFA也是一样的配置,测试没有报错,但不知实际是否用上了端口跳跃

ports 启用时,忽略 port 的设置(但是 port 仍然必填)。 hop-interval 默认为 30 秒。 设置 log-level: debug 就可以看到端口跳跃的日志了。

那请问 ports 里面的内容格式是怎么样的?我能这样写吗?ports: {2000,2333-23333}

https://github.com/MetaCubeX/mihomo/blob/81c832ef9ef97265da9e59298b3642554ca17fe7/common/utils/ranges.go#L17

https://github.com/MetaCubeX/mihomo/blob/81c832ef9ef97265da9e59298b3642554ca17fe7/common/utils/ranges.go#L23

200,204,401-429,501-503 这样的写法感觉有些不符合 yaml 规范,不知后面是否有计划改进 我使用 yaml 格式化工具对包含 ports: 2000,2333-23333, 的配置文件进行格式化的时候,2333-23333, 会自动被分到下一行

wwqgtxx commented 6 months ago

@MMMMMoris 你需要加引号,又没让你直接写

bettswang commented 6 months ago

@keakon 另外 012e448 已经去掉了ports启用时,port必填的需求了

@wwqgtxx 您好。请问下,我是用CFA v2.10.1的最新版本,服务端配置hysteria2端口跳跃,拉取配置文件时,还是提示说缺少关键字段port? 320777630-bf69a275-7655-4b79-ac15-1ba3c2d9d9fb