# define QUIC_STATELESS_RESET_TOKEN_LEN 16
+/*
+ * An encoded preferred_addr transport parameter cannot be longer than this
+ * number of bytes.
+ */
+# define QUIC_MIN_ENCODED_PREFERRED_ADDR_LEN 41
+# define QUIC_MAX_ENCODED_PREFERRED_ADDR_LEN 61
+
# endif
#endif
uint64_t *id,
QUIC_CONN_ID *cid);
+/*
+ * Decodes a QUIC transport parameter TLV containing a preferred_address.
+ */
+typedef struct quic_preferred_addr_st {
+ uint16_t ipv4_port, ipv6_port;
+ unsigned char ipv4[4], ipv6[16];
+ unsigned char stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN];
+ QUIC_CONN_ID cid;
+} QUIC_PREFERRED_ADDR;
+
+int ossl_quic_wire_decode_transport_param_preferred_addr(PACKET *pkt,
+ QUIC_PREFERRED_ADDR *p);
+
# endif
#endif
break;
case QUIC_TPARAM_PREFERRED_ADDR:
- /* TODO(QUIC): Handle preferred address. */
- if (ch->is_server) {
- reason = TP_REASON_SERVER_ONLY("PREFERRED_ADDR");
- goto malformed;
+ {
+ /* TODO(QUIC): Handle preferred address. */
+ QUIC_PREFERRED_ADDR pfa;
+
+ /*
+ * RFC 9000 s. 18.2: "A server that chooses a zero-length
+ * connection ID MUST NOT provide a preferred address.
+ * Similarly, a server MUST NOT include a zero-length connection
+ * ID in this transport parameter. A client MUST treat a
+ * violation of these requirements as a connection error of type
+ * TRANSPORT_PARAMETER_ERROR."
+ */
+ if (ch->is_server) {
+ reason = TP_REASON_SERVER_ONLY("PREFERRED_ADDR");
+ goto malformed;
+ }
+
+ if (ch->cur_remote_dcid.id_len == 0) {
+ reason = "PREFERRED_ADDR provided for zero-length CID";
+ goto malformed;
+ }
+
+ if (!ossl_quic_wire_decode_transport_param_preferred_addr(&pkt, &pfa)) {
+ reason = TP_REASON_MALFORMED("PREFERRED_ADDR");
+ goto malformed;
+ }
+
+ if (pfa.cid.id_len == 0) {
+ reason = "zero-length CID in PREFERRED_ADDR";
+ goto malformed;
+ }
}
-
- body = ossl_quic_wire_decode_transport_param_bytes(&pkt, &id, &len);
- if (body == NULL) {
- reason = TP_REASON_MALFORMED("PREFERRED_ADDR");
- goto malformed;
- }
-
break;
case QUIC_TPARAM_DISABLE_ACTIVE_MIGRATION:
goto err;
if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_ACTIVE_CONN_ID_LIMIT,
- 2))
+ QUIC_MIN_ACTIVE_CONN_ID_LIMIT))
goto err;
if (!ossl_quic_wire_encode_transport_param_int(&wpkt, QUIC_TPARAM_INITIAL_MAX_DATA,
memcpy(cid->id, body, cid->id_len);
return 1;
}
+
+int ossl_quic_wire_decode_transport_param_preferred_addr(PACKET *pkt,
+ QUIC_PREFERRED_ADDR *p)
+{
+ const unsigned char *body;
+ uint64_t id;
+ size_t len = 0;
+ PACKET pkt2;
+ unsigned int ipv4_port, ipv6_port, cidl;
+
+ body = ossl_quic_wire_decode_transport_param_bytes(pkt, &id, &len);
+ if (body == NULL
+ || len < QUIC_MIN_ENCODED_PREFERRED_ADDR_LEN
+ || len > QUIC_MAX_ENCODED_PREFERRED_ADDR_LEN
+ || id != QUIC_TPARAM_PREFERRED_ADDR)
+ return 0;
+
+ if (!PACKET_buf_init(&pkt2, body, len))
+ return 0;
+
+ if (!PACKET_copy_bytes(&pkt2, p->ipv4, sizeof(p->ipv4))
+ || !PACKET_get_net_2(&pkt2, &ipv4_port)
+ || !PACKET_copy_bytes(&pkt2, p->ipv6, sizeof(p->ipv6))
+ || !PACKET_get_net_2(&pkt2, &ipv6_port)
+ || !PACKET_get_1(&pkt2, &cidl)
+ || cidl > QUIC_MAX_CONN_ID_LEN
+ || !PACKET_copy_bytes(&pkt2, p->cid.id, cidl)
+ || !PACKET_copy_bytes(&pkt2, p->stateless_reset_token,
+ sizeof(p->stateless_reset_token)))
+ return 0;
+
+ p->ipv4_port = (uint16_t)ipv4_port;
+ p->ipv6_port = (uint16_t)ipv6_port;
+ p->cid.id_len = (unsigned char)cidl;
+ return 1;
+}