---
title: net-socks
date: 2018-09-28
updated: 2019-02-22
---
# socks 协议
SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。SOCKS是"SOCKetS"的缩写

1. 当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。
2.  最新协议是版本5,与前一版本相比,增加支持UDP、验证,以及IPv6。
3.  根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。

一般来说要建立起一个代理的连接要经过三个阶段。

## 阶段一: 验证
客户端告诉代理服务器自已支持的验证方式,可以有多种验证方式。

	+---------+-----------------+------------------+
	|协议版本  | 支持的验证式数量   | 验证方式          |
	+---------+-----------------+------------------+
	|1个字节   | 1个字节          | 1种式占一个字节    |
	+---------+-----------------+------------------+
	|0x05     |0x02             |0x00,0x02         |
	+---------+-----------------+------------------+

上面例子,客户端告诉代理服务器自已支持两种验证方式。

	0x00 无验证需求
	0x01 通用安全服务应用程序接口(GSSAPI)
	0x02 用户名/密码(USERNAME/PASSWORD)
	0x03 至 X'7F' IANA 分配(IANA ASSIGNED)
	0x80 至 X'FE' 私人方法保留(RESERVED FOR PRIVATE METHODS)
	0xFF 无可接受方法(NO ACCEPTABLE METHODS)

代理服务器收到上面的报文,选择自已所能支持的验证方式,并返回相应的数所包

	+----------+------------------+
	|协议版本   |  支持的验证方式     |
	+----------+------------------+
	|1个字节    |  1个字节          |
	+----------+------------------+
	|0x05      |  0x00            |
	+----------+------------------+

上面例子,服务端告诉客户端不需要验证。

注:如果代服务器不支持客户凋端给出的所有验证方式则代理服务器返回的验证方式为0xFF,即表示无可验证的方式, 客户端收到该报文后必須要关闭当前连接。
除了无需验证,其它的验证方式还需要子协议来进行验证。这里我们用0x02来举例,即用户/密码这种验证方式,这个也是用得最多的。

当客户端收到代理服务器支持0x02验证方式(用户密码验证),客户端要发送用户密码给代理服务器

	+--------+-----------+-------------------------+---------+-------------------------------+
	|协议版本 | 用户名长度  |用户名                    | 密码长度 |  密码                          |
	+--------+-----------+-------------------------+---------+-------------------------------+
	|1个字节  | 1个字节    |用户名字节数据             | 1个字节  |  密码字节数据                   |
	+--------+-----------+-------------------------+---------+-------------------------------+
	|0x01    | 0x05      |0x70,0x61,0x6b,0x6f,0x72 | 0x06    |  0x31,0x32,0x33,0x34,0x35,0x36|
	+--------+-----------+-------------------------+---------+-------------------------------+

上面的例子用户名为pakor对应的字节长度为5,密码为12345对应的字节数为6。

注:这里的协议版本是子协议版本不是socket5的协议版本。 目前用户名/密码验证这个子议的版本是0x01
代理服务器接收到客户端的用户名和密码进行验证,然后返回验证状态。

	+--------+-------+
	|协议版本 |状态    |
	+--------+-------+
	|1个字节  |1个字节 |
	+--------+-------+
	|0x01    |0x00   |
	+--------+-------+

如果验证成功则返回装态0x00,否则返回任何非0x00的值。客户端收到未成功验的状态必须关闭当前连接。

> 更多关于用户名/密码子协议信息请参看rfc1929

## 阶段二:建立代理连接
完成了校验过程接下来就是让代理服务器建立代理连接。

客户端发送要建立的的代理连接的地址及端口,地址可以是域名、IPv4、IPv6。

	+----------+------------+---------+-----------+-----------------------+------------+
	|协议版本号  | 请求的类型  |保留字段   |  地址类型  |  地址数据              |  地址端口    |
	+----------+------------+---------+-----------+-----------------------+------------+
	|1个字节    | 1个字节     |1个字节   |  1个字节   |  变长                  |  2个字节    |
	+----------+------------+---------+-----------+-----------------------+------------+
	|0x05      | 0x01       |0x00     |  0x01     |  0x0a,0x00,0x01,0x0a  |  0x00,0x50 |
	+----------+------------+---------+-----------+-----------------------+------------+

上面例子,客户端告诉代理服务器建立一个连接到10.0.1.10的80端口。这里的保留字段是固定的0x00。
地址类型有下面几种:

	IPV4 : 0x01
	域名 : 0x03
	IPV6 : 0x04

请求类型有下面几种:

- CONNECT : 0x01, 建立代理连接
- BIND : 0x02,告诉代理服务器监听目标机器的连接,也就是让代理服务器创建socket监听来自目标机器的连接。FTP这类需要服务端主动联接客户端的的应用场景。
- 	1. 只有在完成了connnect操作之后才能进行bind操作
- 	2. bind操作之后,代理服务器会有两次响应, 第一次响应是在创建socket监听完成之后,第二次是在目标机器连接到代理服务器上之后。
- UDP ASSOCIATE : 0x03, udp 协议请求代理。

注:UDP ASSOCIATE 时这里请求的地址和端口是告诉代理服务器客户端要使用这个地址发送数据到代理服务器。 代理服务器可以用这个信息对访问进行一些限制。

代理服务器收到客户端的请求后,要创建到目标机器的连接或创建监听目标机器的socket,然后返回相应的信息

	+----------+--------+---------+-----------+---------------------+------------+
	|协议版本号  | 状态码  |保留字段  |  地址类型  |  绑定的地址           |顷绑定的端口  |   
	+----------+--------+---------+-----------+---------------------+------------+
	|1个字节    | 1个字节 |1个字节   |  1个字节   |  变长                |2个字节     |
	+----------+--------+---------+-----------+---------------------+------------+
	|0x05      | 0x00   |0x00     |  0x01     |  0x0a,0x00,0x01,0x0a|0x00,0x50   |
	+----------+--------+---------+-----------+---------------------+------------+

上面的例子,代理服务器连接目标服务器10.0.1.10端口80成功。

状态码有下面几种:

	X00 succeeded
	X01 general SOCKS server failure
	X02 connection not allowed by ruleset
	X03 Network unreachable
	X04 Host unreachable
	X05 Connection refused
	X06 TTL expired
	X07 Command not supported
	X08 Address type not supported
	X09 to X'FF' unassigned

绑定的地址和端口根据请求中的CMD的不同而不同

- CONNECT : 此时绑定的地址是指代理服务器连接到目标机器时的ip和端口
- BIND : 这里会有两次响应
	第一次响应中是指代理服务器创建监听socket时绑定的ip和端口
	第二次响应中是指代理服务器监听的scoket收来自目标机器连接时的ip和端口
- UDP ASSOCIATE : 此时绑定地址指明了客户发送UDP消息至服务器的端口和地址

## 阶段三:数据包转发
到了这个阶段基本就是数据转发了,tcp就直接转发,udp还须要做点工作。

客户端发送给代理服务器和代理服务器返回给客户端的数据都需要包装下

	+----+------+------+----------+----------+----------+
	|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
	+----+------+------+----------+----------+----------+
	| 2  |  1   |  1   | Variable |    2     | Variable |
	+----+------+------+----------+----------+----------+ 

	RSV Reserved 0x00,0x00
	FRAG Current fragment number
	ATYP address type of following addresses:
	IP V4 address: 0x01
	DOMAINNAME: 0x03
	IP V6 address: 0x04
	DST.ADDR desired destination address
	DST.PORT desired destination port
	DATA user data

> 详细文档参考rfc1928

## demo
1. https://github.com/facert/socket-example/blob/master/socks5_server.py
2. /py-demo/socket/socks5_server.py

client0: https://github.com/Anorov/PySocks
client1:

    curl -x socks5h://localhost:8888 'https://api.myip.com'

# ss protocol
https://loggerhead.me/posts/shadowsocks-yuan-ma-fen-xi-xie-yi-yu-jie-gou.html

# References
- [socks-note]
- [socks-wiki]
- 由浅入深写代理(3)-socks5: https://zhuanlan.zhihu.com/p/28645864

[socks-wiki]: https://zh.wikipedia.org/wiki/SOCKS
[socks-note]: http://www.mojidong.com/network/2015/03/07/socket5-1/
  1. 笔记