1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.haproxy;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufProcessor;
20 import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
21 import io.netty.util.CharsetUtil;
22 import io.netty.util.NetUtil;
23
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27
28
29
30
31 public final class HAProxyMessage {
32
33
34
35
36
37 private static final HAProxyMessage V1_UNKNOWN_MSG = new HAProxyMessage(
38 HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0);
39
40
41
42
43
44 private static final HAProxyMessage V2_UNKNOWN_MSG = new HAProxyMessage(
45 HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0);
46
47
48
49
50
51 private static final HAProxyMessage V2_LOCAL_MSG = new HAProxyMessage(
52 HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0);
53
54 private final HAProxyProtocolVersion protocolVersion;
55 private final HAProxyCommand command;
56 private final HAProxyProxiedProtocol proxiedProtocol;
57 private final String sourceAddress;
58 private final String destinationAddress;
59 private final int sourcePort;
60 private final int destinationPort;
61 private final List<HAProxyTLV> tlvs;
62
63
64
65
66 private HAProxyMessage(
67 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
68 String sourceAddress, String destinationAddress, String sourcePort, String destinationPort) {
69 this(
70 protocolVersion, command, proxiedProtocol,
71 sourceAddress, destinationAddress, portStringToInt(sourcePort), portStringToInt(destinationPort));
72 }
73
74
75
76
77 private HAProxyMessage(
78 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
79 String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) {
80
81 this(protocolVersion, command, proxiedProtocol,
82 sourceAddress, destinationAddress, sourcePort, destinationPort, Collections.<HAProxyTLV>emptyList());
83 }
84
85
86
87
88 private HAProxyMessage(
89 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
90 String sourceAddress, String destinationAddress, int sourcePort, int destinationPort,
91 List<HAProxyTLV> tlvs) {
92
93 if (proxiedProtocol == null) {
94 throw new NullPointerException("proxiedProtocol");
95 }
96 AddressFamily addrFamily = proxiedProtocol.addressFamily();
97
98 checkAddress(sourceAddress, addrFamily);
99 checkAddress(destinationAddress, addrFamily);
100 checkPort(sourcePort);
101 checkPort(destinationPort);
102
103 this.protocolVersion = protocolVersion;
104 this.command = command;
105 this.proxiedProtocol = proxiedProtocol;
106 this.sourceAddress = sourceAddress;
107 this.destinationAddress = destinationAddress;
108 this.sourcePort = sourcePort;
109 this.destinationPort = destinationPort;
110 this.tlvs = Collections.unmodifiableList(tlvs);
111 }
112
113
114
115
116
117
118
119
120 static HAProxyMessage decodeHeader(ByteBuf header) {
121 if (header == null) {
122 throw new NullPointerException("header");
123 }
124
125 if (header.readableBytes() < 16) {
126 throw new HAProxyProtocolException(
127 "incomplete header: " + header.readableBytes() + " bytes (expected: 16+ bytes)");
128 }
129
130
131 header.skipBytes(12);
132 final byte verCmdByte = header.readByte();
133
134 HAProxyProtocolVersion ver;
135 try {
136 ver = HAProxyProtocolVersion.valueOf(verCmdByte);
137 } catch (IllegalArgumentException e) {
138 throw new HAProxyProtocolException(e);
139 }
140
141 if (ver != HAProxyProtocolVersion.V2) {
142 throw new HAProxyProtocolException("version 1 unsupported: 0x" + Integer.toHexString(verCmdByte));
143 }
144
145 HAProxyCommand cmd;
146 try {
147 cmd = HAProxyCommand.valueOf(verCmdByte);
148 } catch (IllegalArgumentException e) {
149 throw new HAProxyProtocolException(e);
150 }
151
152 if (cmd == HAProxyCommand.LOCAL) {
153 return V2_LOCAL_MSG;
154 }
155
156
157 HAProxyProxiedProtocol protAndFam;
158 try {
159 protAndFam = HAProxyProxiedProtocol.valueOf(header.readByte());
160 } catch (IllegalArgumentException e) {
161 throw new HAProxyProtocolException(e);
162 }
163
164 if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) {
165 return V2_UNKNOWN_MSG;
166 }
167
168 int addressInfoLen = header.readUnsignedShort();
169
170 String srcAddress;
171 String dstAddress;
172 int addressLen;
173 int srcPort = 0;
174 int dstPort = 0;
175
176 AddressFamily addressFamily = protAndFam.addressFamily();
177
178 if (addressFamily == AddressFamily.AF_UNIX) {
179
180 if (addressInfoLen < 216 || header.readableBytes() < 216) {
181 throw new HAProxyProtocolException(
182 "incomplete UNIX socket address information: " +
183 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)");
184 }
185 int startIdx = header.readerIndex();
186 int addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL);
187 if (addressEnd == -1) {
188 addressLen = 108;
189 } else {
190 addressLen = addressEnd - startIdx;
191 }
192 srcAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
193
194 startIdx += 108;
195
196 addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL);
197 if (addressEnd == -1) {
198 addressLen = 108;
199 } else {
200 addressLen = addressEnd - startIdx;
201 }
202 dstAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
203
204
205 header.readerIndex(startIdx + 108);
206 } else {
207 if (addressFamily == AddressFamily.AF_IPv4) {
208
209 if (addressInfoLen < 12 || header.readableBytes() < 12) {
210 throw new HAProxyProtocolException(
211 "incomplete IPv4 address information: " +
212 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 12+ bytes)");
213 }
214 addressLen = 4;
215 } else if (addressFamily == AddressFamily.AF_IPv6) {
216
217 if (addressInfoLen < 36 || header.readableBytes() < 36) {
218 throw new HAProxyProtocolException(
219 "incomplete IPv6 address information: " +
220 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 36+ bytes)");
221 }
222 addressLen = 16;
223 } else {
224 throw new HAProxyProtocolException(
225 "unable to parse address information (unknown address family: " + addressFamily + ')');
226 }
227
228
229 srcAddress = ipBytesToString(header, addressLen);
230 dstAddress = ipBytesToString(header, addressLen);
231 srcPort = header.readUnsignedShort();
232 dstPort = header.readUnsignedShort();
233 }
234
235 final List<HAProxyTLV> tlvs = readTlvs(header);
236
237 return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort, tlvs);
238 }
239
240 private static List<HAProxyTLV> readTlvs(final ByteBuf header) {
241 HAProxyTLV haProxyTLV = readNextTLV(header);
242 if (haProxyTLV == null) {
243 return Collections.emptyList();
244 }
245
246 List<HAProxyTLV> haProxyTLVs = new ArrayList<HAProxyTLV>(4);
247
248 do {
249 haProxyTLVs.add(haProxyTLV);
250 if (haProxyTLV instanceof HAProxySSLTLV) {
251 haProxyTLVs.addAll(((HAProxySSLTLV) haProxyTLV).encapsulatedTLVs());
252 }
253 } while ((haProxyTLV = readNextTLV(header)) != null);
254 return haProxyTLVs;
255 }
256
257 private static HAProxyTLV readNextTLV(final ByteBuf header) {
258
259
260 if (header.readableBytes() < 4) {
261 return null;
262 }
263
264 final byte typeAsByte = header.readByte();
265 final HAProxyTLV.Type type = HAProxyTLV.Type.typeForByteValue(typeAsByte);
266
267 final int length = header.readUnsignedShort();
268 switch (type) {
269 case PP2_TYPE_SSL:
270 final ByteBuf rawContent = header.slice(header.readerIndex(), length).retain();
271 final ByteBuf byteBuf = header.readSlice(length);
272 final byte client = byteBuf.readByte();
273 final int verify = byteBuf.readInt();
274
275 if (byteBuf.readableBytes() >= 4) {
276
277 final List<HAProxyTLV> encapsulatedTlvs = new ArrayList<HAProxyTLV>(4);
278 do {
279 final HAProxyTLV haProxyTLV = readNextTLV(byteBuf);
280 if (haProxyTLV == null) {
281 break;
282 }
283 encapsulatedTlvs.add(haProxyTLV);
284 } while (byteBuf.readableBytes() >= 4);
285
286 return new HAProxySSLTLV(verify, client, encapsulatedTlvs, rawContent);
287 }
288 return new HAProxySSLTLV(verify, client, Collections.<HAProxyTLV>emptyList(), rawContent);
289
290 case PP2_TYPE_ALPN:
291 case PP2_TYPE_AUTHORITY:
292 case PP2_TYPE_SSL_VERSION:
293 case PP2_TYPE_SSL_CN:
294 case PP2_TYPE_NETNS:
295 case OTHER:
296 return new HAProxyTLV(type, typeAsByte, header.readSlice(length).retain());
297 default:
298 return null;
299 }
300 }
301
302
303
304
305
306
307
308
309 static HAProxyMessage decodeHeader(String header) {
310 if (header == null) {
311 throw new HAProxyProtocolException("header");
312 }
313
314 String[] parts = header.split(" ");
315 int numParts = parts.length;
316
317 if (numParts < 2) {
318 throw new HAProxyProtocolException(
319 "invalid header: " + header + " (expected: 'PROXY' and proxied protocol values)");
320 }
321
322 if (!"PROXY".equals(parts[0])) {
323 throw new HAProxyProtocolException("unknown identifier: " + parts[0]);
324 }
325
326 HAProxyProxiedProtocol protAndFam;
327 try {
328 protAndFam = HAProxyProxiedProtocol.valueOf(parts[1]);
329 } catch (IllegalArgumentException e) {
330 throw new HAProxyProtocolException(e);
331 }
332
333 if (protAndFam != HAProxyProxiedProtocol.TCP4 &&
334 protAndFam != HAProxyProxiedProtocol.TCP6 &&
335 protAndFam != HAProxyProxiedProtocol.UNKNOWN) {
336 throw new HAProxyProtocolException("unsupported v1 proxied protocol: " + parts[1]);
337 }
338
339 if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) {
340 return V1_UNKNOWN_MSG;
341 }
342
343 if (numParts != 6) {
344 throw new HAProxyProtocolException("invalid TCP4/6 header: " + header + " (expected: 6 parts)");
345 }
346
347 return new HAProxyMessage(
348 HAProxyProtocolVersion.V1, HAProxyCommand.PROXY,
349 protAndFam, parts[2], parts[3], parts[4], parts[5]);
350 }
351
352
353
354
355
356
357
358
359 private static String ipBytesToString(ByteBuf header, int addressLen) {
360 StringBuilder sb = new StringBuilder();
361 if (addressLen == 4) {
362 sb.append(header.readByte() & 0xff);
363 sb.append('.');
364 sb.append(header.readByte() & 0xff);
365 sb.append('.');
366 sb.append(header.readByte() & 0xff);
367 sb.append('.');
368 sb.append(header.readByte() & 0xff);
369 } else {
370 sb.append(Integer.toHexString(header.readUnsignedShort()));
371 sb.append(':');
372 sb.append(Integer.toHexString(header.readUnsignedShort()));
373 sb.append(':');
374 sb.append(Integer.toHexString(header.readUnsignedShort()));
375 sb.append(':');
376 sb.append(Integer.toHexString(header.readUnsignedShort()));
377 sb.append(':');
378 sb.append(Integer.toHexString(header.readUnsignedShort()));
379 sb.append(':');
380 sb.append(Integer.toHexString(header.readUnsignedShort()));
381 sb.append(':');
382 sb.append(Integer.toHexString(header.readUnsignedShort()));
383 sb.append(':');
384 sb.append(Integer.toHexString(header.readUnsignedShort()));
385 }
386 return sb.toString();
387 }
388
389
390
391
392
393
394
395
396 private static int portStringToInt(String value) {
397 int port;
398 try {
399 port = Integer.parseInt(value);
400 } catch (NumberFormatException e) {
401 throw new HAProxyProtocolException("invalid port: " + value, e);
402 }
403
404 if (port <= 0 || port > 65535) {
405 throw new HAProxyProtocolException("invalid port: " + value + " (expected: 1 ~ 65535)");
406 }
407
408 return port;
409 }
410
411
412
413
414
415
416
417
418 private static void checkAddress(String address, AddressFamily addrFamily) {
419 if (addrFamily == null) {
420 throw new NullPointerException("addrFamily");
421 }
422
423 switch (addrFamily) {
424 case AF_UNSPEC:
425 if (address != null) {
426 throw new HAProxyProtocolException("unable to validate an AF_UNSPEC address: " + address);
427 }
428 return;
429 case AF_UNIX:
430 return;
431 }
432
433 if (address == null) {
434 throw new NullPointerException("address");
435 }
436
437 switch (addrFamily) {
438 case AF_IPv4:
439 if (!NetUtil.isValidIpV4Address(address)) {
440 throw new HAProxyProtocolException("invalid IPv4 address: " + address);
441 }
442 break;
443 case AF_IPv6:
444 if (!NetUtil.isValidIpV6Address(address)) {
445 throw new HAProxyProtocolException("invalid IPv6 address: " + address);
446 }
447 break;
448 default:
449 throw new Error();
450 }
451 }
452
453
454
455
456
457
458
459 private static void checkPort(int port) {
460 if (port < 0 || port > 65535) {
461 throw new HAProxyProtocolException("invalid port: " + port + " (expected: 1 ~ 65535)");
462 }
463 }
464
465
466
467
468 public HAProxyProtocolVersion protocolVersion() {
469 return protocolVersion;
470 }
471
472
473
474
475 public HAProxyCommand command() {
476 return command;
477 }
478
479
480
481
482 public HAProxyProxiedProtocol proxiedProtocol() {
483 return proxiedProtocol;
484 }
485
486
487
488
489 public String sourceAddress() {
490 return sourceAddress;
491 }
492
493
494
495
496 public String destinationAddress() {
497 return destinationAddress;
498 }
499
500
501
502
503 public int sourcePort() {
504 return sourcePort;
505 }
506
507
508
509
510 public int destinationPort() {
511 return destinationPort;
512 }
513
514
515
516
517
518
519 public List<HAProxyTLV> tlvs() {
520 return tlvs;
521 }
522 }