使用netty实现socks5协议
一、socks5协议简介
SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。
SOCKS是”SOCKetS”的缩写[注 1]。 当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接,这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。
SOCKS 协议第 4 版本为基于 TCP 协议的 C/S 应用,包括 TELNET, FTP 和 使用广泛的信息发现协议如 HTTP 、 WAIS 提供了不保证安全性的防火墙穿透服务。
SOCKS 5 扩展了第 4 版本,加入了 UDP 协议支持,在框架上加入了强认证功能,并且地址信息也加入了域名和 IPV6 的支持。
SOCKS协议不提供加密。
socks5协议适用如下几种场景:
- 局域网内只有某台机器被授权访问网络,其它机器需要连接外部网络,但是未被授权,这时候可以在被授权机器上运行socks5协议的服务端,其它局域网内未被授权的机器上运行socks5客户端程序通过被授权机器上网。
- 网络管理。socks5代理服务器会代理所有流量,所以能获取所有客户端想要访问的目标地址和端口号,这时候代理服务器可以自主决定是否允许客户端访问目标服务器。
- 其它。懂的自然懂,但是由于流量特征明显而且未加密,所以一旦开始用,立马会被封掉服务器,不要玩火,这里仅作为技术研究使用。
二、socks5协议交互过程
socks5协议大体上会经过两个或者三个交互过程,这取决于是否有认证流程。以用户名密码认证方式为例,完整的流程如下图所示
1、版本和认证方式交互
第一步,客户端向代理服务器发送代理请求,其中包含了代理的版本和认证方式:
+----+----------+----------+
|VER | NMETHODS | METHODS |
+----+----------+----------+
| 1 | 1 | 1 to 255 |
+----+----------+----------+
如果是socks5代理,第一个字段VER
的值是0x05
,表明是socks代理的第5个版本。
第二个字段NMETHODS
表示支持的认证方式,第三个字段是一个数组,包含了支持的认证方式列表:
0x00
: 不需要认证0x01
: GSSAPI认证0x02
: 用户名和密码方式认证0x03
: IANA认证0x80-0xfe
: 保留的认证方式0xff
: 不支持任何认证方式
服务端收到客户端的代理请求后,选择双方都支持的认证方式回复给客户端:
+----+--------+
|VER | METHOD |
+----+--------+
| 1 | 1 |
+----+--------+
这个过程完成之后,服务端知道了客户端想要使用的socks版本号,告诉客户端是否使用认证;客户端则通过请求服务端,得知下一步是否需要认证。
2、认证交互
如果上一步版本和认证方式交互的结果,服务器不需要认证,则可以跳过该步骤,否则需要进行认证交互。
上一步协商好了使用的认证方式,这里以使用用户名和密码交互方式为例,接下来客户端需要发送用户名和密码给服务端让服务端进行认证,请求格式如下所示
+----+------+----------+------+----------+
|VER | ULEN | UNAME | PLEN | PASSWD |
+----+------+----------+------+----------+
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+----+------+----------+------+----------+
- VER:固定长度一个字节,固定值X'01'表示用户名密码认证
- ULEN:用户名长度,固定一个字节大小
- UNAME:用户名,不固定大小,但是其长度和ULEN一致
- PLEN:密码长度,固定一个字节大小
- PASSWD:密码,不固定大小,但是其长度和PLEN一致
服务端会进行用户名和密码的校验,然后做出如下响应
+----+--------+
|VER | STATUS |
+----+--------+
| 1 | 1 |
+----+--------+
如果服务器响应STATUS的值为X'00',表示认证成功;其它状态表示认证失败,这时候客户端需要关闭连接。
3、数据交互
如果上一步用户名密码认证成功,或者无用户名密码认证,则会进入数据交互阶段,这阶段会进行真正的数据传输。首先,客户端会发送一个请求告诉服务端本次请求的信息,格式如下所示
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
- VER 是SOCKS版本,0x05;
- CMD 是SOCK的命令码
- 0x01 表示CONNECT请求
- 0x02 表示BIND请求
- 0x03 表示UDP转发
- RSV 0x00,保留
- ATYP DST.ADDR类型
- 0x01 IPv4地址
- 0x03 域名类型
- 0x04 IPv6地址
- DST.ADDR 目标服务地址,如果是IPv4类型,则固定4个字节长度;如果是域名类型,第一个字节是域名长度,剩余的内容为域名内容;如果是IPv6类型,固定16个字节长度。
- DST.PORT 目标服务端口,固定两个字节长度
代理服务在接收到该连接报文后,会创建和目标服务器的连接,同时返回和目标服务建立连接的结果报文
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | 0x00 | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
- VER是SOCKS版本,0x05;
- REP应答字段,表示和目标服务建立连接的结果
- 0x00 表示成功
- 0x01 普通SOCKS服务器连接失败
- 0x02 现有规则不允许连接
- 0x03 网络不可达
- 0x04 主机不可达
- 0x05 连接被拒
- 0x06 TTL超时
- 0x07 不支持的命令
- 0x08 不支持的地址类型
- 0x09 - 0xFF未定义
- RSV 0x00,保留
- ATYP BND.ADDR类型
- 0x01 IPv4地址,DST.ADDR部分4字节长度
- 0x03 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有0结尾。
- 0x04 IPv6地址,16个字节长度。
- BND.ADDR 目标服务地址
- BND.PORT 目标服务端口
至此,Socks5协议的“握手”部分完成,可以开始发送数据。代理服务器只需要将收到的客户端数据“盲目”的转发到目标服务,同时将收到的目标服务数据转发给客户端,只是一个中继(Relay)的角色。
三、netty实现
1.基本实现
netty作为使用java实现的高级网络编程框架,实现socks5协议最终作为代理服务器程序再合适不过了。从上面的交互流程上来看,整个过程还是稍稍有些复杂的,netty框架的特色之一就是实现了各种协议的编解码器给开发人员使用,开箱即用,非常方便。
netty提供了三个解码器和一个编码器帮助开发人员实现socks5协议的服务端的绝大多数功能。
编码器 | 作用 |
---|---|
io.netty.handler.codec.socksx.v5.Socks5ServerEncoder | socks5协议交互过程中编码服务端给客户端的响应 |
解码器 | 作用 |
---|---|
io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder | 版本和认证方式交互阶段解码客户端请求 |
io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequestDecoder | 认证交互阶段解码客户端认证请求 |
io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder | 数据交互阶段解码客户端连接请求 |
这几个解码器解决了从抽象的协议请求到对象的转换;而编码器解决了对象到抽象的协议转换。所以这些编解码器只是解决了这些问题还是不够的,剩下的逻辑需要自己实现才行。所以对应着三个解码器,有三个后续的自定义的入栈处理器与其一一对应
处理器 | 作用 |
---|---|
Socks5InitialRequestInboundHandler | 响应版本和认证方式交互阶段客户端请求 |
Socks5PasswordAuthRequestInboundHandler | 响应认证交互阶段客户端认证请求 |
Socks5CommandRequestInboundHandler | 响应数据交互阶段客户端连接请求 |
在第三阶段,在收到客户端发起连接请求后,代理服务器连接目标服务器,这时候涉及到转发客户端的请求到目标服务器以及转发目标服务器的响应到客户端,所以这里设计了两个入栈处理器
处理器 | 作用 | 绑定的Channel |
---|---|---|
Client2DestInboundHandler | 转发客户端请求到目标服务器 | 客户端与代理服务器之间的Channel |
Dest2ClientInboundHandler | 转发目标服务器响应到客户端 | 代理服务器和目标服务器之间的Cahnnel |
2.黑名单处理
这里想要实现一个功能,就是在黑名单中的地址不允许连接,如果是http请求,则直接返回错误页面;https请求或者其它类型协议则直接断开连接。
这个功能在第三阶段连接阶段实现,因为只有这时候才知道客户端想要访问的网络地址。
//检查黑名单
if (inBlackList(msg.dstAddr())) {
log.info("{} 地址在黑名单中,拒绝连接", msg.dstAddr());
//假装连接成功
DefaultSocks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, socks5AddressType);
ctx.writeAndFlush(commandResponse);
ctx.pipeline().addLast("HttpServerCodec", new HttpServerCodec());
ctx.pipeline().addLast(new BlackListInboundHandler());
ctx.pipeline().remove(Socks5CommandRequestInboundHandler.class);
ctx.pipeline().remove(Socks5CommandRequestDecoder.class);
return;
}
这里自定义了BlackListInboundHandler
处理http请求类型并返回在黑名单中的友好页面提示。
四、项目地址和使用说明
项目地址:https://gitee.com/kdyzm/socks5-netty
使用方法:由于在windows环境下系统并非天然支持socks5协议,所以需要借助Proxifier工具使其支持socks5;另外,如果出现了连接速度缓慢,有些网页打不开的现象,是Proxifier没设置好,一定要注意使用代理的dns设置,菜单:Profile->Name Resolution 取消Detect DNS settings automatically
选项,勾选Resolve hostnames through proxy
,之后就好了。
五、参考文档
https://zh.wikipedia.org/wiki/SOCKS
https://cloud.tencent.com/developer/article/1781560
https://www.dyxmq.cn/network/socks5.html
https://www.quarkay.com/code/383/socks5-protocol-rfc-chinese-traslation
https://blog.csdn.net/qq_33215972/article/details/105657960
https://segmentfault.com/a/1190000038498680
https://tools.ietf.org/html/rfc1928
https://tools.ietf.org/html/rfc1929
https://tools.ietf.org/html/rfc1961
使用netty实现socks5协议的更多相关文章
- netty系列之:netty对SOCKS协议的支持
目录 简介 SocksMessage Socks4Message Socks5Message 总结 简介 SOCKS是一个优秀的网络协议,主要被用来做代理,它的两个主要版本是SOCKS4和SOCKS5 ...
- [转]netty对http协议解析原理
本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...
- dubbo源码分析4-基于netty的dubbo协议的server
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- netty 对 protobuf 协议的解码与包装探究(2)
netty 默认支持protobuf 的封装与解码,如果通信双方都使用netty则没有什么障碍,但如果客户端是其它语言(C#)则需要自己仿写与netty一致的方式(解码+封装),提前是必须很了解net ...
- netty对http协议解析原理解析
本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...
- HTTP协议和SOCKS5协议
HTTP协议和SOCKS5协议 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们平时上网的时候基本上是离不开浏览器的,尤其是搜索资料的时候,那么这个浏览器是如何工作的呢?用的又是 ...
- SOCKS5 协议解析
代理 根据 HTTP 1.1 的定义,proxy 是: An intermediary program which acts as both a server and a client for the ...
- netty支持的协议
流经网络的数据总是具有相同的类型:字节.这些字节是如何流动的主要取决于我们所说的 网络传输--一个帮助我们抽象底层数据传输机制的概念.用户并不关心这些细节:他们只想确保他们的字节被可靠地发送和接收. ...
- netty对http协议解析原理解析(转载)
本文主要介绍netty对http协议解析原理,着重讲解keep-alive,gzip,truncked等机制,详细描述了netty如何实现对http解析的高性能. 1 http协议 1.1 描述 标示 ...
随机推荐
- Python - Asyncio模块实现的生产消费者模型
[原创]转载请注明作者Johnthegreat和本文链接 在设计模式中,生产消费者模型占有非常重要的地位,这个模型在现实世界中也有很多有意思的对应场景,比如做包子的人和吃包子的人,当两者速度不匹配时, ...
- js导出execl 兼容ie Chrome Firefox各种主流浏览器(js export execl)
第一种导出table布局的表格 1 <html> 2 3 <head> 4 <meta charset="utf-8"> 5 <scrip ...
- 基于Hi3559AV100 RFCN实现细节解析-(3)系统输入VI分析(HiISP)二 :
下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,整个过程涉及到VI.VDEC.VPSS.VGS.VO.NNIE,其中涉及的内容,大家可以参考之前我写的博客: 基于Hi3559AV10 ...
- Java 多线程 02
多线程·线程间通信 和 GUI 单例设计模式 * A:单例设计模式 * 保证类在内存中只有一个对象 * B:如何保证 * a:控制类的创建,不让其他类来创建泵类的对象,私有化构造方法 * b:在本类中 ...
- 初窥MyBatis-普通的CRUD操作
编写接口 编写对应的Mapper.xml中的sql语句 测试(增删改需要提交事务) <mapper namespace="com.perwrj.dao.UserMapper" ...
- 如何快速的插入 100W数据到数据库,使用PreparedStatement 最快实现!
有时候,我们使用数据库的时候,如何快速的添加测试数据到数据库中,做测试呢,添加100W 数据,如果使用工具的话可能很慢,这里我推荐大家使用 PreparedStatement 预编译 去进行操作:单线 ...
- WPF 基础 - 绘画 2) Path
1. Path 霸中霸 既可以替代其他几种图形,也可以将直线.圆弧.贝尔赛曲线组合起来; 重要属性:Geometry Data: 其中 Geometry 为抽象类,不可实例化,可使用其子类: Line ...
- 使用 autofac 实现 asp .net core 的属性注入
使用 autofac 代替 asp .net core 默认的 IOC 容器,可实现属性注入. 之前的使用方式不受影响. 源码已开源: dotnet-campus/Autofac.Annotation ...
- P1090 合并果子(JAVA语言)
题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...
- P1047_校门外的树(JAVA语言)
题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米. 我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置: 数轴上的每个整数点,即0,1,2,-,L都种 ...