From bf1e8cdbf47f61627a47a7e02e55d107c93288d2 Mon Sep 17 00:00:00 2001 From: Guancheng Wang Date: Sat, 26 Sep 2026 11:20:25 +0800 Subject: [PATCH 1/4] tlshd: fix printf format for the certificate version gnutls_x509_crt_get_version() returns an int, so print it with %d instead of %u. Fixes #59 Signed-off-by: Guancheng Wang <271496918+warter666@users.noreply.github.com> --- src/tlshd/tags.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlshd/tags.c b/src/tlshd/tags.c index 1a83c7c..0491ca6 100644 --- a/src/tlshd/tags.c +++ b/src/tlshd/tags.c @@ -1433,7 +1433,7 @@ tlshd_tags_filter_type_match_x509_tbs_version(struct tlshd_tags_filter *filter, goto deinit; } - snprintf(version, sizeof(version), "%u", ret); + snprintf(version, sizeof(version), "%d", ret); res = tlshd_tags_filter_type_match_string(filter, version); tlshd_log_debug("Filter '%s' %s version '%s'", filter->fi_name, From bd55ae722217bed58f9214183972ba2d0be3ad48 Mon Sep 17 00:00:00 2001 From: Guancheng Wang Date: Sat, 26 Sep 2026 11:20:28 +0800 Subject: [PATCH 2/4] tlshd: use strtok_r() instead of strtok() tlshd forks one child process per handshake and does not link pthreads, so strtok()'s static state is never shared between threads. Prefer strtok_r() anyway to avoid hidden global state in a parser. Fixes #57 Signed-off-by: Guancheng Wang <271496918+warter666@users.noreply.github.com> --- src/tlshd/quic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tlshd/quic.c b/src/tlshd/quic.c index 55c36fa..38e77eb 100644 --- a/src/tlshd/quic.c +++ b/src/tlshd/quic.c @@ -360,7 +360,8 @@ static int quic_session_set_priority(gnutls_session_t session, uint32_t cipher) static int quic_session_set_alpns(gnutls_session_t session, char *alpn_data) { gnutls_datum_t alpns[TLSHD_QUIC_MAX_ALPNS_LEN / 2]; - char *alpn = strtok(alpn_data, ","); + char *saveptr = NULL; + char *alpn = strtok_r(alpn_data, ",", &saveptr); int count = 0, ret; while (alpn) { @@ -369,7 +370,7 @@ static int quic_session_set_alpns(gnutls_session_t session, char *alpn_data) alpns[count].data = (unsigned char *)alpn; alpns[count].size = strlen(alpn); count++; - alpn = strtok(NULL, ","); + alpn = strtok_r(NULL, ",", &saveptr); } ret = gnutls_alpn_set_protocols(session, alpns, count, GNUTLS_ALPN_MANDATORY); From 3885de2f7c631dbe0a7d3b2c22737a2f3c4bb605 Mon Sep 17 00:00:00 2001 From: Guancheng Wang Date: Mon, 28 Sep 2026 14:25:16 +0800 Subject: [PATCH 3/4] tlshd: NUL-terminate the ALPN string returned by the kernel The kernel's QUIC_SOCKOPT_ALPN getter returns the comma-joined ALPN string without a NUL terminator, and may return up to QUIC_ALPN_MAX_LEN (128) bytes. conn->alpns was exactly that size, so a full-length value left the buffer unterminated and quic_session_set_alpns()'s strtok_r()/strlen() ran off the end of alpns[] into conn->ticket[]. Make alpns one byte larger than the kernel maximum and write a NUL at the length getsockopt() returns. Fixes #58 Signed-off-by: Guancheng Wang <271496918+warter666@users.noreply.github.com> --- src/tlshd/quic.c | 1 + src/tlshd/tlshd.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tlshd/quic.c b/src/tlshd/quic.c index 38e77eb..92d355a 100644 --- a/src/tlshd/quic.c +++ b/src/tlshd/quic.c @@ -422,6 +422,7 @@ static int quic_conn_get_config(struct tlshd_quic_conn *conn) tlshd_log_error("socket getsockopt alpn error %d", errno); return -1; } + conn->alpns[len] = '\0'; len = sizeof(conn->ticket); if (getsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_SESSION_TICKET, conn->ticket, &len)) { tlshd_log_error("socket getsockopt session ticket error %d", errno); diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h index b7d3e8a..bc86bce 100644 --- a/src/tlshd/tlshd.h +++ b/src/tlshd/tlshd.h @@ -219,7 +219,8 @@ struct tlshd_quic_msg { */ struct tlshd_quic_conn { struct tlshd_handshake_parms *parms; - char alpns[TLSHD_QUIC_MAX_ALPNS_LEN]; + /* one byte larger than the kernel maximum, to hold the NUL */ + char alpns[TLSHD_QUIC_MAX_ALPNS_LEN + 1]; uint8_t ticket[TLSHD_QUIC_MAX_DATA_LEN]; uint32_t ticket_len; uint32_t cipher; From 1028a0ff5a4ed622287be95cf1f3c05b1bab4b80 Mon Sep 17 00:00:00 2001 From: Guancheng Wang Date: Mon, 28 Sep 2026 14:25:19 +0800 Subject: [PATCH 4/4] tlshd: skip empty ALPN tokens An interior empty token such as "h3,,x" survives the kernel's parser and arrives as an empty string, which then goes to GnuTLS as a zero-length ALPN. RFC 7301 requires 1 to 255 octets, so skip empty tokens after the space-strip. Signed-off-by: Guancheng Wang <271496918+warter666@users.noreply.github.com> --- src/tlshd/quic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tlshd/quic.c b/src/tlshd/quic.c index 92d355a..76e9202 100644 --- a/src/tlshd/quic.c +++ b/src/tlshd/quic.c @@ -367,6 +367,10 @@ static int quic_session_set_alpns(gnutls_session_t session, char *alpn_data) while (alpn) { while (*alpn == ' ') alpn++; + if (!*alpn) { + alpn = strtok_r(NULL, ",", &saveptr); + continue; + } alpns[count].data = (unsigned char *)alpn; alpns[count].size = strlen(alpn); count++;