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.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
20 import io.netty.util.AbstractReferenceCounted;
21 import io.netty.util.CharsetUtil;
22 import io.netty.util.NetUtil;
23 import io.netty.util.ResourceLeakDetector;
24 import io.netty.util.ResourceLeakDetectorFactory;
25 import io.netty.util.ResourceLeakTracker;
26 import io.netty.util.internal.ObjectUtil;
27 import io.netty.util.internal.PlatformDependent;
28 import io.netty.util.internal.StringUtil;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33
34
35
36
37 public final class HAProxyMessage extends AbstractReferenceCounted {
38
39
40 private static final int MAX_NESTING_LEVEL = 128;
41 private static final ResourceLeakDetector<HAProxyMessage> leakDetector =
42 ResourceLeakDetectorFactory.instance().newResourceLeakDetector(HAProxyMessage.class);
43
44 private final ResourceLeakTracker<HAProxyMessage> leak;
45 private final HAProxyProtocolVersion protocolVersion;
46 private final HAProxyCommand command;
47 private final HAProxyProxiedProtocol proxiedProtocol;
48 private final String sourceAddress;
49 private final String destinationAddress;
50 private final int sourcePort;
51 private final int destinationPort;
52 private final List<HAProxyTLV> tlvs;
53
54
55
56
57 private HAProxyMessage(
58 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
59 String sourceAddress, String destinationAddress, String sourcePort, String destinationPort) {
60 this(
61 protocolVersion, command, proxiedProtocol,
62 sourceAddress, destinationAddress, portStringToInt(sourcePort), portStringToInt(destinationPort));
63 }
64
65
66
67
68
69
70
71
72
73
74
75 public HAProxyMessage(
76 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
77 String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) {
78
79 this(protocolVersion, command, proxiedProtocol,
80 sourceAddress, destinationAddress, sourcePort, destinationPort, Collections.<HAProxyTLV>emptyList());
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94 public HAProxyMessage(
95 HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
96 String sourceAddress, String destinationAddress, int sourcePort, int destinationPort,
97 List<? extends HAProxyTLV> tlvs) {
98
99 ObjectUtil.checkNotNull(protocolVersion, "protocolVersion");
100 ObjectUtil.checkNotNull(proxiedProtocol, "proxiedProtocol");
101 ObjectUtil.checkNotNull(tlvs, "tlvs");
102 AddressFamily addrFamily = proxiedProtocol.addressFamily();
103
104 checkAddress(sourceAddress, addrFamily);
105 checkAddress(destinationAddress, addrFamily);
106 checkPort(sourcePort, addrFamily);
107 checkPort(destinationPort, addrFamily);
108
109 this.protocolVersion = protocolVersion;
110 this.command = command;
111 this.proxiedProtocol = proxiedProtocol;
112 this.sourceAddress = sourceAddress;
113 this.destinationAddress = destinationAddress;
114 this.sourcePort = sourcePort;
115 this.destinationPort = destinationPort;
116 this.tlvs = Collections.unmodifiableList(tlvs);
117
118 leak = leakDetector.track(this);
119 }
120
121
122
123
124
125
126
127
128 static HAProxyMessage decodeHeader(ByteBuf header) {
129 ObjectUtil.checkNotNull(header, "header");
130
131 if (header.readableBytes() < 16) {
132 throw new HAProxyProtocolException(
133 "incomplete header: " + header.readableBytes() + " bytes (expected: 16+ bytes)");
134 }
135
136
137 header.skipBytes(12);
138 final byte verCmdByte = header.readByte();
139
140 HAProxyProtocolVersion ver;
141 try {
142 ver = HAProxyProtocolVersion.valueOf(verCmdByte);
143 } catch (IllegalArgumentException e) {
144 throw new HAProxyProtocolException(e);
145 }
146
147 if (ver != HAProxyProtocolVersion.V2) {
148 throw new HAProxyProtocolException("version 1 unsupported: 0x" + Integer.toHexString(verCmdByte));
149 }
150
151 HAProxyCommand cmd;
152 try {
153 cmd = HAProxyCommand.valueOf(verCmdByte);
154 } catch (IllegalArgumentException e) {
155 throw new HAProxyProtocolException(e);
156 }
157
158 if (cmd == HAProxyCommand.LOCAL) {
159 return unknownMsg(HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL);
160 }
161
162
163 HAProxyProxiedProtocol protAndFam;
164 try {
165 protAndFam = HAProxyProxiedProtocol.valueOf(header.readByte());
166 } catch (IllegalArgumentException e) {
167 throw new HAProxyProtocolException(e);
168 }
169
170 if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) {
171 return unknownMsg(HAProxyProtocolVersion.V2, HAProxyCommand.PROXY);
172 }
173
174 int addressInfoLen = header.readUnsignedShort();
175
176 String srcAddress;
177 String dstAddress;
178 int addressLen;
179 int srcPort = 0;
180 int dstPort = 0;
181
182 AddressFamily addressFamily = protAndFam.addressFamily();
183
184 if (addressFamily == AddressFamily.AF_UNIX) {
185
186 if (addressInfoLen < 216 || header.readableBytes() < 216) {
187 throw new HAProxyProtocolException(
188 "incomplete UNIX socket address information: " +
189 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)");
190 }
191 int startIdx = header.readerIndex();
192 int addressEnd = header.indexOf(startIdx, startIdx + 108, (byte) 0);
193 if (addressEnd == -1) {
194 addressLen = 108;
195 } else {
196 addressLen = addressEnd - startIdx;
197 }
198 srcAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
199
200 startIdx += 108;
201
202 addressEnd = header.indexOf(startIdx, startIdx + 108, (byte) 0);
203 if (addressEnd == -1) {
204 addressLen = 108;
205 } else {
206 addressLen = addressEnd - startIdx;
207 }
208 dstAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
209
210
211 header.readerIndex(startIdx + 108);
212 } else {
213 if (addressFamily == AddressFamily.AF_IPv4) {
214
215 if (addressInfoLen < 12 || header.readableBytes() < 12) {
216 throw new HAProxyProtocolException(
217 "incomplete IPv4 address information: " +
218 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 12+ bytes)");
219 }
220 addressLen = 4;
221 } else if (addressFamily == AddressFamily.AF_IPv6) {
222
223 if (addressInfoLen < 36 || header.readableBytes() < 36) {
224 throw new HAProxyProtocolException(
225 "incomplete IPv6 address information: " +
226 Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 36+ bytes)");
227 }
228 addressLen = 16;
229 } else {
230 throw new HAProxyProtocolException(
231 "unable to parse address information (unknown address family: " + addressFamily + ')');
232 }
233
234
235 srcAddress = ipBytesToString(header, addressLen);
236 dstAddress = ipBytesToString(header, addressLen);
237 srcPort = header.readUnsignedShort();
238 dstPort = header.readUnsignedShort();
239 }
240
241 final List<HAProxyTLV> tlvs = readTlvs(header);
242
243 return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort, tlvs);
244 }
245
246 private static List<HAProxyTLV> readTlvs(final ByteBuf header) {
247 HAProxyTLV haProxyTLV = readNextTLV(header, 0);
248 if (haProxyTLV == null) {
249 return Collections.emptyList();
250 }
251
252 List<HAProxyTLV> haProxyTLVs = new ArrayList<HAProxyTLV>(4);
253
254 try {
255 do {
256 haProxyTLVs.add(haProxyTLV);
257 if (haProxyTLV instanceof HAProxySSLTLV) {
258 haProxyTLVs.addAll(((HAProxySSLTLV) haProxyTLV).encapsulatedTLVs());
259 }
260 } while ((haProxyTLV = readNextTLV(header, 0)) != null);
261 } catch (Throwable t) {
262
263 releaseTlvs(haProxyTLVs);
264 PlatformDependent.throwException(t);
265 }
266 return haProxyTLVs;
267 }
268
269 private static void releaseDeep(List<HAProxyTLV> children) {
270 for (HAProxyTLV child : children) {
271 child.release();
272 if (child instanceof HAProxySSLTLV) {
273 releaseDeep(((HAProxySSLTLV) child).encapsulatedTLVs());
274 }
275 }
276 }
277
278 private static void releaseTlvs(List<HAProxyTLV> tlvs) {
279 int skip = 0;
280 for (HAProxyTLV tlv : tlvs) {
281 if (skip > 0) {
282 skip--;
283
284
285 if (tlv instanceof HAProxySSLTLV) {
286 releaseDeep(((HAProxySSLTLV) tlv).encapsulatedTLVs());
287 }
288 } else if (tlv instanceof HAProxySSLTLV) {
289
290
291
292 skip = ((HAProxySSLTLV) tlv).encapsulatedTLVs().size();
293 }
294 tlv.release();
295 }
296 }
297
298 private static HAProxyTLV readNextTLV(final ByteBuf header, int nestingLevel) {
299 if (nestingLevel > MAX_NESTING_LEVEL) {
300 throw new HAProxyProtocolException(
301 "Maximum TLV nesting level reached: " + nestingLevel + " (expected: < " + MAX_NESTING_LEVEL + ')');
302 }
303
304 if (header.readableBytes() < 4) {
305 return null;
306 }
307
308 final byte typeAsByte = header.readByte();
309 final HAProxyTLV.Type type = HAProxyTLV.Type.typeForByteValue(typeAsByte);
310
311 final int length = header.readUnsignedShort();
312 switch (type) {
313 case PP2_TYPE_SSL:
314 if (length < 5) {
315 throw new HAProxyProtocolException("TLV length must be at least 5 but was: " + length);
316 }
317 if (length > header.readableBytes()) {
318 throw new HAProxyProtocolException("TLV length must be smaller or equal the readable bytes (" +
319 header.readableBytes() + ") but was: " + length);
320 }
321
322
323 final ByteBuf rawContent = header.slice(header.readerIndex(), length);
324 final ByteBuf byteBuf = header.readSlice(length);
325 final byte client = byteBuf.readByte();
326 final int verify = byteBuf.readInt();
327
328 if (byteBuf.readableBytes() >= 4) {
329
330 final List<HAProxyTLV> encapsulatedTlvs = new ArrayList<HAProxyTLV>(4);
331 try {
332 do {
333 final HAProxyTLV haProxyTLV = readNextTLV(byteBuf, nestingLevel + 1);
334 if (haProxyTLV == null) {
335 break;
336 }
337 encapsulatedTlvs.add(haProxyTLV);
338 } while (byteBuf.readableBytes() >= 4);
339 } catch (Throwable t) {
340 releaseTlvs(encapsulatedTlvs);
341 PlatformDependent.throwException(t);
342 }
343
344 return new HAProxySSLTLV(verify, client, encapsulatedTlvs, rawContent.retain());
345 }
346 return new HAProxySSLTLV(verify, client, Collections.<HAProxyTLV>emptyList(), rawContent.retain());
347
348 case PP2_TYPE_ALPN:
349 case PP2_TYPE_AUTHORITY:
350 case PP2_TYPE_SSL_VERSION:
351 case PP2_TYPE_SSL_CN:
352 case PP2_TYPE_NETNS:
353 case OTHER:
354 return new HAProxyTLV(type, typeAsByte, header.readRetainedSlice(length));
355 default:
356 return null;
357 }
358 }
359
360
361
362
363
364
365
366
367 static HAProxyMessage decodeHeader(String header) {
368 if (header == null) {
369 throw new HAProxyProtocolException("header");
370 }
371
372 String[] parts = header.split(" ");
373 int numParts = parts.length;
374
375 if (numParts < 2) {
376 throw new HAProxyProtocolException(
377 "invalid header: " + header + " (expected: 'PROXY' and proxied protocol values)");
378 }
379
380 if (!"PROXY".equals(parts[0])) {
381 throw new HAProxyProtocolException("unknown identifier: " + parts[0]);
382 }
383
384 HAProxyProxiedProtocol protAndFam;
385 try {
386 protAndFam = HAProxyProxiedProtocol.valueOf(parts[1]);
387 } catch (IllegalArgumentException e) {
388 throw new HAProxyProtocolException(e);
389 }
390
391 if (protAndFam != HAProxyProxiedProtocol.TCP4 &&
392 protAndFam != HAProxyProxiedProtocol.TCP6 &&
393 protAndFam != HAProxyProxiedProtocol.UNKNOWN) {
394 throw new HAProxyProtocolException("unsupported v1 proxied protocol: " + parts[1]);
395 }
396
397 if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) {
398 return unknownMsg(HAProxyProtocolVersion.V1, HAProxyCommand.PROXY);
399 }
400
401 if (numParts != 6) {
402 throw new HAProxyProtocolException("invalid TCP4/6 header: " + header + " (expected: 6 parts)");
403 }
404
405 try {
406 return new HAProxyMessage(
407 HAProxyProtocolVersion.V1, HAProxyCommand.PROXY,
408 protAndFam, parts[2], parts[3], parts[4], parts[5]);
409 } catch (RuntimeException e) {
410 throw new HAProxyProtocolException("invalid HAProxy message", e);
411 }
412 }
413
414
415
416
417
418 private static HAProxyMessage unknownMsg(HAProxyProtocolVersion version, HAProxyCommand command) {
419 return new HAProxyMessage(version, command, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0);
420 }
421
422
423
424
425
426
427
428
429 private static String ipBytesToString(ByteBuf header, int addressLen) {
430 StringBuilder sb = new StringBuilder();
431 final int ipv4Len = 4;
432 final int ipv6Len = 8;
433 if (addressLen == ipv4Len) {
434 for (int i = 0; i < ipv4Len; i++) {
435 sb.append(header.readByte() & 0xff);
436 sb.append('.');
437 }
438 } else {
439 for (int i = 0; i < ipv6Len; i++) {
440 sb.append(Integer.toHexString(header.readUnsignedShort()));
441 sb.append(':');
442 }
443 }
444 sb.setLength(sb.length() - 1);
445 return sb.toString();
446 }
447
448
449
450
451
452
453
454
455 private static int portStringToInt(String value) {
456 int port;
457 try {
458 port = Integer.parseInt(value);
459 } catch (NumberFormatException e) {
460 throw new IllegalArgumentException("invalid port: " + value, e);
461 }
462
463 if (port <= 0 || port > 65535) {
464 throw new IllegalArgumentException("invalid port: " + value + " (expected: 1 ~ 65535)");
465 }
466
467 return port;
468 }
469
470
471
472
473
474
475
476
477 private static void checkAddress(String address, AddressFamily addrFamily) {
478 ObjectUtil.checkNotNull(addrFamily, "addrFamily");
479
480 switch (addrFamily) {
481 case AF_UNSPEC:
482 if (address != null) {
483 throw new IllegalArgumentException("unable to validate an AF_UNSPEC address: " + address);
484 }
485 return;
486 case AF_UNIX:
487 ObjectUtil.checkNotNull(address, "address");
488 if (address.getBytes(CharsetUtil.US_ASCII).length > 108) {
489 throw new IllegalArgumentException("invalid AF_UNIX address: " + address);
490 }
491 return;
492 }
493
494 ObjectUtil.checkNotNull(address, "address");
495
496 switch (addrFamily) {
497 case AF_IPv4:
498 if (!NetUtil.isValidIpV4Address(address)) {
499 throw new IllegalArgumentException("invalid IPv4 address: " + address);
500 }
501 break;
502 case AF_IPv6:
503 if (!NetUtil.isValidIpV6Address(address)) {
504 throw new IllegalArgumentException("invalid IPv6 address: " + address);
505 }
506 break;
507 default:
508 throw new IllegalArgumentException("unexpected addrFamily: " + addrFamily);
509 }
510 }
511
512
513
514
515
516
517
518 private static void checkPort(int port, AddressFamily addrFamily) {
519 switch (addrFamily) {
520 case AF_IPv6:
521 case AF_IPv4:
522 if (port < 0 || port > 65535) {
523 throw new IllegalArgumentException("invalid port: " + port + " (expected: 0 ~ 65535)");
524 }
525 break;
526 case AF_UNIX:
527 case AF_UNSPEC:
528 if (port != 0) {
529 throw new IllegalArgumentException("port cannot be specified with addrFamily: " + addrFamily);
530 }
531 break;
532 default:
533 throw new IllegalArgumentException("unexpected addrFamily: " + addrFamily);
534 }
535 }
536
537
538
539
540 public HAProxyProtocolVersion protocolVersion() {
541 return protocolVersion;
542 }
543
544
545
546
547 public HAProxyCommand command() {
548 return command;
549 }
550
551
552
553
554 public HAProxyProxiedProtocol proxiedProtocol() {
555 return proxiedProtocol;
556 }
557
558
559
560
561
562 public String sourceAddress() {
563 return sourceAddress;
564 }
565
566
567
568
569 public String destinationAddress() {
570 return destinationAddress;
571 }
572
573
574
575
576 public int sourcePort() {
577 return sourcePort;
578 }
579
580
581
582
583 public int destinationPort() {
584 return destinationPort;
585 }
586
587
588
589
590
591
592 public List<HAProxyTLV> tlvs() {
593 return tlvs;
594 }
595
596 int tlvNumBytes() {
597 int tlvNumBytes = 0;
598 for (int i = 0; i < tlvs.size(); i++) {
599 tlvNumBytes += tlvs.get(i).totalNumBytes();
600 }
601 return tlvNumBytes;
602 }
603
604 @Override
605 public HAProxyMessage touch() {
606 tryRecord();
607 return (HAProxyMessage) super.touch();
608 }
609
610 @Override
611 public HAProxyMessage touch(Object hint) {
612 if (leak != null) {
613 leak.record(hint);
614 }
615 return this;
616 }
617
618 @Override
619 public HAProxyMessage retain() {
620 tryRecord();
621 return (HAProxyMessage) super.retain();
622 }
623
624 @Override
625 public HAProxyMessage retain(int increment) {
626 tryRecord();
627 return (HAProxyMessage) super.retain(increment);
628 }
629
630 @Override
631 public boolean release() {
632 tryRecord();
633 return super.release();
634 }
635
636 @Override
637 public boolean release(int decrement) {
638 tryRecord();
639 return super.release(decrement);
640 }
641
642 private void tryRecord() {
643 if (leak != null) {
644 leak.record();
645 }
646 }
647
648 @Override
649 protected void deallocate() {
650 try {
651 releaseTlvs(tlvs);
652 } finally {
653 final ResourceLeakTracker<HAProxyMessage> leak = this.leak;
654 if (leak != null) {
655 boolean closed = leak.close(this);
656 assert closed;
657 }
658 }
659 }
660
661 @Override
662 public String toString() {
663 StringBuilder sb = new StringBuilder(256)
664 .append(StringUtil.simpleClassName(this))
665 .append("(protocolVersion: ").append(protocolVersion)
666 .append(", command: ").append(command)
667 .append(", proxiedProtocol: ").append(proxiedProtocol)
668 .append(", sourceAddress: ").append(sourceAddress)
669 .append(", destinationAddress: ").append(destinationAddress)
670 .append(", sourcePort: ").append(sourcePort)
671 .append(", destinationPort: ").append(destinationPort)
672 .append(", tlvs: [");
673 if (!tlvs.isEmpty()) {
674 for (HAProxyTLV tlv: tlvs) {
675 sb.append(tlv).append(", ");
676 }
677 sb.setLength(sb.length() - 2);
678 }
679 sb.append("])");
680 return sb.toString();
681 }
682 }