From 1f0d47dee797df8d437b4151aaa39fcfae73b513 Mon Sep 17 00:00:00 2001 From: Georg Held Date: Wed, 28 Sep 2016 23:04:07 +0200 Subject: [PATCH] Added PROXY Protocol TLV support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: The current PROXY protocol implementation does not have support for optional Type-Length-Value fields. This pull requests adds the TLV values as specified in the PROXY protocol specification (http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt) and adds support for arbitrary TLVs. Modifications: The existing HAProxyMessage implements an additional TLV reading operation. A small bug in the AF_UNIX reader which didn’t set the reader index correctly was also fixed. Result: The PROXY protocol supports TLVs --- .../handler/codec/haproxy/HAProxyMessage.java | 93 ++++- .../handler/codec/haproxy/HAProxySSLTLV.java | 86 +++++ .../handler/codec/haproxy/HAProxyTLV.java | 151 ++++++++ .../haproxy/HAProxyMessageDecoderTest.java | 329 +++++++++++------- .../codec/haproxy/HAProxySSLTLVTest.java | 67 ++++ 5 files changed, 594 insertions(+), 132 deletions(-) create mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java create mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java create mode 100644 codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java index 43ea8da686..b40bf42df2 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java @@ -21,6 +21,10 @@ import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Message container for decoded HAProxy proxy protocol parameters */ @@ -54,6 +58,7 @@ public final class HAProxyMessage { private final String destinationAddress; private final int sourcePort; private final int destinationPort; + private final List tlvs; /** * Creates a new instance @@ -73,6 +78,18 @@ public final class HAProxyMessage { HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) { + this(protocolVersion, command, proxiedProtocol, + sourceAddress, destinationAddress, sourcePort, destinationPort, Collections.emptyList()); + } + + /** + * Creates a new instance + */ + private HAProxyMessage( + HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, + String sourceAddress, String destinationAddress, int sourcePort, int destinationPort, + List tlvs) { + if (proxiedProtocol == null) { throw new NullPointerException("proxiedProtocol"); } @@ -90,6 +107,7 @@ public final class HAProxyMessage { this.destinationAddress = destinationAddress; this.sourcePort = sourcePort; this.destinationPort = destinationPort; + this.tlvs = Collections.unmodifiableList(tlvs); } /** @@ -214,7 +232,71 @@ public final class HAProxyMessage { dstPort = header.readUnsignedShort(); } - return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort); + final List tlvs = readTlvs(header); + + return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort, tlvs); + } + + private static List readTlvs(final ByteBuf header) { + HAProxyTLV haProxyTLV = readNextTLV(header); + if (haProxyTLV == null) { + return Collections.emptyList(); + } + // In most cases there are less than 4 TLVs available + List haProxyTLVs = new ArrayList(4); + + do { + haProxyTLVs.add(haProxyTLV); + if (haProxyTLV instanceof HAProxySSLTLV) { + haProxyTLVs.addAll(((HAProxySSLTLV) haProxyTLV).encapsulatedTLVs()); + } + } while ((haProxyTLV = readNextTLV(header)) != null); + return haProxyTLVs; + } + + private static HAProxyTLV readNextTLV(final ByteBuf header) { + + // We need at least 4 bytes for a TLV + if (header.readableBytes() < 4) { + return null; + } + + final byte typeAsByte = header.readByte(); + final HAProxyTLV.Type type = HAProxyTLV.Type.typeForByteValue(typeAsByte); + + final int length = header.readUnsignedShort(); + switch (type) { + case PP2_TYPE_SSL: + final ByteBuf rawContent = header.retainedSlice(header.readerIndex(), length); + final ByteBuf byteBuf = header.readSlice(length); + final byte client = byteBuf.readByte(); + final int verify = byteBuf.readInt(); + + if (byteBuf.readableBytes() >= 4) { + + final List encapsulatedTlvs = new ArrayList(4); + do { + final HAProxyTLV haProxyTLV = readNextTLV(byteBuf); + if (haProxyTLV == null) { + break; + } + encapsulatedTlvs.add(haProxyTLV); + } while (byteBuf.readableBytes() >= 4); + + return new HAProxySSLTLV(verify, client, encapsulatedTlvs, rawContent); + } + return new HAProxySSLTLV(verify, client, Collections.emptyList(), rawContent); + // If we're not dealing with a SSL Type, we can use the same mechanism + case PP2_TYPE_ALPN: + case PP2_TYPE_AUTHORITY: + case PP2_TYPE_SSL_VERSION: + case PP2_TYPE_SSL_CN: + case PP2_TYPE_NETNS: + case OTHER: + return new HAProxyTLV(type, typeAsByte, header.readRetainedSlice(length)); + default: + return null; + } } /** @@ -428,4 +510,13 @@ public final class HAProxyMessage { public int destinationPort() { return destinationPort; } + + /** + * Returns a list of {@link HAProxyTLV} or an empty list if no TLVs are present. + *

+ * TLVs are only available for the Proxy Protocol V2 + */ + public List tlvs() { + return tlvs; + } } diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java new file mode 100644 index 0000000000..5d3dc10367 --- /dev/null +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.handler.codec.haproxy; + +import io.netty.buffer.ByteBuf; + +import java.util.Collections; +import java.util.List; + +/** + * Represents a {@link HAProxyTLV} of the type {@link HAProxyTLV.Type#PP2_TYPE_SSL}. + * This TLV encapsulates other TLVs and has additional information like verification information and a client bitfield. + */ +public final class HAProxySSLTLV extends HAProxyTLV { + + private final int verify; + private final List tlvs; + private final byte clientBitField; + + /** + * Creates a new HAProxySSLTLV + * + * @param verify the verification result as defined in the specification for the pp2_tlv_ssl struct (see + * http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt) + * @param clientBitField the bitfield with client information + * @param tlvs the encapsulated {@link HAProxyTLV}s + * @param rawContent the raw TLV content + */ + HAProxySSLTLV(final int verify, final byte clientBitField, final List tlvs, final ByteBuf rawContent) { + super(Type.PP2_TYPE_SSL, (byte) 0x20, rawContent); + + this.verify = verify; + this.tlvs = Collections.unmodifiableList(tlvs); + this.clientBitField = clientBitField; + } + + /** + * Returns {@code true} if the bit field for PP2_CLIENT_CERT_CONN was set + */ + public boolean isPP2ClientCertConn() { + return (clientBitField & 0x2) != 0; + } + + /** + * Returns {@code true} if the bit field for PP2_CLIENT_SSL was set + */ + public boolean isPP2ClientSSL() { + return (clientBitField & 0x1) != 0; + } + + /** + * Returns {@code true} if the bit field for PP2_CLIENT_CERT_SESS was set + */ + public boolean isPP2ClientCertSess() { + return (clientBitField & 0x4) != 0; + } + + /** + * Returns the verification result + */ + public int verify() { + return verify; + } + + /** + * Returns an unmodifiable Set of encapsulated {@link HAProxyTLV}s. + */ + public List encapsulatedTLVs() { + return tlvs; + } + +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java new file mode 100644 index 0000000000..7fbd787888 --- /dev/null +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java @@ -0,0 +1,151 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.handler.codec.haproxy; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.DefaultByteBufHolder; + +import static io.netty.util.internal.ObjectUtil.*; + +/** + * A Type-Length Value (TLV vector) that can be added to the PROXY protocol + * to include additional information like SSL information. + * + * @see HAProxySSLTLV + */ +public class HAProxyTLV extends DefaultByteBufHolder { + + private final Type type; + private final byte typeByteValue; + + /** + * The registered types a TLV can have regarding the PROXY protocol 1.5 spec + */ + public enum Type { + PP2_TYPE_ALPN, + PP2_TYPE_AUTHORITY, + PP2_TYPE_SSL, + PP2_TYPE_SSL_VERSION, + PP2_TYPE_SSL_CN, + PP2_TYPE_NETNS, + /** + * A TLV type that is not officially defined in the spec. May be used for nonstandard TLVs + */ + OTHER; + + /** + * Returns the the {@link Type} for a specific byte value as defined in the PROXY protocol 1.5 spec + *

+ * If the byte value is not an official one, it will return {@link Type#OTHER}. + * + * @param byteValue the byte for a type + * + * @return the {@link Type} of a TLV + */ + public static Type typeForByteValue(final byte byteValue) { + switch (byteValue) { + case 0x01: + return PP2_TYPE_ALPN; + case 0x02: + return PP2_TYPE_AUTHORITY; + case 0x20: + return PP2_TYPE_SSL; + case 0x21: + return PP2_TYPE_SSL_VERSION; + case 0x22: + return PP2_TYPE_SSL_CN; + case 0x30: + return PP2_TYPE_NETNS; + default: + return OTHER; + } + } + } + + /** + * Creates a new HAProxyTLV + * + * @param type the {@link Type} of the TLV + * @param typeByteValue the byteValue of the TLV. This is especially important if non-standard TLVs are used + * @param content the raw content of the TLV + */ + HAProxyTLV(final Type type, final byte typeByteValue, final ByteBuf content) { + super(content); + checkNotNull(type, "type"); + + this.type = type; + this.typeByteValue = typeByteValue; + } + + /** + * Returns the {@link Type} of this TLV + */ + public Type type() { + return type; + } + + /** + * Returns the type of the TLV as byte + */ + public byte typeByteValue() { + return typeByteValue; + } + + @Override + public HAProxyTLV copy() { + return replace(content().copy()); + } + + @Override + public HAProxyTLV duplicate() { + return replace(content().duplicate()); + } + + @Override + public HAProxyTLV retainedDuplicate() { + return replace(content().retainedDuplicate()); + } + + @Override + public HAProxyTLV replace(ByteBuf content) { + return new HAProxyTLV(type, typeByteValue, content); + } + + @Override + public HAProxyTLV retain() { + super.retain(); + return this; + } + + @Override + public HAProxyTLV retain(int increment) { + super.retain(increment); + return this; + } + + @Override + public HAProxyTLV touch() { + super.touch(); + return this; + } + + @Override + public HAProxyTLV touch(Object hint) { + super.touch(hint); + return this; + } +} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java index ae68414665..2d4039de3d 100644 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java +++ b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java @@ -26,6 +26,8 @@ import io.netty.util.CharsetUtil; import org.junit.Before; import org.junit.Test; +import java.util.List; + import static io.netty.buffer.Unpooled.*; import static org.junit.Assert.*; @@ -155,7 +157,7 @@ public class HAProxyMessageDecoderTest { @Test(expected = HAProxyProtocolException.class) public void testHeaderTooLong() { String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + - "00000000000000000000000000000000000000000000000000000000000000000443\r\n"; + "00000000000000000000000000000000000000000000000000000000000000000443\r\n"; ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); } @@ -212,16 +214,16 @@ public class HAProxyMessageDecoderTest { @Test public void testV2IPV4Decode() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -267,16 +269,16 @@ public class HAProxyMessageDecoderTest { @Test public void testV2UDPDecode() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -322,16 +324,16 @@ public class HAProxyMessageDecoderTest { @Test public void testv2IPV6Decode() { byte[] header = new byte[52]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -401,16 +403,16 @@ public class HAProxyMessageDecoderTest { @Test public void testv2UnixDecode() { byte[] header = new byte[232]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -479,16 +481,16 @@ public class HAProxyMessageDecoderTest { @Test public void testV2LocalProtocolDecode() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -534,16 +536,16 @@ public class HAProxyMessageDecoderTest { @Test public void testV2UnknownProtocolDecode() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -586,21 +588,86 @@ public class HAProxyMessageDecoderTest { assertFalse(ch.finish()); } + @Test + public void testV2WithSslTLVs() throws Exception { + ch = new EmbeddedChannel(new HAProxyMessageDecoder()); + + final byte[] bytes = { + 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 35, 127, 0, 0, 1, 127, 0, 0, 1, + -55, -90, 7, 89, 32, 0, 20, 5, 0, 0, 0, 0, 33, 0, 5, 84, 76, 83, 118, 49, 34, 0, 4, 76, 69, 65, 70 + }; + + int startChannels = ch.pipeline().names().size(); + assertTrue(ch.writeInbound(copiedBuffer(bytes))); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + HAProxyMessage msg = (HAProxyMessage) msgObj; + + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); + assertEquals("127.0.0.1", msg.sourceAddress()); + assertEquals("127.0.0.1", msg.destinationAddress()); + assertEquals(51622, msg.sourcePort()); + assertEquals(1881, msg.destinationPort()); + final List tlvs = msg.tlvs(); + + assertEquals(3, tlvs.size()); + final HAProxyTLV firstTlv = tlvs.get(0); + assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL, firstTlv.type()); + final HAProxySSLTLV sslTlv = (HAProxySSLTLV) firstTlv; + assertEquals(0, sslTlv.verify()); + assertTrue(sslTlv.isPP2ClientSSL()); + assertTrue(sslTlv.isPP2ClientCertSess()); + assertFalse(sslTlv.isPP2ClientCertConn()); + + final HAProxyTLV secondTlv = tlvs.get(1); + + assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL_VERSION, secondTlv.type()); + ByteBuf secondContentBuf = secondTlv.content(); + byte[] secondContent = new byte[secondContentBuf.readableBytes()]; + secondContentBuf.readBytes(secondContent); + assertArrayEquals("TLSv1".getBytes(CharsetUtil.US_ASCII), secondContent); + + final HAProxyTLV thirdTLV = tlvs.get(2); + assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL_CN, thirdTLV.type()); + ByteBuf thirdContentBuf = thirdTLV.content(); + byte[] thirdContent = new byte[thirdContentBuf.readableBytes()]; + thirdContentBuf.readBytes(thirdContent); + assertArrayEquals("LEAF".getBytes(CharsetUtil.US_ASCII), thirdContent); + + assertTrue(sslTlv.encapsulatedTLVs().contains(secondTlv)); + assertTrue(sslTlv.encapsulatedTLVs().contains(thirdTLV)); + + assertTrue(0 < firstTlv.refCnt()); + assertTrue(0 < secondTlv.refCnt()); + assertTrue(0 < thirdTLV.refCnt()); + assertFalse(thirdTLV.release()); + assertFalse(secondTlv.release()); + assertTrue(firstTlv.release()); + assertEquals(0, firstTlv.refCnt()); + assertEquals(0, secondTlv.refCnt()); + assertEquals(0, thirdTLV.refCnt()); + + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + @Test public void testV2WithTLV() { ch = new EmbeddedChannel(new HAProxyMessageDecoder(4)); byte[] header = new byte[236]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -676,16 +743,16 @@ public class HAProxyMessageDecoderTest { @Test(expected = HAProxyProtocolException.class) public void testV2InvalidProtocol() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -717,16 +784,16 @@ public class HAProxyMessageDecoderTest { @Test(expected = HAProxyProtocolException.class) public void testV2MissingParams() { byte[] header = new byte[26]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -755,16 +822,16 @@ public class HAProxyMessageDecoderTest { @Test(expected = HAProxyProtocolException.class) public void testV2InvalidCommand() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -796,16 +863,16 @@ public class HAProxyMessageDecoderTest { @Test(expected = HAProxyProtocolException.class) public void testV2InvalidVersion() { byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -839,16 +906,16 @@ public class HAProxyMessageDecoderTest { ch = new EmbeddedChannel(new HAProxyMessageDecoder(0)); byte[] header = new byte[248]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- @@ -880,16 +947,16 @@ public class HAProxyMessageDecoderTest { @Test public void testV2IncompleteHeader() { byte[] header = new byte[13]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- header[10] = 0x54; // ----- header[11] = 0x0A; // ----- diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java new file mode 100644 index 0000000000..51cc85d1a7 --- /dev/null +++ b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.handler.codec.haproxy; + +import io.netty.buffer.Unpooled; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.*; + +public class HAProxySSLTLVTest { + + @Test + public void testClientBitmask() throws Exception { + + // 0b0000_0111 + final byte allClientsEnabled = 0x7; + final HAProxySSLTLV allClientsEnabledTLV = + new HAProxySSLTLV(0, allClientsEnabled, Collections.emptyList(), Unpooled.buffer()); + + assertTrue(allClientsEnabledTLV.isPP2ClientCertConn()); + assertTrue(allClientsEnabledTLV.isPP2ClientSSL()); + assertTrue(allClientsEnabledTLV.isPP2ClientCertSess()); + + assertTrue(allClientsEnabledTLV.release()); + + // 0b0000_0101 + final byte clientSSLandClientCertSessEnabled = 0x5; + + final HAProxySSLTLV clientSSLandClientCertSessTLV = + new HAProxySSLTLV(0, clientSSLandClientCertSessEnabled, Collections.emptyList(), + Unpooled.buffer()); + + assertFalse(clientSSLandClientCertSessTLV.isPP2ClientCertConn()); + assertTrue(clientSSLandClientCertSessTLV.isPP2ClientSSL()); + assertTrue(clientSSLandClientCertSessTLV.isPP2ClientCertSess()); + + assertTrue(clientSSLandClientCertSessTLV.release()); + // 0b0000_0000 + final byte noClientEnabled = 0x0; + + final HAProxySSLTLV noClientTlv = + new HAProxySSLTLV(0, noClientEnabled, Collections.emptyList(), + Unpooled.buffer()); + + assertFalse(noClientTlv.isPP2ClientCertConn()); + assertFalse(noClientTlv.isPP2ClientSSL()); + assertFalse(noClientTlv.isPP2ClientCertSess()); + + assertTrue(noClientTlv.release()); + } +}