proxy
最近在开发代理相关的,从ss重温一下代理
ss是一个能骗过防火墙的网络代理工具。它把要传输的原数据经过加密后再传输给特定的relay节点,再由relay节点转发到真正的终端
firewall即不知道内容,又不知道目的地(不是封锁ip),只能放行
因此可以把ss分成两个部分
ss-local
ss-local
的职责是在本机启动和监听着一个服务,本地软件的网络请求都先发送到ss-local
,ss-local
收到来自本地软件的网络请求后,把要传输的原数据根据用户配置的加密方法和密码进行加密,再转发到ss-server
的服务器去。
ss-server
ss-server
的职责是在墙外服务器启动和监听一个服务,该服务监听来自本机的ss-local
的请求。在收到来自ss-local
转发过来的数据时,会先根据用户配置的加密方法和密码对数据进行对称解密,以获得加密后的数据的原内容。同时还会解SOCKS5
协议,读出本次请求真正的目标服务地址(例如 Google 服务器地址),再把解密后得到的原数据转发到真正的目标服务。
当真正的目标服务返回了数据时,ss-server
端会把返回的数据加密后转发给对应的ss-local
端,ss-local
端收到数据再解密后,转发给本机的软件。这是一个对称相反的过程。
由于ss-local
和ss-server
端都需要用对称加密算法对数据进行加密和解密,因此这两端的加密方法和密码必须配置为一样。ss提供了一系列标准可靠的对称算法可供用户选择,例如rc4
、aes
、des
、chacha20
等等。
Shadowsocks 对数据加密后再传输的目的是为了混淆原数据,让途中的防火墙无法得出传输的原数据。但其实用这些安全性高计算量大的对称加密算法去实现混淆有点“杀鸡用牛刀”。
socks5
ss是建立在socks5
协议之上的,SOCKS5
是TCP/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
:GSSAPI0x02
:USERNAME/PASSWORD(用户名密码)0x03
: to X’7F’ IANA ASSIGNED0x80
: to X’FE’ RESERVED FOR PRIVATE METHODS0xFF
: 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实现的混淆用的对称加密,其实没啥必要。感觉用密钥字典类的加解密就可以了
两端有相同的密钥,密钥稍微有点要求:
由256个 byte 组成,也就是一个数组
这个数组必须由 0~255 这256个数字组成,一个都不能差(uint_8)
这个数组中第I个的值不能等于I;
按照字典加密就好。。。
这种混淆的好处是可以针对byte做加解密,不对大的数据块做加解密。效率虽然看起来低了点。
但是复杂度其实是N的,针对流媒体其实比较合适
骗过墙其实戳戳有余,还可以在握手的时候下发key,做随机数的salt,然后生成一个随机数组。
两边用一样的随机数生成,生成的随机数应该是一致的。。。