1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
43
44 protected Socks5ClientEncoder() {
45 this(Socks5AddressEncoder.DEFAULT);
46 }
47
48
49
50
51 public Socks5ClientEncoder(Socks5AddressEncoder addressEncoder) {
52 this.addressEncoder = ObjectUtil.checkNotNull(addressEncoder, "addressEncoder");
53 }
54
55
56
57
58 protected final Socks5AddressEncoder addressEncoder() {
59 return addressEncoder;
60 }
61
62 @Override
63 protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception {
64 if (msg instanceof Socks5InitialRequest) {
65 encodeAuthMethodRequest((Socks5InitialRequest) msg, out);
66 } else if (msg instanceof Socks5PasswordAuthRequest) {
67 encodePasswordAuthRequest((Socks5PasswordAuthRequest) msg, out);
68 } else if (msg instanceof Socks5CommandRequest) {
69 encodeCommandRequest((Socks5CommandRequest) msg, out);
70 } else {
71 throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg));
72 }
73 }
74
75 private static void encodeAuthMethodRequest(Socks5InitialRequest msg, ByteBuf out) {
76 out.writeByte(msg.version().byteValue());
77
78 final List<Socks5AuthMethod> authMethods = msg.authMethods();
79 final int numAuthMethods = authMethods.size();
80 writeFieldLength(out, numAuthMethods);
81
82 if (authMethods instanceof RandomAccess) {
83 for (int i = 0; i < numAuthMethods; i ++) {
84 out.writeByte(authMethods.get(i).byteValue());
85 }
86 } else {
87 for (Socks5AuthMethod a: authMethods) {
88 out.writeByte(a.byteValue());
89 }
90 }
91 }
92
93 private static void encodePasswordAuthRequest(Socks5PasswordAuthRequest msg, ByteBuf out) {
94 out.writeByte(0x01);
95
96 final String username = msg.username();
97 writeFieldLength(out, username.length());
98 ByteBufUtil.writeAscii(out, username);
99
100 final String password = msg.password();
101 writeFieldLength(out, password.length());
102 ByteBufUtil.writeAscii(out, password);
103 }
104
105 private void encodeCommandRequest(Socks5CommandRequest msg, ByteBuf out) throws Exception {
106 out.writeByte(msg.version().byteValue());
107 out.writeByte(msg.type().byteValue());
108 out.writeByte(0x00);
109
110 final Socks5AddressType dstAddrType = msg.dstAddrType();
111 out.writeByte(dstAddrType.byteValue());
112 String addrValue = msg.dstAddr();
113 if (addrValue != null && dstAddrType == Socks5AddressType.DOMAIN) {
114 checkFieldLength(addrValue.length());
115 }
116 addressEncoder.encodeAddress(dstAddrType, addrValue, out);
117 ByteBufUtil.writeShortBE(out, msg.dstPort());
118 }
119
120 private static void writeFieldLength(ByteBuf out, int length) {
121 checkFieldLength(length);
122 out.writeByte(length);
123 }
124
125 private static void checkFieldLength(int length) {
126 if (length > 255 || length < 0) {
127 throw new EncoderException("Invalid field length value: " + length);
128 }
129 }
130 }