ss-android上实现fakedns
ss-android算是android上比较稳定的产品了,但对dns的处理上依然不够理想,经常因为dns解析阻塞导致请求缓慢甚至失败。
ss-android对dns解析,有两种方案:
- udp方案,直接走ss的udp转发,需要服务端也支持udp转发,而且从我个人使用来说,并不十分稳定。
- tcp方案,用overture将请求转成tcp,再从tcp连接出去,缺点非常明显,原本udp的一来一回,到这里需要三步握手四步挥手。
iOS上surge的fakedns的方案非常好,毕竟dns污染的域名是少数的,大部分人在手机上的操作也不是那么复杂,完全可以通过定制规则的方式来解决这个问题。
接下来就是怎么实现了,修改ss-libev的方案我觉得略微复杂了一点,因此决定修改go-ss2,直接添加相关代码。
通读了一下ss-android和ss-libev的代码,大概注意的点如下:
- ss-libev通过android.c文件提供android定制支持
- 通过
__ANDROID__
宏、-V参数来控制部分功能,主要是禁用socks5 domain支持、回调java的protect和stat - android的VPNService需要用protect方法对vpn app自己的连接进行保护,防止陷入死循环,相关原理见这个SO答案。ss-libev是通过一个unix socket,将fd用libancillary传给java端,java端监听请求并将fd提取出来,完成protect后再返回结果。
- android的速度和流量信息,也是通过另一个unix socket文件,从libev回调回来的。
于是就照着在go-ss2上实现类似的东西就可以了,修改后的代码在这里,过程中几点要注意的:
- 需要安装android-ndk来编译android版本
- 生成各个平台的toolchain(以下均以arm64说明):
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm64 --api 21 --install-dir $ANDROID_ARM64_TOOLCHAIN
- 导出环境变量
CGO_ENABLED=1 CC=$ANDROID_ARM64_TOOLCHAIN/bin/aarch64-linux-android-clang GOOS=android GOARCH=arm64
- 最好使用golang 1.9进行编译,本文写于该版本
由于golang的限制,按这里的方法新建文件,然后重新编译标准库
go install -v net
go install -v github.com/ayanamist/go-shadowsocks2
,将文件改名为libss-local.so,替换到apk包的lib/arm64-v8a目录下- 编写一个空壳文件liboverture.so,也替换到apk中,屏蔽掉原本的overture,dns功能由新的go-ss2程序提供
#!/system/bin/sh exec sleep 10000000
- 用dex2jar的d2j-apk-sign签名后安装
- 调试的时候一定要关掉selinux,否则各种奇奇怪怪的问题:
setenforce permissive
;为了不修改java代码,因此fakedns使用了11.0.0.0/8的地址段。
使用的注意事项:
- 需要fakedns的,用ss-android的自定义规则,将域名添加进去,符合规则的都会走fakedns,无法关闭
- 需要禁用DNS转发,go-ss2不支持udp-relay,所以开启DNS转发的话会无法解析
- 建议将路由设置为“绕过局域网及中国”获得更好体验
- 不支持IPv6
- go-ss2内置成114DNS,android上设置的远端DNS无效。事实上原始版本,如果不开DNS转发,也是走114和dnspod 可以通过访问welcome.114dns.com来检查是否生效 可以访问www.taobao.com看是否会被重定向到国际版,原来的DNS转发由于服务器一般在国外,会解析出国外的IP导致访问国际版页面。
- 由于本人使用MIUI,因此不需要流量速度显示,因此没有实现这部分。
- 目前发现,如果在开启ss-android的情况下,在另一个vpn app中抢夺vpn,那后续ss-android会出现开启后无法访问网络的问题,请执行一次“强制停止”后重新开启。
最后附一个ss-android_v4.1.8的fakedns版,注意只替换了arm64:apk下载