BUG/MAJOR: proxy_protocol: Properly validate TLV lengths

This patch fixes PROXYv2 parsing when the payload of the TCP connection is
fused with the PROXYv2 header within a single recv() call.

Previously HAProxy ignored the PROXYv2 header length when attempting to
parse the TLV, possibly interpreting the first byte of the payload as a
TLV type.

This patch adds proper validation. It ensures that:

1. TLV parsing stops when the end of the PROXYv2 header is reached.
2. TLV lengths cannot exceed the PROXYv2 header length.
3. The PROXYv2 header ends together with the last TLV, not allowing for
   "stray bytes" to be ignored.

A reg-test was added to ensure proper behavior.

This patch tries to find the sweat spot between a small and easily
backportable one, and a cleaner one that's more easily adaptable to
older versions, hence why it merges the "if" and "while" blocks which
causes a reindent of the whole block. It should be used as-is for
versions 1.9 to 2.1, the block about PP2_TYPE_AUTHORITY should be
dropped for 2.0 and the block about CRC32C should be dropped for 1.8.

This bug was introduced when TLV parsing was added. This happened in commit
b3e54fe387c7c1ea750f39d3029672d640c499f9. This commit was first released
with HAProxy 1.6-dev1.

A similar issue was fixed in commit 7209c204bd6f3c49132264c7a58f689cdc741c12.

This patch must be backported to HAProxy 1.6+.

(cherry picked from commit 488ee7fb6e4a388bb68153341826a6391da794e9)
[wt: the regtest will fail, it requires 2.2]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 6321012472aa910aca7823da45e6eead716f86f5)
[wt: drop the PP2_TYPE_AUTHORITY changes]
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/reg-tests/connection/proxy_protocol_tlv_validation.vtc b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
new file mode 100644
index 0000000..65a251c
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_tlv_validation.vtc
@@ -0,0 +1,138 @@
+varnishtest "Check that the TLVs are properly validated"
+
+feature ignore_unknown_macro
+
+# We need one HAProxy for each test, because apparently the connection by
+# the client is reused, leading to connection resets.
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "12345"
+    sendhex "02 00 05 31 32 33 34 35"
+
+    txreq -url "/"
+    rxresp
+    expect resp.http.echo == "3132333435"
+} -run
+
+haproxy h2 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a TLV after the end of the PROXYv2 header is ignored
+client c2 -connect ${h2_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "12345"
+    sendhex "02 00 05 31 32 33 34 35"
+    # after the end of the PROXYv2 header: PP2_TYPE_AUTHORITY + length of the value + "54321"
+    sendhex "02 00 05 35 34 33 32 31"
+
+    txreq -url "/"
+    rxresp
+    expect resp.http.echo == "3132333435"
+} -run
+
+haproxy h3 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that a TLV length exceeding the PROXYv2 length fails
+client c3 -connect ${h3_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + too small length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "1234512345"
+    sendhex "02 00 0A 31 32 33 34 35 31 32 33 34 35"
+
+    txreq -url "/"
+    expect_close
+} -run
+
+haproxy h4 -conf {
+    defaults
+        mode http
+        timeout connect 1s
+        timeout client  1s
+        timeout server  1s
+
+    frontend a
+        bind "fd@${fe1}" accept-proxy
+        http-after-response set-header echo %[fc_pp_authority,hex]
+        http-request return status 200
+} -start
+
+# Validate that TLVs not ending with the PROXYv2 header fail
+client c4 -connect ${h4_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + too big length of the TLV (8)
+    sendhex "00 14"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+    # PP2_TYPE_AUTHORITY + length of the value + "1234"
+    sendhex "02 00 04 31 32 33 34"
+
+    txreq -url "/"
+    expect_close
+} -run