基于netty实现的socks5代理协议

socks5协议

简介

socks5协议是一个标准的代理协议,工作在网络的四层,理论上可以代理任意应用层协议。协议标准RFC1928,用户/密码鉴权标准RFC1929。协议的中文版本可以参考这里

现有开源实现

用的比较多的是linux下的ss5开源实现,ss5的安装和配置如下:

  1. ./configure —with-debug —with-epollio —with-gssapi
  2. make
  3. make install
  4. chomd +x /etc/init.d/ss5

修改/etc/init.d/ss5,增加端口,日志等配置:

  1. export SS5_SOCKS_PORT=1081
  2. export SS5_CONFIG_FILE=/tmp/ss5.conf
  3. export SS5_PASSWORD_FILE=/tmp/ss5.passwd
  4. export SS5_LOG_FILE=/tmp/ss5.log
  5. export SS5_PROFILE_PATH=/tmp

修改/etc/sysconfig/ss5,配置服务器启动用户:

SS5_OPTS=" -u root"

修改/etc/opt/ss5/ss5.conf,配置鉴权方式:

  1. permit0.0.0.0/00.0.0.0/0 – – – – –
  2. #不使用用户认证,
  3. auth 0.0.0.0/0 – –
  4. #使用用户名/密码认证,
  5. auth 0.0.0.0/0 – u

修改/etc/opt/ss5/ss5.passwd,配置用户密码:

user passord

启动ss5服务:

service ss5 start

为什么要自己实现

需要更精确的记录代理访问日志,需要自己特殊的鉴权方式。ss5是基于c开发的,本人c语言一般,改起来还是稍显费劲,而基于java开发的socks5协议并没有特别成熟的,最终决定自己用netty4.1开发。

netty基本概念

NIO和BIO

BIO阻塞IO,NIO非阻塞IO。NIO基于事件通知机制,可以达到在单线程情况下同时处理多个IO。传统的BIO对每个IO请求都需要分配单独线程处理,而线程的分配是需要占用内存,分配的代价很高。因此传统的BIO每秒并发最多到千级别(和服务器配置相关),而NIO能支持每秒万级的并发。

NIO并不是没有缺点,NIO的异步特性对编程要求较高,和BIO的传统开发相比较,要更难控制,开发和调试难度也较大。

netty简介

在java开发中,提到NIO必会想到netty,netty是一个NIO开发框架,能降低NIO应用开发难度,开发者不需要关注通讯连接,可以将主要的精力放在对收到的信息进行编码,转换为对象,进行业务逻辑处理。netty框架是一个典型的管道过滤器模式的应用。netty3.x和4.x版本有较大的改动,本文主要使用netty4.1。

EventLoopGroup和EventLoop

前面提到NIO能在单线程模型相支持多IO,实际上线程并不是一个,而是有限的几个,这里的EventLoopGroup就是线程池,EventLoop就是线程。EventLoopGroup的线程数量和CPU核心数量相关,也可以初始化的时候自定义。

NIO应用的服务器端通常不止一个线程池,一般会建立一个boss线程池,一个worker线程池。boss线程池用来接收客户端连接,接收的连接交给worker线程池监听,一个worker线程可以监听多个io。

  1. //boss线程池,这里定义线程数量为2
  2. EventLoopGroup boss = new NioEventLoopGroup(2);
  3. //worker线程池,线程数量结合cpu核心计算出来Runtime.getRuntime().availableProcessors() * 2
  4. EventLoopGroup worker = new NioEventLoopGroup();

ServerBootstrap和Bootstrap

Bootstrap是一个builder模式的类型构造器,ServerBootstrap用于服务器端的构造,Bootstrap用于客户端的构造。主要配置线程池、ChannelFactory、基本参数、ChannelHandler链表初始化等。ServerBootstrap调用bind监听端口,Bootstrap调用connect连接服务器端。

在netty中ChannelFuture会经常碰到,由于netty所有的操作都是异步的,bind,connet,close等都不会立即返回接口,都是通过监听方法回调。通过调用ChannelFuture.addListener()添加监听方法。ChannelFutureListener里有几个内置的监听方法CLOSE,CLOSE_ON_FAILURE,FIRE_EXCEPTION_ON_FAILURE。

Channel,ChannelPipeline,ChannelHandler,ChannelHandlerContext

一个Channel分配一个ChannelPipeline,每个ChannelPipeline里是多个ChannelHandler组成的链表,每个ChannelHandler会对应一个ChannelHandlerContext。看看下面的图就能明白: 输入图片说明

  • Channel负责底层通讯,Channel会绑定到一个worker线程中,Channel在收到Event后会传递给ChannelPipeline,Event会在ChannelHandler链表中流转。 输入图片说明伦理片 http://www.dotdy.com/  
  • ChannelPipeline负责消息的传递,里面是一个ChannelHandler的组成的双向链表,按消息类型分为上行消息ChannelHandler和下行消息ChannelHandler。因此就有了两个子接口,ChannelInboundHandler只关心上行消息,ChannelOutboundHandler只关心下行消息。如果上下行消息都需要关系可以继承ChannelDuplexHandler。ChannelHandler通过调用fireChannelRead()将上行消息传递给下一个Handler,通过调用write()形成下行消息,调用write()后,上行消息将不再传递给下一个Handler。
  • ChannelHandler是处理消息的单元,是netty开发的核心。开发人员大部分都是在写各种Handler。netty内置了很多协议的handler,但是比较遗憾文档不多。上面已经说过,ChannelHandler按消息类型分可以分为ChannelInboundHandler和ChannelOutboundHandler。按功能分为编解码Handler(编码Encoder/解码Decoder)和消息处理Handler。通常需要将上行消息需要进行解码转换为对象,再将对象传递给之后的业务处理Handler。业务处理完毕,将下行对象编码后转换为ByteBuf交给Channel传递给网络层。

ByteBuf

理解ByteBuf对消息的编解码有很多好处,ByteBuf是Channel进行网络通信的核心对象,ByteBuf就像一个缓冲池一样,ByteBuf逻辑上就是一个byte容器。ByteBuf里的数据被两个指针划分为三个部分,如下图所示: 输入图片说明

  • reader index前面的数据是已经读过的数据,这些数据可以扔掉
  • 从reader index开始,到writer index之前的数据是可读数据
  • 从writer index开始,为可写区域

使用netty实现socks5协议

netty中已经内置了socks5协议的编解码,我们只需要实现逻辑处理Handler就行,下面是几个主要的编解码codec:

  • Socks5InitialRequestDecoder负责服务器和客户端协商鉴权方式,返回服务器端支持的鉴权方式,会将消息解码为DefaultSocks5InitialRequest对象
  • Socks5PasswordAuthRequestDecoder是使用用户名和密码的鉴权方式的协议,返回鉴权是否通过,会将消息解码为DefaultSocks5PasswordAuthRequest对象
  • Socks5CommandRequestDecoder负责目标服务器的连接建立,返回建立是否成功,会将消息解码为DefaultSocks5CommandRequest对象
  • Socks5ServerEncoder负责所有下行对象的编码,转换为ByteBuf

编解码工作不需要我们自己开发,剩下的事情就比较简单,只需要处理各个层级的Socks5Request就行了。ChannelHandler链的结构如下:

  1. Socks5ServerEncoder
  2. Socks5InitialRequestDecoder
  3. Socks5InitialRequestHandler(实现自己的处理逻辑)
  4. Socks5PasswordAuthRequestDecoder
  5. Socks5PasswordAuthRequestHandler(实现自己的处理逻辑)
  6. Socks5CommandRequestDecoder
  7. Socks5CommandRequestHandler(实现自己的处理逻辑)

通过实现3个ChannelHandler,我们就能轻松搞定一个socks5协议。全部代码可以在github上查到,项目名为socks5-netty。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享