From 2454bda1405da260021547d1c9edd676b4219a22 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 18 Oct 2021 14:32:36 +0200 Subject: [PATCH] MINOR: connection: implement function to update ALPN Implement a new function to update the ALPN on an existing connection. on an existing connection. The ALPN from the ssl context can be checked to update the ALPN only if it is a subset of the context value. This method will be useful to change a connection ALPN for websocket, must notably if the server does not support h2 websocket through the rfc8441 Extended Connect. --- include/haproxy/connection.h | 2 ++ src/connection.c | 68 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h index bacf7ca440..a4bef3df73 100644 --- a/include/haproxy/connection.h +++ b/include/haproxy/connection.h @@ -557,6 +557,8 @@ static inline int conn_install_mux(struct connection *conn, const struct mux_ops return ret; } +int conn_update_alpn(struct connection *conn, const struct ist alpn, int force); + static inline const char *conn_get_ctrl_name(const struct connection *conn) { if (!conn || !conn_ctrl_ready(conn)) diff --git a/src/connection.c b/src/connection.c index dfd3299f47..a1472f6812 100644 --- a/src/connection.c +++ b/src/connection.c @@ -328,6 +328,74 @@ int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *ses return conn_install_mux(conn, mux_ops, ctx, prx, sess); } +/* Set the ALPN of connection to . If force is false, must + * be a subset or identical to the registered protos for the parent SSL_CTX. + * In this case must be a single protocol value, not a list. + * + * Returns 0 if ALPN is updated else -1. + */ +int conn_update_alpn(struct connection *conn, const struct ist alpn, int force) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + size_t alpn_len = istlen(alpn); + char *ctx_alpn_str = NULL; + int ctx_alpn_len = 0, found = 0; + + /* if not force, first search if alpn is a subset or identical to the + * parent SSL_CTX. + */ + if (!force) { + /* retrieve the SSL_CTX according to the connection side. */ + if (conn_is_back(conn)) { + if (obj_type(conn->target) == OBJ_TYPE_SERVER) { + struct server *srv = __objt_server(conn->target); + ctx_alpn_str = srv->ssl_ctx.alpn_str; + ctx_alpn_len = srv->ssl_ctx.alpn_len; + } + } + else { + struct session *sess = conn->owner; + struct listener *li = sess->listener; + + if (li->bind_conf && li->bind_conf->is_ssl) { + ctx_alpn_str = li->bind_conf->ssl_conf.alpn_str; + ctx_alpn_len = li->bind_conf->ssl_conf.alpn_len; + } + } + + if (ctx_alpn_str) { + /* search if ALPN is present in SSL_CTX ALPN before + * using it. + */ + while (ctx_alpn_len) { + /* skip ALPN whose size is not 8 */ + if (*ctx_alpn_str != alpn_len - 1) { + ctx_alpn_len -= *ctx_alpn_str + 1; + } + else { + if (isteqi(ist2(ctx_alpn_str, alpn_len), alpn)) { + found = 1; + break; + } + } + ctx_alpn_str += *ctx_alpn_str + 1; + + /* This indicates an invalid ALPN formatted + * string and should never happen. */ + BUG_ON(ctx_alpn_len < 0); + } + } + } + + if (found || force) { + ssl_sock_set_alpn(conn, (const uchar *)istptr(alpn), istlen(alpn)); + return 0; + } + +#endif + return -1; +} + /* Initializes all required fields for a new connection. Note that it does the * minimum acceptable initialization for a connection that already exists and * is about to be reused. It also leaves the addresses untouched, which makes -- 2.47.3