BUG/MINOR: connection: parse PROXY TLV for LOCAL mode

conn_recv_proxy() is responsible to parse PROXY protocol header. For v2
of the protocol, TLVs parsing is implemented. However, this step was
only done inside 'PROXY' command label. TLVs were never extracted for
'LOCAL' command mode.

Fix this by extracting TLV parsing loop outside of the switch case. Of
notable importance, tlv_offset is updated on LOCAL label to point to
first TLV location.

This bug should be backported up to 2.9 at least. It should even
probably be backported to every stable versions. Note however that this
code has changed much over time. It may be useful to use option
'--ignore-all-space' to have a clearer overview of the git diff.

(cherry picked from commit 8b72270e95c965a9672457c3af0de3093c06ba61)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 6c32fe36e9f833ac334c421759cecc580931cec4)
 [ad: context adjustment]
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 5c6f20b0d7ad6e24e459555a4ee1b4e1e9c4502d)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
(cherry picked from commit 76bc84c2c4d3c246defef94768709cba3dab7e40)
 [ad: context adjustment]
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
diff --git a/src/connection.c b/src/connection.c
index 9e710c3..d7614ba 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -515,98 +515,100 @@
 			break;
 		}
 
-		/* TLV parsing */
-		while (tlv_offset < total_v2_len) {
-			struct tlv *tlv_packet;
-			struct ist tlv;
+		/* unsupported protocol, keep local connection address */
+		break;
+	case 0x00: /* LOCAL command */
+		/* keep local connection address for LOCAL */
 
-			/* Verify that we have at least TLV_HEADER_SIZE bytes left */
-			if (tlv_offset + TLV_HEADER_SIZE > total_v2_len)
-				goto bad_header;
+		tlv_offset = PP2_HEADER_LEN;
+		break;
+	default:
+		goto bad_header; /* not a supported command */
+	}
 
-			tlv_packet = (struct tlv *) &trash.area[tlv_offset];
-			tlv = ist2((const char *)tlv_packet->value, get_tlv_length(tlv_packet));
-			tlv_offset += istlen(tlv) + TLV_HEADER_SIZE;
+	/* TLV parsing */
+	while (tlv_offset < total_v2_len) {
+		struct tlv *tlv_packet;
+		struct ist tlv;
 
-			/* Verify that the TLV length does not exceed the total PROXYv2 length */
-			if (tlv_offset > total_v2_len)
-				goto bad_header;
+		/* Verify that we have at least TLV_HEADER_SIZE bytes left */
+		if (tlv_offset + TLV_HEADER_SIZE > total_v2_len)
+			goto bad_header;
 
-			switch (tlv_packet->type) {
-			case PP2_TYPE_CRC32C: {
-				uint32_t n_crc32c;
+		tlv_packet = (struct tlv *) &trash.area[tlv_offset];
+		tlv = ist2((const char *)tlv_packet->value, get_tlv_length(tlv_packet));
+		tlv_offset += istlen(tlv) + TLV_HEADER_SIZE;
 
-				/* Verify that this TLV is exactly 4 bytes long */
-				if (istlen(tlv) != 4)
-					goto bad_header;
+		/* Verify that the TLV length does not exceed the total PROXYv2 length */
+		if (tlv_offset > total_v2_len)
+			goto bad_header;
 
-				n_crc32c = read_n32(istptr(tlv));
-				write_n32(istptr(tlv), 0); // compute with CRC==0
+		switch (tlv_packet->type) {
+		case PP2_TYPE_CRC32C: {
+			uint32_t n_crc32c;
 
-				if (hash_crc32c(trash.area, total_v2_len) != n_crc32c)
-					goto bad_header;
-				break;
-			}
+			/* Verify that this TLV is exactly 4 bytes long */
+			if (istlen(tlv) != 4)
+				goto bad_header;
+
+			n_crc32c = read_n32(istptr(tlv));
+			write_n32(istptr(tlv), 0); // compute with CRC==0
+
+			if (hash_crc32c(trash.area, total_v2_len) != n_crc32c)
+				goto bad_header;
+			break;
+		}
 #ifdef USE_NS
-			case PP2_TYPE_NETNS: {
-				const struct netns_entry *ns;
+		case PP2_TYPE_NETNS: {
+			const struct netns_entry *ns;
 
-				ns = netns_store_lookup(istptr(tlv), istlen(tlv));
-				if (ns)
-					conn->proxy_netns = ns;
-				break;
-			}
+			ns = netns_store_lookup(istptr(tlv), istlen(tlv));
+			if (ns)
+				conn->proxy_netns = ns;
+			break;
+		}
 #endif
-			case PP2_TYPE_AUTHORITY: {
-				if (istlen(tlv) > PP2_AUTHORITY_MAX)
-					goto bad_header;
-				conn->proxy_authority = ist2(pool_alloc(pool_head_authority), 0);
-				if (!isttest(conn->proxy_authority))
-					goto fail;
-				if (istcpy(&conn->proxy_authority, tlv, PP2_AUTHORITY_MAX) < 0) {
-					/* This is technically unreachable, because we verified above
-					 * that the TLV value fits.
-					 */
-					goto fail;
-				}
-				break;
-			}
-			case PP2_TYPE_UNIQUE_ID: {
-				if (istlen(tlv) > UNIQUEID_LEN)
-					goto bad_header;
-				conn->proxy_unique_id = ist2(pool_alloc(pool_head_uniqueid), 0);
-				if (!isttest(conn->proxy_unique_id))
-					goto fail;
-				if (istcpy(&conn->proxy_unique_id, tlv, UNIQUEID_LEN) < 0) {
-					/* This is technically unreachable, because we verified above
-					 * that the TLV value fits.
-					 */
-					goto fail;
-				}
-				break;
+		case PP2_TYPE_AUTHORITY: {
+			if (istlen(tlv) > PP2_AUTHORITY_MAX)
+				goto bad_header;
+			conn->proxy_authority = ist2(pool_alloc(pool_head_authority), 0);
+			if (!isttest(conn->proxy_authority))
+				goto fail;
+			if (istcpy(&conn->proxy_authority, tlv, PP2_AUTHORITY_MAX) < 0) {
+				/* This is technically unreachable, because we verified above
+				 * that the TLV value fits.
+				 */
+				goto fail;
 			}
-			default:
-				break;
+			break;
+		}
+		case PP2_TYPE_UNIQUE_ID: {
+			if (istlen(tlv) > UNIQUEID_LEN)
+				goto bad_header;
+			conn->proxy_unique_id = ist2(pool_alloc(pool_head_uniqueid), 0);
+			if (!isttest(conn->proxy_unique_id))
+				goto fail;
+			if (istcpy(&conn->proxy_unique_id, tlv, UNIQUEID_LEN) < 0) {
+				/* This is technically unreachable, because we verified above
+				 * that the TLV value fits.
+				 */
+				goto fail;
 			}
+			break;
 		}
-
-		/* Verify that the PROXYv2 header ends at a TLV boundary.
-		 * This is technically unreachable, because the TLV parsing already
-		 * verifies that a TLV does not exceed the total length and also
-		 * that there is space for a TLV header.
-		 */
-		if (tlv_offset != total_v2_len)
-			goto bad_header;
-
-		/* unsupported protocol, keep local connection address */
-		break;
-	case 0x00: /* LOCAL command */
-		/* keep local connection address for LOCAL */
-		break;
-	default:
-		goto bad_header; /* not a supported command */
+		default:
+			break;
+		}
 	}
 
+	/* Verify that the PROXYv2 header ends at a TLV boundary.
+	 * This is technically unreachable, because the TLV parsing already
+	 * verifies that a TLV does not exceed the total length and also
+	 * that there is space for a TLV header.
+	 */
+	if (tlv_offset != total_v2_len)
+		goto bad_header;
+
 	trash.data = total_v2_len;
 	goto eat_header;