View Javadoc
1   /*
2    * Copyright 2025 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  package io.netty.handler.codec.socksx.v5;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelHandlerContext;
20  import io.netty.handler.codec.DecoderException;
21  import io.netty.handler.codec.DecoderResult;
22  import io.netty.handler.codec.ByteToMessageDecoder;
23  import io.netty.util.internal.EmptyArrays;
24  
25  import java.util.List;
26  
27  /**
28   * Decodes a single {@link Socks5PrivateAuthRequest} from the inbound {@link ByteBuf}s.
29   * On successful decode, this decoder will forward the received data to the next handler, so that
30   * other handler can remove or replace this decoder later.
31   */
32  public final class Socks5PrivateAuthRequestDecoder extends ByteToMessageDecoder {
33  
34      private boolean decoded;
35  
36      @Override
37      protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
38          try {
39              if (decoded) {
40                  int readableBytes = in.readableBytes();
41                  if (readableBytes > 0) {
42                      out.add(in.readRetainedSlice(readableBytes));
43                  }
44                  return;
45              }
46  
47              // Check if we have enough data to decode the message
48              if (in.readableBytes() < 2) {
49                  return;
50              }
51  
52              final int startOffset = in.readerIndex();
53              final byte version = in.getByte(startOffset);
54              if (version != 1) {
55                  throw new DecoderException("unsupported subnegotiation version: " + version + " (expected: 1)");
56              }
57  
58              final int tokenLength = in.getUnsignedByte(startOffset + 1);
59  
60              // Check if the full message is available
61              if (in.readableBytes() < 2 + tokenLength) {
62                  return;
63              }
64  
65              // Read the version and token length
66              in.skipBytes(2);
67  
68              // Read the token
69              byte[] token = new byte[tokenLength];
70              in.readBytes(token);
71  
72              // Add the decoded token to the output list
73              out.add(new DefaultSocks5PrivateAuthRequest(token));
74  
75              // Mark as decoded to handle remaining bytes in future calls
76              decoded = true;
77          } catch (Exception e) {
78              fail(out, e);
79          }
80      }
81  
82      private void fail(List<Object> out, Exception cause) {
83          if (!(cause instanceof DecoderException)) {
84              cause = new DecoderException(cause);
85          }
86  
87          decoded = true;
88  
89          Socks5Message m = new
90              DefaultSocks5PrivateAuthRequest(EmptyArrays.EMPTY_BYTES);
91          m.setDecoderResult(DecoderResult.failure(cause));
92          out.add(m);
93      }
94  }