View Javadoc
1   /*
2    * Copyright 2015 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  
17  package io.netty.handler.codec.socksx;
18  
19  import io.netty.buffer.ByteBuf;
20  import io.netty.channel.ChannelHandlerContext;
21  import io.netty.channel.ChannelPipeline;
22  import io.netty.handler.codec.ByteToMessageDecoder;
23  import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder;
24  import io.netty.handler.codec.socksx.v4.Socks4ServerEncoder;
25  import io.netty.handler.codec.socksx.v5.Socks5AddressEncoder;
26  import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder;
27  import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder;
28  import io.netty.util.internal.logging.InternalLogger;
29  import io.netty.util.internal.logging.InternalLoggerFactory;
30  
31  import java.util.List;
32  
33  /**
34   * Detects the version of the current SOCKS connection and initializes the pipeline with
35   * {@link Socks4ServerDecoder} or {@link Socks5InitialRequestDecoder}.
36   */
37  public class SocksPortUnificationServerHandler extends ByteToMessageDecoder {
38  
39      private static final InternalLogger logger =
40              InternalLoggerFactory.getInstance(SocksPortUnificationServerHandler.class);
41  
42      private final Socks5ServerEncoder socks5encoder;
43  
44      /**
45       * Creates a new instance with the default configuration.
46       */
47      public SocksPortUnificationServerHandler() {
48          this(Socks5ServerEncoder.DEFAULT);
49      }
50  
51      /**
52       * Creates a new instance with the specified {@link Socks5ServerEncoder}.
53       * This constructor is useful when a user wants to use an alternative {@link Socks5AddressEncoder}.
54       */
55      public SocksPortUnificationServerHandler(Socks5ServerEncoder socks5encoder) {
56          if (socks5encoder == null) {
57              throw new NullPointerException("socks5encoder");
58          }
59  
60          this.socks5encoder = socks5encoder;
61      }
62  
63      @Override
64      protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
65          final int readerIndex = in.readerIndex();
66          if (in.writerIndex() == readerIndex) {
67              return;
68          }
69  
70          ChannelPipeline p = ctx.pipeline();
71          final byte versionVal = in.getByte(readerIndex);
72          SocksVersion version = SocksVersion.valueOf(versionVal);
73  
74          switch (version) {
75          case SOCKS4a:
76              logKnownVersion(ctx, version);
77              p.addAfter(ctx.name(), null, Socks4ServerEncoder.INSTANCE);
78              p.addAfter(ctx.name(), null, new Socks4ServerDecoder());
79              break;
80          case SOCKS5:
81              logKnownVersion(ctx, version);
82              p.addAfter(ctx.name(), null, socks5encoder);
83              p.addAfter(ctx.name(), null, new Socks5InitialRequestDecoder());
84              break;
85          default:
86              logUnknownVersion(ctx, versionVal);
87              in.skipBytes(in.readableBytes());
88              ctx.close();
89              return;
90          }
91  
92          p.remove(this);
93      }
94  
95      private static void logKnownVersion(ChannelHandlerContext ctx, SocksVersion version) {
96          logger.debug("{} Protocol version: {}({})", ctx.channel(), version);
97      }
98  
99      private static void logUnknownVersion(ChannelHandlerContext ctx, byte versionVal) {
100         if (logger.isDebugEnabled()) {
101             logger.debug("{} Unknown protocol version: {}", ctx.channel(), versionVal & 0xFF);
102         }
103     }
104 }