认证
首先客户端向服务端发送认证信息,结构如下
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1 to 255 |
+----+----------+----------+
1就是指长度是一个byte,因此 1 to 255 也就是 1~255个byte。
- VER 是指协议版本,因为是 socks5,所以值是 0x05
- NMETHODS 是指有多少个可以使用的方法,也就是客户端支持的多少种认证方法
- METHODS 就是方法值,有多少个方法就有多少个byte,可以是如下值:
- 0x00 NO AUTHENTICATION REQUIRED 不需要认证
- 0x01 GSSAPI 参考维基百科
- 0x02 USERNAME/PASSWORD 用户名密码认证
- 0x03 to 0x7f IANA ASSIGNED 一般不用。INNA保留。
- 0x80 to 0xfe RESERVED FOR PRIVATE METHODS 保留作私有用处。
- 0xFF NO ACCEPTABLE METHODS 不接受任何方法/没有合适的方法
举例
这个例子表明支持两种认证协议,一种是不需要认证,一种是用户口令认证
+-------+----------+------------+
| VER | NMETHODS | METHODS |
+-------+----------+------------+
| 0x05 | 0x02 | 0x00, 0x02 |
+-------+----------+------------+
收到Client的请求之后,Server选择一个自己也支持的认证方案,然后返回:
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
如果服务端认证失败或不支持客户端提供的方法,响应的中的METHOD字段的值为0xFF,服务端关闭连接。
如果选择用户和口令的认证方式,客户端向服务端发送如下认证信息
+----+------+----------+------+----------+
|VER | ULEN | UNAME | PLEN | PASSWD |
+----+------+----------+------+----------+
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+----+------+----------+------+----------+
这里的VER为子协议(用户口令认证协议)的版本。
- VER的值为0x01
- ULEN表示用户名UNAME的长度,大小为1个字节
- UNAME为用户名,长度范围为1-255个字节,长度由ULEN指定
- PLEN表示口令的长度,大小为一个字节
- PASSWD为口令,长度范围为1-255,长度由PLEN指定
服务端接收客户端发过来的认证请求,并响应如下信息
+----+--------+
|VER | STATUS |
+----+--------+
| 1 | 1 |
+----+--------+
- VER的值为0x01
- STATUS表示认证的状态,如果服务端通过认证,STATUS为0;如果认证失败,STATUS为非0,且服务端应当关闭连接。
信息告知
客户端通过认证之后,将需要代理的信息发送给服务端
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
- VER 还是版本,取值是 0x05
- CMD 是指要做啥,取值如下:
- CONNECT 0x01 连接
- BIND 0x02 端口监听(也就是在Server上监听一个端口)
- UDP ASSOCIATE 0x03 使用UDP
- RSV 是保留位,值是 0x00
- ATYP 是目标地址类型,有如下取值:
- 0x01 IPv4
- 0x03 域名
- 0x04 IPv6
- DST.ADDR 就是目标地址的值了,如果是IPv4,那么就是4 bytes,如果是IPv6那么就是16 bytes,如果是域名,那么第一个字节代表 接下来有多少个字节是表示目标地址
- DST.PORT 两个字节代表端口号
CONNECT 0x01
In the reply to a CONNECT, BND.PORT contains the port number that the
server assigned to connect to the target host, while BND.ADDR
contains the associated IP address. The supplied BND.ADDR is often
different from the IP address that the client uses to reach the SOCKS
server, since such servers are often multi-homed. It is expected
that the SOCKS server will use DST.ADDR and DST.PORT, and the
client-side source address and port in evaluating the CONNECT
request.
CMD 为 CONNECT 的响应中,BND.PORT 为(socket5)服务器分配给连接到目标主机的端口号,
而 BND.ADDR 为(服务器)关联的(目标主机) IP 地址。提供的BND.ADDR通常与客户端连接的
服务器地址(目标主机地址)不同,因为这类服务器(目标主机)通常存在多个地址。socket5服务
器将会使用 DST.ADDR 和 DST.PORT和客户端源地址和端口以评估CONNECT请求
BIND 0x02
The BIND request is used in protocols which require the client to
accept connections from the server. FTP is a well-known example,
which uses the primary client-to-server connection for commands and
status reports, but may use a server-to-client connection for
transferring data on demand (e.g. LS, GET, PUT).
It is expected that the client side of an application protocol will
use the BIND request only to establish secondary connections after a
primary connection is established using CONNECT. In is expected that
a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND
request.
Two replies are sent from the SOCKS server to the client during a
BIND operation. The first is sent after the server creates and binds
a new socket. The BND.PORT field contains the port number that the
SOCKS server assigned to listen for an incoming connection. The
BND.ADDR field contains the associated IP address. The client will
typically use these pieces of information to notify (via the primary
or control connection) the application server of the rendezvous
address. The second reply occurs only after the anticipated incoming
connection succeeds or fails.
In the second reply, the BND.PORT and BND.ADDR fields contain the
address and port number of the connecting host.
UDP ASSOCIATE 0x03
The UDP ASSOCIATE request is used to establish an association within
the UDP relay process to handle UDP datagrams. The DST.ADDR and
DST.PORT fields contain the address and port that the client expects
to use to send UDP datagrams on for the association. The server MAY
use this information to limit access to the association. If the
client is not in possesion of the information at the time of the UDP
ASSOCIATE, the client MUST use a port number and address of all
zeros.
服务端处理客户端发过来的信息,并返回
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
- VER 还是版本,值是 0x05
- REP 是状态码,取值如下:
- 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 保留位,取值为 0x00
- ATYP 是目标地址类型,有如下取值:
- 0x01 IPv4
- 0x03 域名
- 0x04 IPv6
- BND.ADDR 就是目标地址的值了,如果是IPv4,那么就是4 bytes,如果是IPv6那么就是16 bytes,如果是域名,那么第一个字节代表 接下来有多少个字节是表示目标地址
- BND.PORT 两个字节代表端口号
流量转发
设有如下三种角色:
- client:客户端
- proxy:socks5代理服务器
- server:服务器
正常情况下是client和server直接通信,但是这里经过socks5代理转发:
client连接到socks5之后得到client的socket,这里写作client_socket
client将要连接的server信息告诉proxy,proxy开启一个socket和server通信,这里写做server_socket
client <–> proxy <–> server
客户端转发到服务端
recv(clientfd, buf, size)
send(serverfd, buf, size)
服务端转发到客户端
recv(serverfd, buf, size)
send(clientfd, buf, size)
参考
socks5 协议详解
RFC 1928
RFC 1929