138 lines
6.9 KiB
Java
138 lines
6.9 KiB
Java
/*
|
|
* Copyright 2012 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
package io.netty.example.socksproxy;
|
|
|
|
import io.netty.bootstrap.Bootstrap;
|
|
import io.netty.channel.Channel;
|
|
import io.netty.channel.ChannelFuture;
|
|
import io.netty.channel.ChannelFutureListener;
|
|
import io.netty.channel.ChannelHandler;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelOption;
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
|
import io.netty.handler.codec.socksx.SocksMessage;
|
|
import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandResponse;
|
|
import io.netty.handler.codec.socksx.v4.Socks4CommandRequest;
|
|
import io.netty.handler.codec.socksx.v4.Socks4CommandStatus;
|
|
import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse;
|
|
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
|
|
import io.netty.handler.codec.socksx.v5.Socks5CommandStatus;
|
|
import io.netty.util.concurrent.Future;
|
|
import io.netty.util.concurrent.FutureListener;
|
|
import io.netty.util.concurrent.Promise;
|
|
|
|
@ChannelHandler.Sharable
|
|
public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksMessage> {
|
|
|
|
private final Bootstrap b = new Bootstrap();
|
|
|
|
@Override
|
|
public void messageReceived(final ChannelHandlerContext ctx, final SocksMessage message) throws Exception {
|
|
if (message instanceof Socks4CommandRequest) {
|
|
final Socks4CommandRequest request = (Socks4CommandRequest) message;
|
|
Promise<Channel> promise = ctx.executor().newPromise();
|
|
promise.addListener(
|
|
(FutureListener<Channel>) future -> {
|
|
final Channel outboundChannel = future.getNow();
|
|
if (future.isSuccess()) {
|
|
ChannelFuture responseFuture = ctx.channel().writeAndFlush(
|
|
new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS));
|
|
|
|
responseFuture.addListener((ChannelFutureListener) 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));
|
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
|
}
|
|
});
|
|
|
|
final Channel inboundChannel = ctx.channel();
|
|
b.group(inboundChannel.eventLoop())
|
|
.channel(NioSocketChannel.class)
|
|
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
|
|
.option(ChannelOption.SO_KEEPALIVE, true)
|
|
.handler(new DirectClientHandler(promise));
|
|
|
|
b.connect(request.dstAddr(), request.dstPort()).addListener((ChannelFutureListener) future -> {
|
|
if (future.isSuccess()) {
|
|
// Connection established use handler provided results
|
|
} else {
|
|
// Close the connection if the connection attempt has failed.
|
|
ctx.channel().writeAndFlush(
|
|
new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED)
|
|
);
|
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
|
}
|
|
});
|
|
} else if (message instanceof Socks5CommandRequest) {
|
|
final Socks5CommandRequest request = (Socks5CommandRequest) message;
|
|
Promise<Channel> promise = ctx.executor().newPromise();
|
|
promise.addListener(
|
|
(FutureListener<Channel>) future -> {
|
|
final Channel outboundChannel = future.getNow();
|
|
if (future.isSuccess()) {
|
|
ChannelFuture responseFuture =
|
|
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
|
Socks5CommandStatus.SUCCESS,
|
|
request.dstAddrType(),
|
|
request.dstAddr(),
|
|
request.dstPort()));
|
|
|
|
responseFuture.addListener((ChannelFutureListener) channelFuture -> {
|
|
ctx.pipeline().remove(SocksServerConnectHandler.this);
|
|
outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
|
|
ctx.pipeline().addLast(new RelayHandler(outboundChannel));
|
|
});
|
|
} else {
|
|
ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse(
|
|
Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
|
}
|
|
});
|
|
|
|
final Channel inboundChannel = ctx.channel();
|
|
b.group(inboundChannel.eventLoop())
|
|
.channel(NioSocketChannel.class)
|
|
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
|
|
.option(ChannelOption.SO_KEEPALIVE, true)
|
|
.handler(new DirectClientHandler(promise));
|
|
|
|
b.connect(request.dstAddr(), request.dstPort()).addListener((ChannelFutureListener) future -> {
|
|
if (future.isSuccess()) {
|
|
// Connection established use handler provided results
|
|
} else {
|
|
// Close the connection if the connection attempt has failed.
|
|
ctx.channel().writeAndFlush(
|
|
new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType()));
|
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
|
}
|
|
});
|
|
} else {
|
|
ctx.close();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
SocksServerUtils.closeOnFlush(ctx.channel());
|
|
}
|
|
}
|