shadowsocks-libev源码阅读总结

数据协议

socks5

握手阶段

1
2
3
4
5
6
7
8
//    client send
//
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
//

客户端发起握手请求,数据格式如上图所示,具体如下

  • VER:固定0x05,表示socks5协议
  • NMETHODS:表示支持认证的方法数量,也是后面的METHODS字段占用的字节数
  • METHODS:认证方法列表,一个协议占用一个字节,其中一些常见的协议如下
    • 0x00:不需要认证
    • 0x01:GSSAPI
    • 0x02:用户名、密码认证
    • 0x03~0x7F:保留
    • 0x80~0xFE:私有
    • 0xFF:无可接受的方法
1
2
3
4
5
6
7
8
// 
// server send
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
//

服务端响应内容如上图所示,具体如下

  • VER:固定0x01
  • METHOD:认证方法,在shadowsocks源码里,选择0x00-不需要认证

认证阶段

忽略

请求连接阶段

1
2
3
4
5
6
7
8
//
// client send
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | 1 | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//

客户端发起请求,数据格式如上图所示,具体如下

  • VER:固定0x05
  • CMD:命令码
    • 0x01:CONNECT请求
    • 0x02:BIND请求
    • 0x03:UDP转发
  • RSV:保留字段,默认0x00
  • ATYP:目的地址类型
    • 0x01:ipv4
    • 0x03:域名
    • 0x04:ipv6
  • DST.ADDR:目的地址
    • ipv4:4个字节
    • 域名:1字节的len+最大255字节的域名
    • ipv6:16个字节
  • DST.PORT:目的端口,固定2个字节
1
2
3
4
5
6
7
8
//
// server send
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | 1 | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//

服务端响应内容如上图所示,具体如下

  • VER:固定0x05
  • REP:响应码
    • 0x00:成功
  • RSV:保留字段,默认0x00
  • ATYP:目的地址类型
    • 0x01:ipv4
    • 0x03:域名
    • 0x04:ipv6
  • BND.ADDR:服务器地址,一般为127.0.0.1或0.0.0.0
  • BND.PORT:服务器端口

注意:BND.ADDR跟BND.PORT如果是用在tcp连接时可以忽略,没什么用。但在udp里,这是后续数据传输的实际地址

数据中继阶段

数据是原始的TCP流,不再有额外协议头

shadowsocks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
// client send (before encrypted)
// +------+----------+----------+----------+
// | ATYP | DST.ADDR | DST.PORT | DATA |
// +------+----------+----------+----------+
// | 1 | Variable | 2 | Variable |
// +------+----------+----------+----------+
//
// sever send (before encrypted)
// +------+----------+----------+----------+
// | ATYP | DST.ADDR | DST.PORT | DATA |
// +------+----------+----------+----------+
// | 1 | Variable | 2 | Variable |
// +------+----------+----------+----------+
//
// client or server (after encrypted)
// +-------+--------------+
// | IV | PAYLOAD |
// +-------+--------------+
// | Fixed | Variable |
// +-------+--------------+
//

shadowsocks会在数据流的前面加上3个字段,同socks5协议后面的几个字段,当然,这里指的是加密前的数据格式

shadowsocks

数据传输流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
// +--------+ +--------+ +--------+
// | client | | server | | remote |
// +--------+ +--------+ +--------+
// | | |
// receive from app | |
// | | |
// encrypt | |
// | | |
// + ---------request----------> | |
// | | |
// | decrypt |
// | | |
// | + ---------request----------> |
// | | |
// | | <---------response--------- +
// | | |
// | encrypt |
// | | |
// | <---------response--------- + |
// | | |
// decrypt | |
// | | |
// send to app | |
// | | |
// | | |
//

应用角色

ss-server

  1. ss-server是服务器端进程,负责接收ss-local、ss-tunnel、ss-redir的客户端请求
  2. 负责将数据解密,然后根据目标地址,创建远程连接并中转数据,如上图的数据传输流程中的server角色
  3. 支持tcp和udp协议,但插件仅支持tcp协议
  4. 支持配置ACL过滤(即根据ip/域名判断是否block,用于广告过滤等)

ss-local

  1. ss-local本身是一个socks5服务器进程,负责接收应用数据,转发给ss-server
  2. 支持配置多个server地址,用于负载均衡(随机选一个服务器地址发送数据)
  3. 支持tcp和udp协议,但插件仅支持tcp协议
  4. 支持配置ACL过滤(即根据ip/域名判断是否需要proxy或直连或block)

ss-tunnel

  1. ss-tunnel类似ss-local,不同的是,这是一个单目标代理,应用数据转发给ss-server时,remote的地址会被ss-tunnel替换为一个固定值,比如8.8.8.8:53,常用于dns查询
  2. 支持配置多个server地址,用于负载均衡(随机选一个服务器地址发送数据)
  3. 支持tcp和udp协议,但插件仅支持tcp协议
  4. 不支持ACL配置

ss-redir

  1. ss-redir类似ss-local,不需要手动配置应用代理,只需要跟nftables配合导流到ss-redir端口,实现透明代理
  2. 支持配置多个server地址,用于负载均衡(随机选一个服务器地址发送数据)
  3. 支持tcp和udp协议,但插件仅支持tcp协议
  4. 不支持ACL配置
  5. 可以根据qos建立tcp、udp服务器

注意事项

  1. client->server->remote第一次请求过程中都会强制禁用nagle算法,完成第一批数据交换后才会根据配置判断是否开启nagle算法
  2. plugin-插件的交互方式实现很粗糙,本质是建立一个进程通过tcp来交互数据
  3. udp数据传输不支持plugin-插件,可以被gfw检查出