View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.example.socksproxy;
17  
18  import io.netty.bootstrap.Bootstrap;
19  import io.netty.channel.Channel;
20  import io.netty.channel.ChannelFuture;
21  import io.netty.channel.ChannelFutureListener;
22  import io.netty.channel.ChannelHandler;
23  import io.netty.channel.ChannelHandlerContext;
24  import io.netty.channel.ChannelOption;
25  import io.netty.channel.SimpleChannelInboundHandler;
26  import io.netty.channel.socket.nio.NioSocketChannel;
27  import io.netty.handler.codec.socks.SocksCmdRequest;
28  import io.netty.handler.codec.socks.SocksCmdResponse;
29  import io.netty.handler.codec.socks.SocksCmdStatus;
30  import io.netty.util.concurrent.Future;
31  import io.netty.util.concurrent.GenericFutureListener;
32  import io.netty.util.concurrent.Promise;
33  
34  @ChannelHandler.Sharable
35  public final class SocksServerConnectHandler extends SimpleChannelInboundHandler<SocksCmdRequest> {
36  
37      private final Bootstrap b = new Bootstrap();
38  
39      @Override
40      public void channelRead0(final ChannelHandlerContext ctx, final SocksCmdRequest request) throws Exception {
41          Promise<Channel> promise = ctx.executor().newPromise();
42          promise.addListener(
43              new GenericFutureListener<Future<Channel>>() {
44              @Override
45              public void operationComplete(final Future<Channel> future) throws Exception {
46                  final Channel outboundChannel = future.getNow();
47                  if (future.isSuccess()) {
48                      ctx.channel().writeAndFlush(new SocksCmdResponse(SocksCmdStatus.SUCCESS, request.addressType()))
49                              .addListener(new ChannelFutureListener() {
50                                  @Override
51                                  public void operationComplete(ChannelFuture channelFuture) {
52                                      ctx.pipeline().remove(SocksServerConnectHandler.this);
53                                      outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
54                                      ctx.pipeline().addLast(new RelayHandler(outboundChannel));
55                                  }
56                              });
57                  } else {
58                      ctx.channel().writeAndFlush(new SocksCmdResponse(SocksCmdStatus.FAILURE, request.addressType()));
59                      SocksServerUtils.closeOnFlush(ctx.channel());
60                  }
61              }
62          });
63  
64          final Channel inboundChannel = ctx.channel();
65          b.group(inboundChannel.eventLoop())
66                  .channel(NioSocketChannel.class)
67                  .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
68                  .option(ChannelOption.SO_KEEPALIVE, true)
69                  .handler(new DirectClientHandler(promise));
70  
71          b.connect(request.host(), request.port()).addListener(new ChannelFutureListener() {
72              @Override
73              public void operationComplete(ChannelFuture future) throws Exception {
74                  if (future.isSuccess()) {
75                      // Connection established use handler provided results
76                  } else {
77                      // Close the connection if the connection attempt has failed.
78                      ctx.channel().writeAndFlush(
79                              new SocksCmdResponse(SocksCmdStatus.FAILURE, request.addressType()));
80                      SocksServerUtils.closeOnFlush(ctx.channel());
81                  }
82              }
83          });
84      }
85  
86      @Override
87      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
88          SocksServerUtils.closeOnFlush(ctx.channel());
89      }
90  }