1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.socks;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufUtil;
20 import io.netty.util.CharsetUtil;
21 import io.netty.util.NetUtil;
22 import io.netty.util.internal.ObjectUtil;
23
24 import java.net.IDN;
25
26
27
28
29
30
31
32 public final class SocksCmdResponse extends SocksResponse {
33 private final SocksCmdStatus cmdStatus;
34
35 private final SocksAddressType addressType;
36 private final String host;
37 private final int port;
38
39
40 private static final byte[] DOMAIN_ZEROED = {0x00};
41 private static final byte[] IPv4_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00};
42 private static final byte[] IPv6_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00, 0x00, 0x00,
45 0x00, 0x00, 0x00, 0x00};
46
47 public SocksCmdResponse(SocksCmdStatus cmdStatus, SocksAddressType addressType) {
48 this(cmdStatus, addressType, null, 0);
49 }
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public SocksCmdResponse(SocksCmdStatus cmdStatus, SocksAddressType addressType, String host, int port) {
65 super(SocksResponseType.CMD);
66 ObjectUtil.checkNotNull(cmdStatus, "cmdStatus");
67 ObjectUtil.checkNotNull(addressType, "addressType");
68 if (host != null) {
69 switch (addressType) {
70 case IPv4:
71 if (!NetUtil.isValidIpV4Address(host)) {
72 throw new IllegalArgumentException(host + " is not a valid IPv4 address");
73 }
74 break;
75 case DOMAIN:
76 String asciiHost = IDN.toASCII(host);
77 if (asciiHost.length() > 255) {
78 throw new IllegalArgumentException(host + " IDN: " + asciiHost + " exceeds 255 char limit");
79 }
80 host = asciiHost;
81 break;
82 case IPv6:
83 if (!NetUtil.isValidIpV6Address(host)) {
84 throw new IllegalArgumentException(host + " is not a valid IPv6 address");
85 }
86 break;
87 case UNKNOWN:
88 break;
89 }
90 }
91 if (port < 0 || port > 65535) {
92 throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535");
93 }
94 this.cmdStatus = cmdStatus;
95 this.addressType = addressType;
96 this.host = host;
97 this.port = port;
98 }
99
100
101
102
103
104
105 public SocksCmdStatus cmdStatus() {
106 return cmdStatus;
107 }
108
109
110
111
112
113
114 public SocksAddressType addressType() {
115 return addressType;
116 }
117
118
119
120
121
122
123
124
125
126 public String host() {
127 return host != null && addressType == SocksAddressType.DOMAIN ? IDN.toUnicode(host) : host;
128 }
129
130
131
132
133
134
135
136 public int port() {
137 return port;
138 }
139
140 @Override
141 public void encodeAsByteBuf(ByteBuf byteBuf) {
142 byteBuf.writeByte(protocolVersion().byteValue());
143 byteBuf.writeByte(cmdStatus.byteValue());
144 byteBuf.writeByte(0x00);
145 byteBuf.writeByte(addressType.byteValue());
146 switch (addressType) {
147 case IPv4: {
148 byte[] hostContent = host == null ?
149 IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
150 byteBuf.writeBytes(hostContent);
151 ByteBufUtil.writeShortBE(byteBuf, port);
152 break;
153 }
154 case DOMAIN: {
155 if (host != null) {
156 byteBuf.writeByte(host.length());
157 byteBuf.writeCharSequence(host, CharsetUtil.US_ASCII);
158 } else {
159 byteBuf.writeByte(DOMAIN_ZEROED.length);
160 byteBuf.writeBytes(DOMAIN_ZEROED);
161 }
162 ByteBufUtil.writeShortBE(byteBuf, port);
163 break;
164 }
165 case IPv6: {
166 byte[] hostContent = host == null
167 ? IPv6_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host);
168 byteBuf.writeBytes(hostContent);
169 ByteBufUtil.writeShortBE(byteBuf, port);
170 break;
171 }
172 }
173 }
174 }