MINOR: quic: adjust Rx packet type parsing

qc_parse_hd_form() is the function used to parse the first byte of a
packet and return its type and version. Its API has been simplified with
the following changes :
* extra out paremeters are removed (long_header and version). All infos
  are now stored directly in quic_rx_packet instance
* a new dummy version is declared in quic_versions array with a 0 number
  code. This can be used to match Version negotiation packets.
* a new default packet type is defined QUIC_PACKET_TYPE_UNKNOWN to be
  used as an initial value.

Also, the function has been exported to an include file. This will be
useful to be able to reuse on quic-sock to parse the first packet of a
datagram.

This should be backported up to 2.7.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index 1efb8f9..5e6b5d6 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -131,6 +131,9 @@
 	 * own convenience.
 	 */
 	QUIC_PACKET_TYPE_SHORT,
+
+	/* unknown type */
+	QUIC_PACKET_TYPE_UNKNOWN
 };
 
 /* Packet number field length. */
diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h
index ade8a81..78a9755 100644
--- a/include/haproxy/quic_conn.h
+++ b/include/haproxy/quic_conn.h
@@ -687,6 +687,8 @@
 
 void qc_kill_conn(struct quic_conn *qc);
 
+int qc_parse_hd_form(struct quic_rx_packet *pkt,
+                     unsigned char **buf, const unsigned char *end);
 int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *qc,
                      struct listener *li);
 
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 1796227..788ba18 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -119,6 +119,11 @@
 const size_t quic_versions_nb = sizeof quic_versions / sizeof *quic_versions;
 /* Listener only preferred version */
 const struct quic_version *preferred_version;
+/* RFC 8999 5.4. Version
+ * A Version field with a
+ * value of 0x00000000 is reserved for version negotiation
+ */
+const struct quic_version quic_version_VN_reserved = { .num = 0, };
 
 /* trace source and events */
 static void quic_trace(enum trace_level level, uint64_t mask, \
@@ -5884,7 +5889,8 @@
 		/* Check that the length of this received DCID matches the CID lengths
 		 * of our implementation for non Initials packets only.
 		 */
-		if (pkt->type != QUIC_PACKET_TYPE_INITIAL &&
+		if (pkt->version && pkt->version->num &&
+		    pkt->type != QUIC_PACKET_TYPE_INITIAL &&
 		    pkt->type != QUIC_PACKET_TYPE_0RTT &&
 		    dcid_len != QUIC_HAP_CID_LEN) {
 			TRACE_ERROR("wrong DCID length", QUIC_EV_CONN_RXPKT);
@@ -6015,48 +6021,72 @@
 	return ret;
 }
 
+/* Return the QUIC version (quic_version struct) with <version> as version number
+ * if supported or NULL if not.
+ */
+static inline const struct quic_version *qc_supported_version(uint32_t version)
+{
+	int i;
+
+	if (unlikely(!version))
+		return &quic_version_VN_reserved;
+
+	for (i = 0; i < quic_versions_nb; i++)
+		if (quic_versions[i].num == version)
+			return &quic_versions[i];
+
+	return NULL;
+}
+
-/* Parse the header form from <byte0> first byte of <pkt> packet to set its type.
- * Also set <*long_header> to 1 if this form is long, 0 if not and the version
- * of this packet into <*version>.
+/* Parse a QUIC packet header starting at <buf> without exceeding <end>.
+ * Version and type are stored in <pkt> packet instance. Type is set to unknown
+ * on two occasions : for unsupported version, in this case version field is
+ * set to NULL; for Version Negotiation packet with version number set to 0.
+ *
+ * Returns 1 on success else 0.
  */
-static inline int qc_parse_hd_form(struct quic_rx_packet *pkt,
-                                   unsigned char **buf, const unsigned char *end,
-                                   int *long_header, uint32_t *version)
+int qc_parse_hd_form(struct quic_rx_packet *pkt,
+                     unsigned char **buf, const unsigned char *end)
 {
+	uint32_t version;
 	int ret = 0;
 	const unsigned char byte0 = **buf;
 
 	TRACE_ENTER(QUIC_EV_CONN_RXPKT);
+	pkt->version = NULL;
+	pkt->type = QUIC_PACKET_TYPE_UNKNOWN;
 
 	(*buf)++;
 	if (byte0 & QUIC_PACKET_LONG_HEADER_BIT) {
 		unsigned char type =
 			(byte0 >> QUIC_PACKET_TYPE_SHIFT) & QUIC_PACKET_TYPE_BITMASK;
 
-		*long_header = 1;
 		/* Version */
-		if (!quic_read_uint32(version, (const unsigned char **)buf, end)) {
+		if (!quic_read_uint32(&version, (const unsigned char **)buf, end)) {
 			TRACE_ERROR("could not read the packet version", QUIC_EV_CONN_RXPKT);
 			goto out;
 		}
 
-		if (*version != QUIC_PROTOCOL_VERSION_2) {
-			pkt->type = type;
-		}
-		else {
-			switch (type) {
-			case 0:
-				pkt->type = QUIC_PACKET_TYPE_RETRY;
-				break;
-			case 1:
-				pkt->type = QUIC_PACKET_TYPE_INITIAL;
-				break;
-			case 2:
-				pkt->type = QUIC_PACKET_TYPE_0RTT;
-				break;
-			case 3:
-				pkt->type = QUIC_PACKET_TYPE_HANDSHAKE;
-				break;
+		pkt->version = qc_supported_version(version);
+		if (version && pkt->version) {
+			if (version != QUIC_PROTOCOL_VERSION_2) {
+				pkt->type = type;
+			}
+			else {
+				switch (type) {
+				case 0:
+					pkt->type = QUIC_PACKET_TYPE_RETRY;
+					break;
+				case 1:
+					pkt->type = QUIC_PACKET_TYPE_INITIAL;
+					break;
+				case 2:
+					pkt->type = QUIC_PACKET_TYPE_0RTT;
+					break;
+				case 3:
+					pkt->type = QUIC_PACKET_TYPE_HANDSHAKE;
+					break;
+				}
 			}
 		}
 	}
@@ -6064,7 +6094,6 @@
 		if (byte0 & QUIC_PACKET_SPIN_BIT)
 			pkt->flags |= QUIC_FL_RX_PACKET_SPIN_BIT;
 		pkt->type = QUIC_PACKET_TYPE_SHORT;
-		*long_header = 0;
 	}
 
 	ret = 1;
@@ -6073,20 +6102,6 @@
 	return ret;
 }
 
-/* Return the QUIC version (quic_version struct) with <version> as version number
- * if supported or NULL if not.
- */
-static inline const struct quic_version *qc_supported_version(uint32_t version)
-{
-	int i;
-
-	for (i = 0; i < quic_versions_nb; i++)
-		if (quic_versions[i].num == version)
-			return &quic_versions[i];
-
-	return NULL;
-}
-
 /*
  * Send a Version Negotiation packet on response to <pkt> on socket <fd> to
  * address <addr>.
@@ -6743,8 +6758,6 @@
 	const unsigned char *beg = buf;
 	struct proxy *prx;
 	struct quic_counters *prx_counters;
-	int long_header = 0;
-	uint32_t version = 0;
 	const struct quic_version *qv = NULL;
 
 	TRACE_ENTER(QUIC_EV_CONN_LPKT);
@@ -6778,15 +6791,15 @@
 	}
 
 	/* Header form */
-	if (!qc_parse_hd_form(pkt, &buf, end, &long_header, &version)) {
+	if (!qc_parse_hd_form(pkt, &buf, end)) {
 		TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
 		goto drop;
 	}
 
-	if (long_header) {
+	if (pkt->type != QUIC_PACKET_TYPE_SHORT) {
 		uint64_t len;
-
 		TRACE_PROTO("long header packet received", QUIC_EV_CONN_LPKT);
+
 		if (!quic_packet_read_long_header(&buf, end, pkt)) {
 			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
 			goto drop;
@@ -6803,14 +6816,14 @@
 		}
 
 		/* Retry of Version Negotiation packets are only sent by servers */
-		if (pkt->type == QUIC_PACKET_TYPE_RETRY || !version) {
+		if (pkt->type == QUIC_PACKET_TYPE_RETRY ||
+		    (pkt->version && !pkt->version->num)) {
 			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
 			goto drop;
 		}
 
 		/* RFC9000 6. Version Negotiation */
-		qv = qc_supported_version(version);
-		if (!qv) {
+		if (!pkt->version) {
 			 /* unsupported version, send Negotiation packet */
 			if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
 				TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
@@ -6820,7 +6833,6 @@
 			TRACE_PROTO("VN packet sent", QUIC_EV_CONN_LPKT);
 			goto drop_silent;
 		}
-		pkt->version = qv;
 
 		/* For Initial packets, and for servers (QUIC clients connections),
 		 * there is no Initial connection IDs storage.