Introduction to the
In the previous article, we saw that Netty provides a SocksMessage object wrapper for SOCKS messages, differentiates SOCKS4 from SOCKS5, and provides various states of connection and response.
Now that we have the encapsulation of SOCKS messages, what else do we need to do to set up a SOCKS server?
Use SSH to set up the SOCKS server
The simplest way is to use SSH to set up SOCKS proxy server.
SSH/SOCKS/SOCKS/SOCKS/SOCKS
ssh -f -C -N -D bindaddress:port name@server
Copy the code
-f Indicates that the SSH is executed as a daemon process in the background.
-n indicates that no remote command is executed and only port forwarding is used.
-d indicates dynamic forwarding on a port. This command supports SOCKS4 and SOCKS5.
-c Compresses data before sending.
Bindaddress Binding address of the local server.
Port Specifies the listening port of the local server.
Name Indicates the login name of the SSH server.
Server Indicates the ADDRESS of the SSH server.
The above command means that port bindings are set up locally and then forwarded to a remote proxy server.
For example, we can open a port of 2000 on this machine and forward it to the remote 168.121.100.23 machine:
Ssh-f-n -d 0.0.0.0:2000 [email protected]Copy the code
Using the curl command, you can use the SOCKS proxy.
We want to access www.flydean.com through a proxy server. How do we do that?
curl -x socks5h://localhost:2000 -v -k -X GET http://www.flydean.com:80
Copy the code
To detect a connection for SOCKS, use the netcat command as follows:
Ncat -proxy 127.0.0.1:2000 -proxy-type socks5 www.flydean.com 80-nvCopy the code
Set up the SOCKS server using Netty
The key to using netty to set up a SOCKS server is to use the Netty server as the trunk. The netty server needs to establish two connections, one between the client and the proxy server, and the other between the proxy server and the target ADDRESS. Next, we explore step-by-step how to build a SOCKS server in NetTY.
The basic steps of setting up a server are basically the same as those of ordinary servers. What we should pay attention to is the encoding, decoding and forwarding of messages in the process of reading and processing.
Encoder and decoder
For a protocol, the final need is the corresponding encoder and decoder, used for protocol object and ByteBuf conversion.
Provide SOCKS converter called SocksPortUnificationServerHandler netty. Let’s take a look at its definition:
public class SocksPortUnificationServerHandler extends ByteToMessageDecoder
Copy the code
It inherits from ByteToMessageDecoder and represents a conversion between ByteBuf and Socks objects.
So we just need to add in the ChannelInitializer SocksPortUnificationServerHandler and custom Socks message handler can:
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new LoggingHandler(LogLevel.DEBUG),
new SocksPortUnificationServerHandler(),
SocksServerHandler.INSTANCE);
}
Copy the code
Wait, no! Have careful friend may find SocksPortUnificationServerHandler just a decoder, we still lack an encoder, is used to transform the Socks object cost ByteBuf, where the encoder?
Don’t worry, we return to SocksPortUnificationServerHandler, in its decode method, there is a piece of code:
case SOCKS4a:
logKnownVersion(ctx, version);
p.addAfter(ctx.name(), null, Socks4ServerEncoder.INSTANCE);
p.addAfter(ctx.name(), null, new Socks4ServerDecoder());
break;
case SOCKS5:
logKnownVersion(ctx, version);
p.addAfter(ctx.name(), null, socks5encoder);
p.addAfter(ctx.name(), null, new Socks5InitialRequestDecoder());
break;
Copy the code
The original is in the decode method, according to the different version of Socks, CTX added the corresponding encoder and decoder, very clever.
The corresponding encoder is Socks4ServerEncoder and Socks5ServerEncoder.
Establish a connection
For Socks4, there is only one request type to establish a connection, represented in Netty by Socks4CommandRequest.
So we just need to determine the version of the request in channelRead0:
case SOCKS4a:
Socks4CommandRequest socksV4CmdRequest = (Socks4CommandRequest) socksRequest;
if (socksV4CmdRequest.type() == Socks4CommandType.CONNECT) {
ctx.pipeline().addLast(new SocksServerConnectHandler());
ctx.pipeline().remove(this);
ctx.fireChannelRead(socksRequest);
} else {
ctx.close();
}
Copy the code
Here we added a custom SocksServerConnectHandler, used to handle the Socks connection, the custom handler will in detail below, here you know it can be used to establish a connection.
For Socks5, it is more complex, including initialization request, authentication request and connection establishment three parts, so it needs to be handled separately:
case SOCKS5:
if (socksRequest instanceof Socks5InitialRequest) {
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH));
} else if (socksRequest instanceof Socks5PasswordAuthRequest) {
ctx.pipeline().addFirst(new Socks5CommandRequestDecoder());
ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS));
} else if (socksRequest instanceof Socks5CommandRequest) {
Socks5CommandRequest socks5CmdRequest = (Socks5CommandRequest) socksRequest;
if (socks5CmdRequest.type() == Socks5CommandType.CONNECT) {
ctx.pipeline().addLast(new SocksServerConnectHandler());
ctx.pipeline().remove(this);
ctx.fireChannelRead(socksRequest);
} else {
ctx.close();
}
Copy the code
Note that our authentication request here only supports username and password authentication.
ConnectHandler
As a proxy server, you need to establish two connections, one from the client to the proxy server and one from the proxy server to the target server.
For Netty, these two connections can be established using two Bootstraps.
ConnectHandler: Netty proxy server: netty proxy server: netty proxy server: netty proxy server: netty proxy server: netty proxy server: netty proxy server: netty proxy server
private final Bootstrap b = new Bootstrap(); Channel inboundChannel = ctx.channel(); b.group(inboundChannel.eventLoop()) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ClientPromiseHandler(promise)); b.connect(request.dstAddr(), Request.dstport ()).addListener(Future -> {if (future.isSuccess())} else {// Close the connection ctx.channel().writeAndFlush( new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED) ); closeOnFlush(ctx.channel()); }});Copy the code
The new Bootstrap needs to extract the address and port of the target server from the received Socks message and then establish a connection.
Then the status of the newly established connection is determined. If the connection succeeds, a forwarder is added to forward the messages from outboundChannel to inboundChannel and the messages from inboundChannel to outboundChannel, thus achieving the purpose of the server proxy.
final Channel outboundChannel = future.getNow(); if (future.isSuccess()) { ChannelFuture responseFuture = ctx.channel().writeAndFlush( new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS)); / / connection is established successfully, delete SocksServerConnectHandler, Add RelayHandler responseFuture. AddListener (channelFuture - > {CTX. Pipeline (). Remove (SocksServerConnectHandler. This); outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel())); ctx.pipeline().addLast(new RelayHandler(outboundChannel)); }); } else { ctx.channel().writeAndFlush( new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED)); closeOnFlush(ctx.channel()); }Copy the code
conclusion
In plain English, a proxy server establishes two connections and forwards messages from one connection to the other. This operation is very simple in Netty.
Learn -netty4 for an example of this article
This article is available at www.flydean.com/37-netty-cu…
The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!
Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!