diff --git a/include/haproxy/ssl_sock.h b/include/haproxy/ssl_sock.h index 3d0eee9b1..cd8d3b6a7 100644 --- a/include/haproxy/ssl_sock.h +++ b/include/haproxy/ssl_sock.h @@ -60,6 +60,8 @@ void ssl_sock_destroy_bind_conf(struct bind_conf *bind_conf); int ssl_sock_prepare_srv_ctx(struct server *srv); void ssl_sock_free_srv_ctx(struct server *srv); void ssl_sock_free_all_ctx(struct bind_conf *bind_conf); +int ssl_sock_get_alpn(const struct connection *conn, void *xprt_ctx, + const char **str, int *len); int ssl_sock_load_ca(struct bind_conf *bind_conf); void ssl_sock_free_ca(struct bind_conf *bind_conf); int ssl_bio_and_sess_init(struct connection *conn, SSL_CTX *ssl_ctx, diff --git a/src/mux_quic.c b/src/mux_quic.c index d870a68e2..b15f1f7f3 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -502,7 +502,8 @@ void quic_mux_transport_params_update(struct qcc *qcc) /* Now that we have all the flow control information, we can finalize the application * context. */ - qcc->app_ops->finalize(qcc->ctx); + if (qcc->app_ops) + qcc->app_ops->finalize(qcc->ctx); } /* Initialize the mux once it's attached. For outgoing connections, the context @@ -570,9 +571,7 @@ static int qc_init(struct connection *conn, struct proxy *prx, qcc->conn->qc->qcc = qcc; /* Application layer initialization. */ - qcc->app_ops = &h3_ops; - if (!qcc->app_ops->init(qcc)) - goto fail; + qcc->app_ops = NULL; /* The transports parameters which control the data sent have been stored * in ->tx.params. The ones which control the received data are stored in diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 8b81ef8a4..d53a3873f 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -6663,7 +6663,7 @@ unsigned int ssl_sock_get_verify_result(struct connection *conn) * freed by the caller. NPN is also checked if available since older versions * of openssl (1.0.1) which are more common in field only support this one. */ -static int ssl_sock_get_alpn(const struct connection *conn, void *xprt_ctx, const char **str, int *len) +int ssl_sock_get_alpn(const struct connection *conn, void *xprt_ctx, const char **str, int *len) { #if defined(TLSEXT_TYPE_application_layer_protocol_negotiation) || \ defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG) diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 0fdc7aa82..d77540a7b 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -1590,6 +1590,9 @@ static forceinline void qc_ssl_dump_errors(struct connection *conn) } } +int ssl_sock_get_alpn(const struct connection *conn, void *xprt_ctx, + const char **str, int *len); + /* Provide CRYPTO data to the TLS stack found at with as length * from encryption level with as QUIC connection context. * Remaining parameter are there for debugging purposes. @@ -1603,6 +1606,8 @@ static inline int qc_provide_cdata(struct quic_enc_level *el, { int ssl_err, state; struct quic_conn *qc; + const char *alpn; + int alpn_len; TRACE_ENTER(QUIC_EV_CONN_SSLDATA, ctx->conn); ssl_err = SSL_ERROR_NONE; @@ -1659,6 +1664,19 @@ static inline int qc_provide_cdata(struct quic_enc_level *el, QUIC_EV_CONN_HDSHK, ctx->conn, &state); } + conn_get_alpn(ctx->conn, &alpn, &alpn_len); + if (alpn_len >= 2 && memcmp(alpn, "h3", 2) == 0) { + qc->qcc->app_ops = &h3_ops; + if (!qc->qcc->app_ops->init(qc->qcc)) + goto err; + } + else { + /* TODO RFC9001 8.1. Protocol Negotiation + * must return no_application_protocol TLS alert + */ + ABORT_NOW(); + } + out: TRACE_LEAVE(QUIC_EV_CONN_SSLDATA, ctx->conn); return 1; @@ -4717,6 +4735,7 @@ static struct xprt_ops ssl_quic = { .start = qc_xprt_start, .prepare_bind_conf = ssl_sock_prepare_bind_conf, .destroy_bind_conf = ssl_sock_destroy_bind_conf, + .get_alpn = ssl_sock_get_alpn, .name = "QUIC", };