1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.socksx.v4;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufUtil;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.handler.codec.DecoderException;
22 import io.netty.handler.codec.DecoderResult;
23 import io.netty.handler.codec.ReplayingDecoder;
24 import io.netty.handler.codec.socksx.SocksVersion;
25 import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder.State;
26 import io.netty.util.CharsetUtil;
27 import io.netty.util.NetUtil;
28 import io.netty.util.internal.UnstableApi;
29
30 import java.util.List;
31
32
33
34
35
36
37
38 public class Socks4ServerDecoder extends ReplayingDecoder<State> {
39
40 private static final int MAX_FIELD_LENGTH = 255;
41
42 @UnstableApi
43 public enum State {
44 START,
45 READ_USERID,
46 READ_DOMAIN,
47 SUCCESS,
48 FAILURE
49 }
50
51 private Socks4CommandType type;
52 private String dstAddr;
53 private int dstPort;
54 private String userId;
55
56 public Socks4ServerDecoder() {
57 super(State.START);
58 setSingleDecode(true);
59 }
60
61 @Override
62 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
63 try {
64 switch (state()) {
65 case START: {
66 final int version = in.readUnsignedByte();
67 if (version != SocksVersion.SOCKS4a.byteValue()) {
68 throw new DecoderException("unsupported protocol version: " + version);
69 }
70
71 type = Socks4CommandType.valueOf(in.readByte());
72 dstPort = ByteBufUtil.readUnsignedShortBE(in);
73 dstAddr = NetUtil.intToIpAddress(ByteBufUtil.readIntBE(in));
74 checkpoint(State.READ_USERID);
75 }
76 case READ_USERID: {
77 userId = readString("userid", in);
78 checkpoint(State.READ_DOMAIN);
79 }
80 case READ_DOMAIN: {
81
82 if (!"0.0.0.0".equals(dstAddr) && dstAddr.startsWith("0.0.0.")) {
83 dstAddr = readString("dstAddr", in);
84 }
85 out.add(new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId));
86 checkpoint(State.SUCCESS);
87 }
88 case SUCCESS: {
89 int readableBytes = actualReadableBytes();
90 if (readableBytes > 0) {
91 out.add(in.readRetainedSlice(readableBytes));
92 }
93 break;
94 }
95 case FAILURE: {
96 in.skipBytes(actualReadableBytes());
97 break;
98 }
99 }
100 } catch (Exception e) {
101 fail(out, e);
102 }
103 }
104
105 private void fail(List<Object> out, Exception cause) {
106 if (!(cause instanceof DecoderException)) {
107 cause = new DecoderException(cause);
108 }
109
110 Socks4CommandRequest m = new DefaultSocks4CommandRequest(
111 type != null? type : Socks4CommandType.CONNECT,
112 dstAddr != null? dstAddr : "",
113 dstPort != 0? dstPort : 65535,
114 userId != null? userId : "");
115
116 m.setDecoderResult(DecoderResult.failure(cause));
117 out.add(m);
118
119 checkpoint(State.FAILURE);
120 }
121
122
123
124
125 private static String readString(String fieldName, ByteBuf in) {
126 int length = in.bytesBefore(MAX_FIELD_LENGTH + 1, (byte) 0);
127 if (length < 0) {
128 throw new DecoderException("field '" + fieldName + "' longer than " + MAX_FIELD_LENGTH + " chars");
129 }
130
131 String value = in.readSlice(length).toString(CharsetUtil.US_ASCII);
132 in.skipBytes(1);
133
134 return value;
135 }
136 }