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