windows搭建代理服务器(netty系列之:从零到壹,搭建一个SOCKS代理服务器)

简介

在上一篇文章中,我们谈到了netty对SOCKS消息对象的封装,区分了SOCKS4和SOCKS5,并提供了各种连接和响应状态。

有了SOCKS消息的封装,我们还需要做什么来构建SOCKS服务器呢?

使用SSH搭建SOCKS服务器

事实上,最简单的方法是使用SSH工具来构建SOCKS代理服务器。

先看看SSH建立SOCKS服务的命令:

SSH-f-c-n-d bind address:port name @ server -f表示SSH作为后台守护进程执行。

-N表示不执行远程命令,仅用于端口转发。

搭建代理服务器

-D表示端口上的动态转发。该命令支持SOCKS4和SOCKS5。

-C表示在发送前压缩数据。

本地服务器的绑定地址。

Port表示本地服务器的指定监听端口。

Name表示ssh服务器登录名。

Server代表ssh服务器地址。

上述命令意味着端口绑定在本地建立,然后转发到远程代理服务器。

例如,我们可以在这台计算机上打开一个2000端口,并将其转发到远程168.121.100.23计算机:

ssh-f-n-d 0 . 0 . 0:2000 root @ 168.121.100.23 有了代理服务器就可以用了。首先介绍一下如何在curl命令中使用SOCKS代理。

我们想通过代理服务器访问www.flydean.com。我们做什么呢

curl-x SOCKS 5h://localhost:2000-v-k-x get http://www . fly dean . com:80 要检测SOCKS的连接,还可以使用netcat命令,如下所示:

NCAT-代理127 . 0 . 0 . 1:2000-代理型SOCKS 5 www.flydean.com 80-NV 用netty构建SOCKS服务器用netty构建SOCKS服务器的关键是用netty服务器作为中继。它需要建立两个连接,一个是下一步,我们将逐步探索如何在netty中建立SOCKS服务器。

搭建服务器的基本步骤和普通服务器基本相同。在消息读取和处理过程中要注意的是消息的编码、解码和转发。

encoder和decoder

对于一个协议,最终需要的是相应的编码器和解码器,用来在协议对象和ByteBuf之间进行转换。

netty提供的SOCKS转换器被称为SockportUnification服务器处理程序。先看它的定义:

公共类socks端口统一服务器处理程序扩展了ByteToMessageDecoder 它继承了ByteToMessageDecoder,这意味着ByteBuf和Socks对象之间的转换。

因此,我们只需要在ChannelInitializer中为socks消息添加Socksportunification server handler和用户定义的处理程序:

公共void init channel(socket channel ch)引发异常{ ch.pipeline()。add last( new logging handler(log level。DEBUG), new SocksPortUnificationServerHandler(), SocksServerHandler。实例); } 等等,不!细心的伙伴可能已经发现Socksportunification server handler只是一个解码器,我们还缺少一个编码器把socks对象转换成ByteBuf。这个编码器在哪里?

别急,让我们回到socksportunification server handler。在它的解码方法中,有这样一段代码:

case socks 4a: logKnownVersion(CTX,version); p.addAfter(ctx.name(),null,Socks4ServerEncoder。实例); p.addAfter(ctx.name(),null,new socks 4 server decoder()); break; case socks 5: logKnownVersion(CTX,version); p.addAfter(ctx.name(),null,socks 5 encoder); p.addAfter(ctx.name(),null,new socks 5 initial request decoder()); break; 原来在解码方法上,根据Socks版本的不同,在ctx上加入了相应的编码器和解码器,非常巧妙。

对应的编码器分别是Socks4ServerEncoder和Socks5ServerEncoder。

建立连接

对于Socks4,建立连接的请求类型只有一种,在netty中用Socks4CommandRequest表示。

因此,我们只需要判断channelRead0中请求的版本:

case socks 4a: socks 4 command request socks v4 cmd request =(socks 4 command request)socks request; if(socks v4 cmd request . type()= = socks 4 commandtype。CONNECT) { ctx.pipeline()。add last(new socksserverconnechandler()); ctx.pipeline()。去掉(这个); CTX . firechannelread(socks request); } else { CTX . close(); } 这里我们添加了一个定制的SocksServerConnectHandler来处理Socks连接。稍后将详细解释这个自定义处理程序。在这里我们都知道它可以用来建立联系。

对于Socks5来说,就比较复杂了,包括初始化请求、认证请求、连接建立三个部分,需要分别处理:

case socks 5: if(socks request instance of socks 5 initial request){ CTX . pipeline()。add first(new socks 5 commandrequestdecoder()); CTX . write(new defaultsocks 5 initial response(socks 5 auth method。NO _ AUTH)); } else if(socks request instance of socks 5 passwordauthrequest){ CTX . pipeline()。add first(new socks 5 commandrequestdecoder()); CTX . write(new defaultsocks 5 passwordauthresponse(socks 5 passwordauthstatus。成功)); } else if(socks request instance of socks 5 command request){ socks 5 command request socks 5 cmd request =(socks 5 command request)socks request; if(socks 5 cmd request . type()= = socks 5 commandtype。CONNECT) { ctx.pipeline()。add last(new socksserverconnechandler()); ctx.pipeline()。去掉(这个); CTX . firechannelread(socks request); } else { CTX . close(); } 注意,我们这里的身份验证请求只支持用户名和密码身份验证。

ConnectHandler

既然是代理服务器,就要建立两个连接,一个是客户端和代理服务器的连接,一个是代理服务器和目标服务器的连接。

对于netty,这两个连接可以通过两个自举来建立。

其中,从客户端到代理服务器的连接是在我们启动netty服务器时建立的,所以我们需要在ConnectHandler中建立一个从代理服务器到目标服务器的新连接:

私有最终Bootstrap b = new Bootstrap(); Channel inbound Channel = CTX . Channel(); b . group(inbound channel . event loop()) 。通道(NioSocketChannel.class) 。选项(ChannelOption。CONNECT_TIMEOUT_MILLIS,10000) 。选项(ChannelOption。SO_KEEPALIVE,true) 。handler(新ClientPromiseHandler(promise)); b . connect(request . dstaddr()、request.dstPort())。add listener(future-& gt;{ if(未来。is success()){ /成功建立连接 } else { /关闭连接 CTX。频道()。writeandflush( close nflush(CTX . channel()); } }); 新的Bootstrap需要从收到的Socks消息中提取目标服务器的地址和端口,然后建立连接。

然后判断新建立的连接的状态。如果成功,添加一个中继器将outboundChannel的消息转发给inboundChannel,同时将inboundChannel的消息转发给outboundChannel,达到服务器代理的目的。

最终通道outbound Channel = future . get now(); if(future . is success()){ channel future response future = CTX . channel()。write and flush( new defaultsocks 4 command response(socks 4 command status。成功)); //成功建立连接,删除SocksServerConnectHandler,添加relay handler response future . addlistener(channel future->;{ ctx.pipeline()。remove(socksserverconnechandler . this); outboundChannel.pipeline()。addLast(新的relay handler(CTX . channel())); ctx.pipeline()。add last(new relay handler(outbound channel)); }); } else { ctx.channel()。write and flush( new defaultsocks 4 command response(socks 4 command status。拒绝_或_失败)); closenflush(CTX . channel()); } 总结说白了就是代理服务器建立两个连接,把一个连接的消息转发给另一个。这个操作在netty中非常简单。

这篇文章的例子可以参考:learn-netty4。

本文已被http://www.flydean.com/37-netty-cust-socks-server/.收录

最通俗的解读,最深刻的干货,最简洁的教程,还有很多你不知道的小技巧等着你去发现!

欢迎关注我的微信官方账号:“程序那些事”,懂技术更懂你!

您可以还会对下面的文章感兴趣

使用微信扫描二维码后

点击右上角发送给好友