how shadowsocks android works
7 min read

how shadowsocks android works

ShadowsocksNatService.scala

对于已经Rooted的机器,可以使用iptable来拦截数据

if(!config.isUdpDns) 启动本地dns服务器pdnsd, 监听udp8153。设置 上游服务器为114.114.114.114, 223.5.5.5,以及127.0.0.1:8163, 并只使用tcp查询。

拦截dns请求,转向本地8153。拦截tcp,转向本地8123。

iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:8153

iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination 127.0.0.1:8123

使用-m owner --uid-owner来把自身排除

通过anroid api 修改所有网络的dns为本地dns。

if(config.isUdpDns) ss-tunnel 转发udp8153 否则 tcp8163,到8.8.8.8:53, 直连远程的ss-server。ss-tunnel就是端口转发,udp包也可以!

redsockes拦截所有tcp请求转向本地 sock5代理服务器

ss-local 实现本地sock5服务器,跟远程的ss-server通讯

ShadowsocksVpnService.scala

对于没有Rooted的机器,而且支持VPNService API的机器,就可以借助tun2socks来拦截

if(!config.isUdpDns)本地dns服务器pdnsd, 监听udp8153。设置 上游服务器为114.114.114.114, 223.5.5.5,以及127.0.0.1:8163, 并只使用tcp查询。

if(!config.isUdpDns) lss-tunnel,转发本地8163转向远程的8.8.8.8:53

ss-local,实现本地sock5服务器,跟远程的ss-server通讯

创建VPN服务,dns设为8.8.8.8,会自动创建tun设备,本端ip为26.26.26.1/24。

tun2sockes,把tun设备转向sock5代理。如果
config.isUdpDns,则添加
--enable-udprelay支持udp转发。否则添加--dnsgw指定dns为26.26.26.1:8153端口

config.isUdpDns 用于控制是否通过udp查询上游服务器。如果是,则不再起本地pdnsd,而直接通过隧道转向8.8.8.8。否则,先把dns转向本地pdnsd,然后再由其通过tcp连接上游dns。

Socks服务器是可以转发UDP包的,因为跟http proxy不同,Socks协议开始时有一个创建socket和绑定端口的协商。另外,socks还允许在服务端进行域名解析(这点跟proxy类似)

https://en.wikipedia.org/wiki/SOCKS

shadowsocks就是实现了一个socks代理,另外ssh的-D参数,即Dynamic Forwarding也是Socks代理。

由于有交互,所以连接socks服务器不是透明的,需要应用程序主动支持! 于是就有了tsocks,可以拦截socket调用,代替应用程序跟socks服务器进行交互,这样就可以实现透明代理的。不过对dns的支持有点问题

tsocks will normally not be able to send DNS queries through a SOCKS server since SOCKS V4 works on TCP and DNS normally uses UDP. Version 1.5 and up do however provide a method to force DNS lookups to use TCP, which then makes them proxyable.

http://linux.die.net/man/8/tsocks

使用tsocks和ssh实现透明代理 Tunnel your apps via SSH with tsocks

http://alexborisov.org/tunnel-your-apps-via-ssh-with-tsocks/

redsocks

类似tsocks,还有一个redsocks,对dns的支持也有问题。 udp代理走的也是udp,所以并不能走ssh隧道。

This tool allows you to redirect any TCP connection to SOCKS or HTTPS proxy using your firewall, so redirection is system-wide.

Another related issue is DNS over TCP. Redsocks includes `dnstc' that is fake and really dumb DNS server that returns "truncated answer" to every query via UDP. RFC-compliant resolver should repeat same query via TCP in this case - so the request can be redirected using usual redsocks facilities.

Features
Redirect any TCP connection to SOCKS4, SOCKS5 or HTTPS (HTTP/CONNECT) proxy server.
Login/password authentication is supported for SOCKS5/HTTPS connections. SOCKS4 supports only username, password is ignored. for HTTPS, currently only Basic and Digest scheme is supported.
Redirect UDP packets via SOCKS5 proxy server. NB: UDP still goes via UDP, so you can't relay UDP via OpenSSH.
Sends "truncated reply" as an answer to UDP DNS queries.
Redirect any HTTP connection to proxy that does not support transparent proxying (e.g. old SQUID had broken `acl myport' for such connections).

tcp使用iptable的REDIRECT指令,做NAT处理
#Anything else should be redirected to port 12345
root#iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345

然后在代码里,直接操作IP-Filter来查询SIOCGNATL即NAT的映射信息!

对于udp,使用了TPROXY来实现类似NAT的功能。只有如何把udp包拦截过来,由外部程序来处理。

http://darkk.net.ru/redsocks/

https://github.com/darkk/redsocks

还有一个叫做tun2socks的,可以把tun设备的tcp流量都转向socks代理,这在某些情况下是比较有用的,比如android的vpn服务。
badvpn-tun2socks is a network utility used to "socksify" TCP connections
at the network layer. It implements a TUN device which accepts all
incoming TCP connections (regardless of destination IP), and forwards
them through a SOCKS server. This allows you to forward all connections
through SOCKS, without any need for application support. It can be used,
for example, to forward connections through a remote SSH server.

https://manned.org/badvpn-tun2socks/acb3cbf9

https://github.com/ambrop72/badvpn

tun2socks主要是基于lwip是来实现ip包的处理,包括解析和转换,然后通过socks服务器进行收发。 没错,udp数据包也是走udp包发送。

因为lwip做了简单的tcp/ip实现,所以除了解析和生成ip,tun2socks还利用了tun2socks的tcp模块,这样就可以不依赖系统的NAT实现,因为可以直接修改ip头啊。

下面是shadowsocks自己的实现代理和转发模块,部分功能跟前面的模块是重复的,估计是为了替代。

ss-redir

实现透明代理

iptables的redirect命令会把所有tcp拦截过来。

监听本地tcp端口,accept连接,通过SO_ORIGINAL_DST或者IP6T_SO_ORIGINAL_DST获取原始的目标地址,然后跟远程的ss-server连接,第一个参数即是原始的目标地址,然后就是简单的数据转发。 每一条tcp连接都对应一条隧道连接,没有multiplex。

如果允许udp relay,则启动udprelay模块

iptables设置说明
https://github.com/shadowsocks/shadowsocks-libev/tree/master#advanced-usage

ss-tunnel

本地端口转发,监听一个本地tcp端口,把所有请求都通过隧道转发到另一台指定的服务器上。

在这里,主要就是把对dns的请求都转到8.8.8.8服务器上。 每一条tcp连接都对应一条隧道连接,没有multiplex。

如果允许udp relay,则启动udprelay模块

udprelay

不管是ss-tunnel和ss-redir,都不支持udp转发,这个工作落到了udprelay模块身上。它有四种模式MODULE_REDIR,MODULE_TUNNEL,MODULE_LOCAL和MODULE,根据普通的编译选项,分别嵌入REDIR,TUNNEL,以及远程服务模块。

upd relay分为两部分,一个是本地负责接收客户端发过来的真实的udp请求,然后通过udp转发到远程代理模块,由代理模块向真实的目标地址发送。需要创建为每一个tuple创建socket,以用于接收真实服务器的回包,并转回给客户端。

udp replay是基于udp的,传输时使用了ss的加密方法。

如果再REDIR使用的,那么就使用IP_TRANSPARENT选项来实现透明代理。拦截请求时,我们要获得真实的目标地址,而且在回包时也要修改源地址为真实服务器地址。 前面的TCP模块,其实是通过NAT来实现的。对UDP的处理,要复杂一点(我觉得只是在应用层而言,因为nat其实就已经支持tcp和udp了)。

https://www.kernel.org/doc/Documentation/networking/tproxy.txt

//看一下回包的过程,

//先绑到真实服务器的地址
bind(src_fd, (struct sockaddr *)&dst_addr, remote_dst_addr_len)
//然后把回包发出去
sendto(src_fd, buf->array, buf->len, 0, (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len);

这样客户端就以为是真实服务器发回来的了

upd TProxy示例代码
http://sen-wiki.blogspot.jp/2011/05/tproxy-transparent-proxy-udp-program.html

原文时间:2016.4