概述

项目中遇到了socks5代理环境下UDP不通的问题,在解决问题的过程中,也学习了socks5到底是如何通信的,下面就原理、客户端、服务器三个方面来详细说一下。

原理

使用场景

有一部分企业,出于安全考虑或者某些原因,员工的上网环境必须走代理,通常服务器会被配置成一个HTTP代理,但是HTTP是基于TCP的,走UDP的数据则不行,所以,像直播或者视频都不行,为了解决这些问题,我们可以将服务器配置成socks5代理,这样就能满足我们的需求。

首先,我们要知道什么是代理,其实代理就是帮你干活的。例如跑腿,你想到楼下的便利店买包烟,但是你的腿被别人打断了,不能下楼,这时,你叫了一个跑腿的,告诉他到楼下的便利店买包烟,他买完以后,又给你送到楼上,你就开始愉快的抽烟。这就是一个完整的代理过程。

认证

通常,socks5代理服务器都会配置在1080端口,他是基于TCP的,客户端要连接到代理服务器,首先要经过三次握手,之后需要和服务器进行认证,格式如下:

+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1 to 255 |
+----+----------+----------+
  • VER 字段是当前协议的版本号,也就是5
  • NMETHODS 字段是代表客户端支持的认证方式的个数
  • METHODS 字段代表客户端支持的认证方式,每一位字节表示一种认证方式。

服务器在客户端发送的认证方式中选择一种进行匹配,返回数据如下:

+----+----------+
|VER | METHODS |
+----+----------+
| 1 | 1 |
+----+----------+
  • METHOD 即为服务器端匹配的结果,如果服务器返回的是OxFF,则表明客户端所支持的认证方式,服务器端都不支持。那么认证失败。

认证方式有如下几种:

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

下面通过WireShark分析socks5认证过程:

首先,客户端和服务器的三次握手建立连接,之后,客户端发送认证请求,我们可以看到,客户端发送的数据为:

  • VER:05 即版本为5
  • NMETHODS:02 即支持两种认证方式
  • METHODS: 00 02 两种认证方式分别为 无须认证和用户名密码认证

服务器回的数据为:

  • VER:05 即版本为5
  • METHODS:00 即和客户端商议使用无须认证方式

当然,这里我配置的代理服务器没有使用用户名密码认证方式,假如需要此种方式的认证,客户端还需要发送用户名密码进行认证,有兴趣的同学可以试试。

连接

认证通过以后,客户端就需要告诉代理,需要它做什么,即需要给代理发命令,具体的格式如下:

+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
  • VER: 协议版本
  • CMD: 命令,有三种命令:
0x01:CONNECT 建立 TCP 连接
0x02: BIND 上报反向连接地址
0x03:关联 UDP 请求
  • RSV: 保留字段,值为 0x00
  • ATYP: 地址类型,取值为:
0x01:IPv4
0x03: 域名
0x04:IPv6
  • DST.ADDR 目的地,取值随ATYP的类型不同而不同,如下:
ATYP == 0x01:4 个字节的 IPv4 地址
ATYP == 0x03:1 个字节表示域名长度,紧随其后的是对应的域名
ATYP == 0x04:16 个字节的 IPv6 地址
  • DST.PORT 目的地端口

服务器返回的数据如下:

+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 1 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
  • VER: 版本号
  • REP: 服务器返回的结果
* X'00' succeeded
* X'01' general SOCKS server failure
* X'02' connection not allowed by ruleset
* X'03' Network unreachable
* X'04' Host unreachable
* X'05' Connection refused
* X'06' TTL expired
* X'07' Command not supported
* X'08' Address type not supported
* X'09' to X'FF' unassigned
  • RSV 保留字段
  • ATYP: 地址类型
  • BND.ADDR: 服务器地址
  • BND.PORT: 服务器端口

CMD

CONNECT

CONNECT表示要和目的主机建立TCP连接,在服务器回应中,BND.ADDR包含了关联的IP地址。此处所提供的BND.ADDR通常情况下不同于客户端连接到socks5代理服务器的IP地址,因为有可能代理服务器是一个集群,当然我这里只是一个服务器,所以返回的和代理的IP一样,BND.PORT表示服务器分配的连接到目标主机的端口号,即代理服务器接下来会使用BND.PORT这个端口与目标主机进行TCP通信。

BIND
BIND请求被用在那些需要客户机接收到服务器连接的协议中。FTP就是一个众所周知的例子。在实际应用场景中,一般用不到,这里不再细说。

UDP ASSOCIATE

此命令表示需要进行UDP转发,BND.ADDR和CONNECT中的含义一样,而BND.PORT表示服务器提供给客户端的UDP转发端口,接下来客户端的所有UDP都需要往代理的此端口发送。

因为我这次遇到的主要是UDP转发的问题,那么接下就着重分析一下UDP ASSOCIATE。

UDP ASSOCIATE

当客户端发送UDP ASSOCIATE命名时,代理服务器会返回相应的断端口,如下:

服务器响应如下:

那么,客户端知道服务器地址以后,是如何发送到代理服务器呢?

一个UDP数据报如下:

+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT |  DATA |
+----+------+------+----------+----------+----------+
| 2 |  1 |  1  | Variable |  2    | Variable |
+----+------+------+----------+----------+----------+
  • RSV 占两个字节 即 0x0000
  • FRAG Current fragment number
  • ATYP 目的地址类型
  • DST.ADDR 目的地址
  • DST.PORT 目的端口
  • DATA 真正的数据

如此,客户端开始不断的发送UDP数据到代理服务器,代理服务器接收到数据后,又会如何呢?

我们可以看到,客户端与代理之间的数据要比代理与目的主机之间的数据大10个字节,而这10个字节正是socks5协议头。

完整的UDP转发流程为:

现在,大家是不是对socks5有了一个全面的认识呢!

配置

客户端

一般情况下,类似于QQ支持socks5代理配置,但是大部分应用并不支持socks5代理,这时候我们可以使用一些代理工具,这里使用SocksCap64这个工具,他会使你的应用支持socks5协议。但是它只支持Windows,在Mac下你可以在网络上搜索其他的全局代理软件。

服务器

服务器可以使用dante软件。

  • 安装Dante

从源码安装

首先要下载源码->

tar -xzf dante-1.4.2.tar.gz
cd dante-1.4.2
./configure
make
make install
  • 配置

dante的默认配置文件时/etc/sockd.conf。具体的配置为:

logoutput: /var/log/sockd.log # 配置log文件
internal: eth0 port = 1080 # 配置内网 这里直接写eth0 端口可以配置成1080
external: 45.77.242.213 #外网ip
socksmethod: none
clientmethod: none
#method: username
#user.privileged: root
user.notprivileged: root
#user.libwrap: nobody
client pass {
from: 0.0.0.0/0 to: 0.0.0.0/0 #客户端过滤规则
log:error
}
socks pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
command: connect udpassociate # 支持的CMD
udp.portrange: 6000-6400 # 指定UDP转发接口范围
log: connect error
}
socks pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
command: bindreply udpreply # UDP回转
log: error
}
  • 启动
sockd

注意:当Socks5协议不使用1080端口时,WireShark是识别不出Socks5代理的,WireShark会将Socks5协议直接识别成TCP或者UDP。这点要注意。

关于更详细的配置,请参考dante官网dante