Java tutorial
/* * 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.aos.netty5.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.SocksRequest; import io.netty.handler.codec.socksx.v4.Socks4CmdRequest; import io.netty.handler.codec.socksx.v4.Socks4CmdResponse; import io.netty.handler.codec.socksx.v4.Socks4CmdStatus; import io.netty.handler.codec.socksx.v5.Socks5CmdRequest; import io.netty.handler.codec.socksx.v5.Socks5CmdResponse; import io.netty.handler.codec.socksx.v5.Socks5CmdStatus; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; @ChannelHandler.Sharable public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksRequest> { private final Bootstrap b = new Bootstrap(); @Override public void messageReceived(final ChannelHandlerContext ctx, final SocksRequest message) throws Exception { if (message instanceof Socks4CmdRequest) { final Socks4CmdRequest request = (Socks4CmdRequest) message; Promise<Channel> promise = ctx.executor().newPromise(); promise.addListener(new GenericFutureListener<Future<Channel>>() { @Override public void operationComplete(final Future<Channel> future) throws Exception { final Channel outboundChannel = future.getNow(); if (future.isSuccess()) { ctx.channel().writeAndFlush(new Socks4CmdResponse(Socks4CmdStatus.SUCCESS)) .addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) { ctx.pipeline().remove(SocksServerConnectHandler.this); outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel())); ctx.pipeline().addLast(new RelayHandler(outboundChannel)); } }); } else { ctx.channel().writeAndFlush(new Socks4CmdResponse(Socks4CmdStatus.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.host(), request.port()).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { // Connection established use handler provided results } else { // Close the connection if the connection attempt has failed. ctx.channel().writeAndFlush(new Socks4CmdResponse(Socks4CmdStatus.REJECTED_OR_FAILED)); SocksServerUtils.closeOnFlush(ctx.channel()); } } }); } else if (message instanceof Socks5CmdRequest) { final Socks5CmdRequest request = (Socks5CmdRequest) message; Promise<Channel> promise = ctx.executor().newPromise(); promise.addListener(new GenericFutureListener<Future<Channel>>() { @Override public void operationComplete(final Future<Channel> future) throws Exception { final Channel outboundChannel = future.getNow(); if (future.isSuccess()) { ctx.channel() .writeAndFlush( new Socks5CmdResponse(Socks5CmdStatus.SUCCESS, request.addressType())) .addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) { ctx.pipeline().remove(SocksServerConnectHandler.this); outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel())); ctx.pipeline().addLast(new RelayHandler(outboundChannel)); } }); } else { ctx.channel().writeAndFlush( new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType())); 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.host(), request.port()).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { // Connection established use handler provided results } else { // Close the connection if the connection attempt has failed. ctx.channel().writeAndFlush( new Socks5CmdResponse(Socks5CmdStatus.FAILURE, request.addressType())); SocksServerUtils.closeOnFlush(ctx.channel()); } } }); } else { ctx.close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { SocksServerUtils.closeOnFlush(ctx.channel()); } }