Post

proxy

最近在开发代理相关的,从ss重温一下代理

ss是一个能骗过防火墙的网络代理工具。它把要传输的原数据经过加密后再传输给特定的relay节点,再由relay节点转发到真正的终端

firewall即不知道内容,又不知道目的地(不是封锁ip),只能放行

因此可以把ss分成两个部分

ss-local

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

ss-server

ss-server的职责是在墙外服务器启动和监听一个服务,该服务监听来自本机的ss-local的请求。在收到来自ss-local转发过来的数据时,会先根据用户配置的加密方法和密码对数据进行对称解密,以获得加密后的数据的原内容。同时还会解SOCKS5协议,读出本次请求真正的目标服务地址(例如 Google 服务器地址),再把解密后得到的原数据转发到真正的目标服务。

当真正的目标服务返回了数据时,ss-server端会把返回的数据加密后转发给对应的ss-local端,ss-local端收到数据再解密后,转发给本机的软件。这是一个对称相反的过程。

由于ss-localss-server端都需要用对称加密算法对数据进行加密和解密,因此这两端的加密方法和密码必须配置为一样。ss提供了一系列标准可靠的对称算法可供用户选择,例如rc4aesdeschacha20等等。

Shadowsocks 对数据加密后再传输的目的是为了混淆原数据,让途中的防火墙无法得出传输的原数据。但其实用这些安全性高计算量大的对称加密算法去实现混淆有点“杀鸡用牛刀”。

socks5

ss是建立在socks5协议之上的,SOCKS5TCP/IP层面的网络代理协议。

可以用tcp/udp实现

ss-server端解密出来的数据就是采用SOCKS5协议封装的,通过SOCKS5协议ss-server端能读出本机软件想访问的服务的真正地址以及要传输的原数据。

可以看下SOCKS5协议的通信细节

Tcp based client

1 建连

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

+----+----------+----------+
|VER | NMETHODS | METHODS  |
+----+----------+----------+
| 1  |    1     | 1 to 255 |
+----+----------+----------+

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

  • VER:代表 SOCKS 的版本,SOCKS5 默认为0x05,其固定长度为1个字节;

  • NMETHODS:表示第三个字段METHODS的长度,它的长度也是1个字节;

  • METHODS:表示客户端支持的验证方式,可以有多种,他的长度是1-255个字节。

目前支持的验证方式共有:

  • 0x00:NO AUTHENTICATION REQUIRED(不需要验证)
  • 0x01:GSSAPI
  • 0x02:USERNAME/PASSWORD(用户名密码)
  • 0x03: to X’7F’ IANA ASSIGNED
  • 0x80: to X’FE’ RESERVED FOR PRIVATE METHODS
  • 0xFF: NO ACCEPTABLE METHODS(都不支持,没法连接了)

2 响应连接

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

+----+--------+
|VER | METHOD |
+----+--------+
| 1  |   1    |
+----+--------+

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

  • VER:代表 SOCKS 的版本,SOCKS5 默认为0x05,其固定长度为1个字节;

  • METHOD:代表服务端需要客户端按此验证方式提供的验证信息,其值长度为1个字节,可为上面六种验证方式之一。

3 和目标服务建立连接

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

1
2
3
4
5
+----+-----+-------+------+----------+----------+
|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X'00' |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+

各个字段的含义如下:

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

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

+----+-----+-------+------+----------+----------+
|VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X'00' |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+

各个字段的含义如下:

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

4 数据转发

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

udp client procudure

数据包格式随便看看把,跟之前一样, 但是每个包都要操作下…

+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
+----+------+------+----------+----------+----------+
| 2  |  1   |  1   | Variable |    2     | Variable |
+----+------+------+----------+----------+----------+
  • RSV Reserved X’0000’
  • FRAG Current fragment number
  • ATYP address type of following addresses:
    • IP V4 address: X’01’
    • DOMAINNAME: X’03’
    • IP V6 address: X’04’
  • DST.ADDR desired destination address
  • DST.PORT desired destination port
  • DATA user data

数据混淆

ss实现的混淆用的对称加密,其实没啥必要。感觉用密钥字典类的加解密就可以了

两端有相同的密钥,密钥稍微有点要求:

  1. 由256个 byte 组成,也就是一个数组

  2. 这个数组必须由 0~255 这256个数字组成,一个都不能差(uint_8)

  3. 这个数组中第I个的值不能等于I;

按照字典加密就好。。。

这种混淆的好处是可以针对byte做加解密,不对大的数据块做加解密。效率虽然看起来低了点。

但是复杂度其实是N的,针对流媒体其实比较合适

骗过墙其实戳戳有余,还可以在握手的时候下发key,做随机数的salt,然后生成一个随机数组。

两边用一样的随机数生成,生成的随机数应该是一致的。。。

REF

  1. RFC-1928-socks5
This post is licensed under CC BY 4.0 by the author.

Trending Tags