View Javadoc
1   /*
2    * Copyright 2014 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.v5;
18  
19  import io.netty.buffer.ByteBuf;
20  import io.netty.buffer.ByteBufUtil;
21  import io.netty.channel.ChannelHandler.Sharable;
22  import io.netty.channel.ChannelHandlerContext;
23  import io.netty.handler.codec.EncoderException;
24  import io.netty.handler.codec.MessageToByteEncoder;
25  import io.netty.util.internal.ObjectUtil;
26  import io.netty.util.internal.StringUtil;
27  
28  import java.util.List;
29  import java.util.RandomAccess;
30  
31  /**
32   * Encodes a client-side {@link Socks5Message} into a {@link ByteBuf}.
33   */
34  @Sharable
35  public class Socks5ClientEncoder extends MessageToByteEncoder<Socks5Message> {
36  
37      public static final Socks5ClientEncoder DEFAULT = new Socks5ClientEncoder();
38  
39      private final Socks5AddressEncoder addressEncoder;
40  
41      /**
42       * Creates a new instance with the default {@link Socks5AddressEncoder}.
43       */
44      protected Socks5ClientEncoder() {
45          this(Socks5AddressEncoder.DEFAULT);
46      }
47  
48      /**
49       * Creates a new instance with the specified {@link Socks5AddressEncoder}.
50       */
51      public Socks5ClientEncoder(Socks5AddressEncoder addressEncoder) {
52          super(Socks5Message.class);
53          this.addressEncoder = ObjectUtil.checkNotNull(addressEncoder, "addressEncoder");
54      }
55  
56      /**
57       * Returns the {@link Socks5AddressEncoder} of this encoder.
58       */
59      protected final Socks5AddressEncoder addressEncoder() {
60          return addressEncoder;
61      }
62  
63      @Override
64      protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception {
65          if (msg instanceof Socks5InitialRequest) {
66              encodeAuthMethodRequest((Socks5InitialRequest) msg, out);
67          } else if (msg instanceof Socks5PasswordAuthRequest) {
68              encodePasswordAuthRequest((Socks5PasswordAuthRequest) msg, out);
69          } else if (msg instanceof Socks5PrivateAuthRequest) {
70              encodePrivateAuthRequest((Socks5PrivateAuthRequest) msg, out);
71          } else if (msg instanceof Socks5CommandRequest) {
72              encodeCommandRequest((Socks5CommandRequest) msg, out);
73          } else {
74              throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg));
75          }
76      }
77  
78      private static void encodeAuthMethodRequest(Socks5InitialRequest msg, ByteBuf out) {
79          out.writeByte(msg.version().byteValue());
80  
81          final List<Socks5AuthMethod> authMethods = msg.authMethods();
82          final int numAuthMethods = authMethods.size();
83          out.writeByte(numAuthMethods);
84  
85          if (authMethods instanceof RandomAccess) {
86              for (int i = 0; i < numAuthMethods; i ++) {
87                  out.writeByte(authMethods.get(i).byteValue());
88              }
89          } else {
90              for (Socks5AuthMethod a: authMethods) {
91                  out.writeByte(a.byteValue());
92              }
93          }
94      }
95  
96      private static void encodePasswordAuthRequest(Socks5PasswordAuthRequest msg, ByteBuf out) {
97          out.writeByte(0x01);
98  
99          final String username = msg.username();
100         out.writeByte(username.length());
101         ByteBufUtil.writeAscii(out, username);
102 
103         final String password = msg.password();
104         out.writeByte(password.length());
105         ByteBufUtil.writeAscii(out, password);
106     }
107 
108     private static void encodePrivateAuthRequest(Socks5PrivateAuthRequest msg, ByteBuf out) {
109         byte[] bytes = msg.privateToken();
110         out.writeByte(0x01);
111         out.writeByte(bytes.length);
112         out.writeBytes(bytes);
113     }
114 
115     private void encodeCommandRequest(Socks5CommandRequest msg, ByteBuf out) throws Exception {
116         out.writeByte(msg.version().byteValue());
117         out.writeByte(msg.type().byteValue());
118         out.writeByte(0x00);
119 
120         final Socks5AddressType dstAddrType = msg.dstAddrType();
121         out.writeByte(dstAddrType.byteValue());
122         addressEncoder.encodeAddress(dstAddrType, msg.dstAddr(), out);
123         ByteBufUtil.writeShortBE(out, msg.dstPort());
124     }
125 }