本文大部分内容由吴浩麟在segmentfault上的技术文章转载,地址为
https://segmentfault.com/a/1190000011862912#articleHeader2

认识 Shadowsocks

Shadowsocks 是一个能骗过防火墙的网络代理工具。它把要传输的原数据经过加密后再传输,网络中的防火墙由于得不出要传输的原内容是什么而只好放行,于是就完成了防火墙穿透,也即是所谓的“翻墙”。
在自由的网络环境下,在本机上访问服务时是直接和远程服务建立连接传输数据,流程如图:

without g_f_w
without g_f_w

但在受限的网络环境下会有防火墙,本机电脑和远程服务之间传输的数据都必须通过防火墙的检查,流程如图:

with g_f_W
with g_f_W

如果防火墙发现你在传输受限的内容,就把拦截本次传输,就会导致在本机无法访问远程服务。
而 Shadowsocks 所做的就是把传输的数据加密,防火墙得到的数据是加密后的数据,防火墙不知道传输的原内容是什么,于是防火墙就放行本次请求,于是在本机就访问到了远程服务,流程如图:

with ss
with ss

也就是说使用 Shadowsocks 的前提是:

  1. 一台在防火墙之外的服务器
  2. 在本机需要安装 Shadowsocks 本地端,用于加密传输数据
  3. 服务器需要安装 Shadowsocks 服务端,用于解密加密后的传输数据,解密出原数据后发送到目标服务器

Shadowsocks 原理

Shadowsocks 由两部分组成,运行在本地的 ss-local 和运行在防火墙之外服务器上的 ss-server,下面来分别详细介绍它们的职责

ss-local

ss-local 的职责是在本机启动和监听着一个服务,本地软件的网络请求都先发送到 ss-local,ss-local 收到来自本地软件的网络请求后,把要传输的原数据根据用户配置的加密方法和密码进行加密,再转发到墙外的服务器去。

ss-server

ss-server 的职责是在墙外服务器启动和监听一个服务,该服务监听来自本机的 ss-local 的请求。在收到来自 ss-local 转发过来的数据时,会先根据用户配置的加密方法和密码对数据进行对称解密,以获得加密后的数据的原内容。同时还会解 SOCKS5 协议,读出本次请求真正的目标服务地址(例如 Google 服务器地址),再把解密后得到的原数据转发到真正的目标服务。
当真正的目标服务返回了数据时,ss-server 端会把返回的数据加密后转发给对应的 ss-local 端,ss-local 端收到数据再解密后,转发给本机的软件。这是一个对称相反的过程。
由于 ss-local 和 ss-server 端都需要用对称加密算法对数据进行加密和解密,因此这两端的加密方法和密码必须配置为一样。Shadowsocks 提供了一系列标准可靠的对称算法可供用户选择,例如 rc4、aes、des、chacha20 等等。Shadowsocks 对数据加密后再传输的目的是为了混淆原数据,让途中的防火墙无法得出传输的原数据。但其实用这些安全性高计算量大的对称加密算法去实现混淆有点“杀鸡用牛刀”。

SOCKS5 协议介绍

Shadowsocks 的数据传输是建立在 SOCKS5 协议之上的,SOCKS5 是 TCP/IP 层面的网络代理协议。
ss-server 端解密出来的数据就是采用 SOCKS5 协议封装的,通过 SOCKS5 协议 ss-server 端能读出本机软件想访问的服务的真正地址以及要传输的原数据,下面来详细介绍 SOCKS5 协议的通信细节。

建立连接

客户端向服务端连接连接,客户端发送的数据包如下:

VER-NMETHODS
VER-NMETHODS

其中各个字段的含义如下:

  1. -VER:代表 SOCKS 的版本,SOCKS5 默认为0x05,其固定长度为1个字节;
  2. -NMETHODS:表示第三个字段METHODS的长度,它的长度也是1个字节;
  3. -METHODS:表示客户端支持的验证方式,可以有多种,他的长度是1-255个字节。
    目前支持的验证方式共有:
  4. 0x00:NO AUTHENTICATION REQUIRED(不需要验证)
  5. 0x01:GSSAPI
  6. 0x02:USERNAME/PASSWORD(用户名密码)
  7. 0x03: to X’7F’ IANA ASSIGNED
  8. 0x80: to X’FE’ RESERVED FOR PRIVATE METHODS
  9. 0xFF: NO ACCEPTABLE METHODS(都不支持,没法连接了)

响应连接

服务端收到客户端的验证信息之后,就要回应客户端,服务端需要客户端提供哪种验证方式的信息。服务端回应的包格式如下:

VER-METHOD
VER-METHOD

其中各个字段的含义如下:

  1. VER:代表 SOCKS 的版本,SOCKS5 默认为0x05,其固定长度为1个字节;
  2. METHOD:代表服务端需要客户端按此验证方式提供的验证信息,其值长度为1个字节,可为上面六种验证方式之一。

和目标服务建立连接

客户端发起的连接由服务端验证通过后,客户端下一步应该告诉真正目标服务的地址给服务器,服务器得到地址后再去请求真正的目标服务。也就是说客户端需要把 Google 服务的地址google.com:80告诉服务端,服务端再去请求google.com:80。
目标服务地址的格式为 (IP或域名)+端口,客户端需要发送的包格式如下:

VER-CMD-RSV
VER-CMD-RSV

各个字段的含义如下

  1. VER:代表 SOCKS 协议的版本,SOCKS 默认为0x05,其值长度为1个字节;
  2. CMD:代表客户端请求的类型,值长度也是1个字节,有三种类型;
    1. CONNECT: 0x01;
    2. BIND: 0x02;
    3. UDP: ASSOCIATE 0x03;
  3. RSV:保留字,值长度为1个字节;
  4. ATYP:代表请求的远程服务器地址类型,值长度1个字节,有三种类型;
    1. IPV4: address: 0x01;
    2. DOMAINNAME: 0x03;
    3. IPV6: address: 0x04;
  5. DST.ADDR:代表远程服务器的地址,根据 ATYP 进行解析,值长度不定;
  6. DST.PORT:代表远程服务器的端口,要访问哪个端口的意思,值长度2个字节。

服务端在得到来自客户端告诉的目标服务地址后,便和目标服务进行连接,不管连接成功与否,服务器都应该把连接的结果告诉客户端。在连接成功的情况下,服务端返回的包格式如下:

VER-REP-RSV
VER-REP-RSV

各个字段的含义如下:

  1. VER:代表 SOCKS 协议的版本,SOCKS 默认为0x05,其值长度为1个字节REP代表响应状态码,值长度也是1个字节,有以下几种类型
    1. 0x00 succeeded
    2. 0x01 general SOCKS server failure
    3. 0x02 connection not allowed by ruleset
    4. 0x03 Network unreachable
    5. 0x04 Host unreachable
    6. 0x05 Connection refused
    7. 0x06 TTL expired
    8. 0x07 Command not supported
    9. 0x08 Address type not supported
    10. 0x09 to 0xFF unassigned
  2. RSV:保留字,值长度为1个字节
  3. ATYP:代表请求的远程服务器地址类型,值长度1个字节,有三种类型
    1. IP V4 address: 0x01
    2. DOMAINNAME: 0x03
    3. IP V6 address: 0x04
  4. BND.ADDR:表示绑定地址,值长度不定。
  5. PORT: 表示绑定端口,值长度2个字节

数据转发

客户端在收到来自服务器成功的响应后,就会开始发送数据了,服务端在收到来自客户端的数据后,会转发到目标服务。

总结

SOCKS5 协议的目的其实就是为了把来自原本应该在本机直接请求目标服务的流程,放到了服务端去代理客户端访问。
其运行流程总结如下:

  1. 本机和代理服务端协商和建立连接;
  2. 本机告诉代理服务端目标服务的地址;
  3. 代理服务端去连接目标服务,成功后告诉本机;
  4. 本机开始发送原本应发送到目标服务的数据给代理服务端,由代理服务端完成数据转发。