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