From 9571a2deecb15e499f1dcec14e8fd598ec817ea8 Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 7 Apr 2026 19:54:09 +0200 Subject: [PATCH 1/4] http: clean up trailing whitespace --- pkg/vere/io/http.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 9ff3fa4e81..5d449c696b 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -114,7 +114,7 @@ typedef struct _u3_httd { SSL_CTX* tls_u; // server SSL_CTX* u3p(u3h_root) sax_p; // url->scry cache u3p(u3h_root) nax_p; // scry->noun cache - u3t_spin* stk_u; // spin stack + u3t_spin* stk_u; // spin stack } u3_httd; static u3_weak _http_rec_to_httq(h2o_req_t* rec_u); @@ -502,7 +502,7 @@ _http_spin_unlink(u3_hreq* req_u) if ( 0 != req_u->nex_u ) { req_u->nex_u->pre_u = 0; - } + } else if (req_u->tim_u != NULL) { uv_timer_stop(req_u->tim_u); } @@ -977,7 +977,7 @@ _http_peek_dispatch(u3_hreq* req_u, beam* bem_u, u3_noun gang, u3_noun spur) u3_noun key = u3nc(auth_o, u3k(bam)); u3_weak nac = u3h_get(htd_u->nax_p, key); u3z(key); - + if ( (u3_none == nac) || ((u3_nul == gang) && (c3y == u3r_at(14, nac))) ) { @@ -1062,7 +1062,7 @@ _http_req_dispatch(u3_hreq* req_u, u3_noun req) u3_noun gang = ( _(_http_req_is_auth(fig_u, rec_u)) ) ? u3nc(u3_nul, u3_nul) : u3_nul; - + h2o_headers_t req_headers = req_u->rec_u->headers; byte_range rng_u; c3_o rng_o = _get_range(req_headers, &rng_u); @@ -1228,7 +1228,7 @@ _http_req_cache(u3_hreq* req_u) u3_noun url = u3dc("scot", 't', _http_vec_to_atom(req_u->rec_u->path)); u3_weak sac = u3h_get(htd_u->sax_p, url); u3z(url); - + if ( u3_none == sac ) { return c3n; } @@ -1450,14 +1450,14 @@ _http_continue_respond(u3_hreq* req_u, u3z(data); u3z(complete); return; } - + u3_hgen* gen_u = req_u->gen_u; - + if ( u3_hgen_done == gen_u->sat_e ) { u3z(data); u3z(complete); return; } - + uv_timer_stop(req_u->tim_u); gen_u->sat_e = ( c3y == complete ) ? u3_hgen_done : u3_hgen_wait; @@ -1584,7 +1584,7 @@ static c3_i _http_spin_accept(h2o_handler_t* han_u, h2o_req_t* rec_u) { u3_hcon* hon_u = _http_rec_sock(rec_u); - + if ( NULL == hon_u->htp_u->htd_u->stk_u ) { u3_hreq* req_u = _http_req_prepare(rec_u, _http_req_new); req_u->sat_e = u3_rsat_plan; @@ -2737,7 +2737,7 @@ _http_io_talk(u3_auto* car_u) //Setup spin stack htd_u->stk_u = u3t_sstack_open(); - + if ( NULL == htd_u->stk_u ) { u3l_log("http.c: failed to open spin stack"); } @@ -2891,7 +2891,7 @@ _http_stream_slog(void* vop_p, c3_w pri_w, u3_noun tan) */ static void _http_spin_timer_cb(uv_timer_t* tim_u) -{ +{ u3_httd* htd_u = tim_u->data; u3_hreq* siq_u = htd_u->fig_u.siq_u; @@ -2942,7 +2942,7 @@ _http_spin_timer_cb(uv_timer_t* tim_u) _http_continue_respond(siq_u, u3k(dat), c3n); siq_u = siq_u->nex_u; } - u3z(dat); u3z(lin); + u3z(dat); u3z(lin); } uv_timer_start(htd_u->fig_u.sin_u, _http_spin_timer_cb, SPIN_TIMER, 0); From 7ae1122060bbfab88eaba0b37991f241c2a684ec Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 7 Apr 2026 19:58:36 +0200 Subject: [PATCH 2/4] http: support multi-cert %set-config gifts Eyre is going to start passing %set-config gifts that contain a list of per-domain SSL certificates in the foreseeable future. Here, we update http.c to handle these and pick the right cert based on the incoming request. The way we do this is by registering a "bootstrap" TLS context on the HTTPS port's server, and giving that an SNI (server name indication) callback. At that point in the connection we get told the hostname by the client, and can go see if there's a matching cert for that hostname. If there is, we swap out the TLS context for one with the matching cert. Otherwise, we continue serving the request with the bootstrap/fallback context. Eyre is expected to provide the domain-cert list in priority order, where at the very least "most generic" certs (commonly, wildcard certs) are at the end of the list. The last cert in the list will be used for bootstrap and fallback. --- pkg/vere/io/http.c | 172 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 152 insertions(+), 20 deletions(-) diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 5d449c696b..26ac34d58e 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -82,14 +82,21 @@ typedef struct _u3_h2o_serv { struct _u3_httd* htd_u; // device backpointer } u3_http; + typedef struct _u3_cert { + u3_noun tuf; // domain (as turf, %$ == *) + uv_buf_t key_u; // PEM RSA private key + uv_buf_t cer_u; // PEM certificate chain + SSL_CTX* ctx_u; // domain-specific SSL context + struct _u3_cert* nex_u; + } u3_cert; + /* u3_form: http config from %eyre */ typedef struct _u3_form { c3_o pro; // proxy c3_o log; // keep access log c3_o red; // redirect to HTTPS - uv_buf_t key_u; // PEM RSA private key - uv_buf_t cer_u; // PEM certificate chain + struct _u3_cert* cer_u; // available certs } u3_form; /* u3_hfig: general http configuration @@ -2356,6 +2363,60 @@ _http_wain_to_buf(u3_noun wan) return uv_buf_init(buf_c, len_w); } +static c3_o +_http_match_turf(const c3_c* sen_c, u3_noun tuf) +{ + u3_noun fut = u3kb_flop(u3k(tuf)); // (list @t) + + while (u3_nul != fut) { + c3_s len_s = 0; + while ( '.' != *(sen_c + len_s) && + 0 != *(sen_c + len_s) ) + { + len_s++; + } + + u3_noun seg = u3i_bytes(len_s, (c3_y*)sen_c); + sen_c = sen_c + len_s + 1; + + if (u3_nul == u3h(fut) || 0 == u3r_comp(u3h(fut), seg)) { + fut = u3t(fut); + continue; + } + else { + return c3n; + } + } + if ( 0 == *(sen_c-1) ) { + return c3y; + } + return c3n; +} + +static int +_http_sni_cb(SSL* ssl_u, int* ad, void* arg) +{ + u3_httd* htd_u = (u3_httd*)arg; + u3_cert* cer_u = htd_u->fig_u.for_u->cer_u; + + const c3_c* sen_c = SSL_get_servername(ssl_u, TLSEXT_NAMETYPE_host_name); + if ( 0 != sen_c ) { + while ( 0 != cer_u ) { + if ( c3y == _http_match_turf(sen_c, cer_u->tuf) ) { + // swap out context to use matching cert + // + SSL_set_SSL_CTX(ssl_u, cer_u->ctx_u); + return SSL_TLSEXT_ERR_OK; + } + cer_u = cer_u->nex_u; + } + } + + // retained default context + // + return SSL_TLSEXT_ERR_OK; +} + /* _http_init_tls: initialize OpenSSL context */ static SSL_CTX* @@ -2435,6 +2496,21 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) return tls_u; } +/* _http_init_bootstrap_tls: initialize default OpenSSL context +*/ +static SSL_CTX* +_http_init_bootstrap_tls(u3_httd* htd_u, uv_buf_t key_u, uv_buf_t cer_u) +{ + SSL_CTX* ssl_ctx = _http_init_tls(key_u, cer_u); + + // install the SNI callback on the default SSL_CTX + // + SSL_CTX_set_tlsext_servername_callback(ssl_ctx, _http_sni_cb); + SSL_CTX_set_tlsext_servername_arg(ssl_ctx, (void*)htd_u); + + return ssl_ctx; +} + /* _http_write_ports_file(): update .http.ports */ static void @@ -2531,6 +2607,7 @@ _http_serv_start_all(u3_httd* htd_u) u3_noun non = u3_none; u3_noun dis; u3_form* for_u = htd_u->fig_u.for_u; + u3_cert* cer_u = for_u->cer_u; u3_assert( 0 != for_u ); @@ -2538,8 +2615,13 @@ _http_serv_start_all(u3_httd* htd_u) htd_u->tls_u = 0; // HTTPS server. - if ( (0 != for_u->key_u.base) && (0 != for_u->cer_u.base) ) { - htd_u->tls_u = _http_init_tls(for_u->key_u, for_u->cer_u); + if ( 0 != cer_u ) { + // use lowest priority / "most general" cert for bootstrap tls + // + while ( 0 != cer_u->nex_u ) { + cer_u = cer_u->nex_u; + } + htd_u->tls_u = _http_init_bootstrap_tls(htd_u, cer_u->key_u, cer_u->cer_u); // Note: if tls_u is used for additional servers, // its reference count must be incremented with SSL_CTX_up_ref @@ -2602,7 +2684,6 @@ _http_serv_start_all(u3_httd* htd_u) } _http_write_ports_file(htd_u, u3_Host.dir_c); - _http_form_free(htd_u); } /* _http_serv_restart(): gracefully shutdown, then start servers. @@ -2620,6 +2701,7 @@ _http_serv_restart(u3_httd* htd_u) while ( 0 != htp_u ) { if ( c3y == htp_u->liv ) { + //NOTE last one that closes ends up calling _http_serv_start_all _http_serv_close(htp_u); } htp_u = htp_u->nex_u; @@ -2640,12 +2722,15 @@ _http_form_free(u3_httd* htd_u) return; } - if ( 0 != for_u->key_u.base ) { - c3_free(for_u->key_u.base); - } + while ( 0 != for_u->cer_u ) { + u3_cert* old_u = for_u->cer_u; + for_u->cer_u = old_u->nex_u; - if ( 0 != for_u->cer_u.base ) { - c3_free(for_u->cer_u.base); + u3z(old_u->tuf); + c3_free(old_u->key_u.base); + c3_free(old_u->cer_u.base); + SSL_CTX_free(old_u->ctx_u); + c3_free(old_u); } c3_free(for_u); @@ -2670,10 +2755,18 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) u3_noun sec, pro, log, red; if ( (c3n == u3r_qual(fig, &sec, &pro, &log, &red) ) || - // confirm sec is a valid (unit ^) - !( u3_nul == sec || ( c3y == u3du(sec) && - c3y == u3du(u3t(sec)) && - u3_nul == u3h(sec) ) ) || + // confirm sec is either (unit ^) or (list [* ^]) + !( // empty + u3_nul == sec + // unit value (old style) + || ( c3y == u3du(sec) && + c3y == u3du(u3t(sec)) && + u3_nul == u3h(sec) ) + // non-empty list (new style) + || ( c3y == u3du(sec) && + c3y == u3du(u3h(sec)) && + c3y == u3du(u3t(u3h(sec))) ) + ) || // confirm valid flags ("loobeans") !( c3y == pro || c3n == pro ) || !( c3y == log || c3n == log ) || @@ -2689,15 +2782,54 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) for_u->red = (c3_o)red; if ( u3_nul != sec ) { - u3_noun key = u3h(u3t(sec)); - u3_noun cer = u3t(u3t(sec)); + // (unit ^) case + // + if (u3_nul == u3h(sec)) { + u3_cert* cer_u = c3_malloc(sizeof(*cer_u)); + + u3_noun key = u3h(u3t(sec)); + u3_noun cer = u3t(u3t(sec)); + + cer_u->key_u = _http_wain_to_buf(u3k(key)); + cer_u->cer_u = _http_wain_to_buf(u3k(cer)); + cer_u->ctx_u = _http_init_tls(cer_u->key_u, cer_u->cer_u); + cer_u->nex_u = 0; - for_u->key_u = _http_wain_to_buf(u3k(key)); - for_u->cer_u = _http_wain_to_buf(u3k(cer)); + for_u->cer_u = cer_u; + } + // (list [* ^]) case + // + else { + sec = u3kb_flop(u3k(sec)); + while (u3_nul != sec) { + u3_cert* cer_u = c3_malloc(sizeof(*cer_u)); + + u3_noun nod = u3h(sec); + u3_noun key = u3h(u3t(nod)); + u3_noun cer = u3t(u3t(nod)); + + cer_u->tuf = u3k(u3h(nod)); + cer_u->key_u = _http_wain_to_buf(u3k(key)); + cer_u->cer_u = _http_wain_to_buf(u3k(cer)); + cer_u->ctx_u = _http_init_tls(cer_u->key_u, cer_u->cer_u); + cer_u->nex_u = 0; + + if (0 == for_u->cer_u) { + for_u->cer_u = cer_u; + } + else { + cer_u->nex_u = for_u->cer_u; + for_u->cer_u = cer_u; + } + + u3z(nod); + sec = u3t(sec); + } + u3z(sec); + } } else { - for_u->key_u = uv_buf_init(0, 0); - for_u->cer_u = uv_buf_init(0, 0); + for_u->cer_u = 0; } u3z(fig); From 0b375178edc9ebcea62551b9b3546b4bae0ebc0a Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 7 Apr 2026 20:06:02 +0200 Subject: [PATCH 3/4] http: use latest _server_method from openssl Use TLS_server_method() instead of the deprecated SSLv23_server_method(). Should be equivalent and be compatible with the version of OpenSSL that we're building against. --- pkg/vere/io/http.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 26ac34d58e..52f1390e9a 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -2422,8 +2422,8 @@ _http_sni_cb(SSL* ssl_u, int* ad, void* arg) static SSL_CTX* _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) { - // XX require 1.1.0 and use TLS_server_method() - SSL_CTX* tls_u = SSL_CTX_new(SSLv23_server_method()); + // XX require 1.1.0 and use TLS_server_method() //TODO that compiles. just do it? + SSL_CTX* tls_u = SSL_CTX_new(TLS_server_method()); // XX use SSL_CTX_set_max_proto_version() and SSL_CTX_set_min_proto_version() SSL_CTX_set_options(tls_u, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | From 067a15b9ffca55c2b040733beda71a529ad35813 Mon Sep 17 00:00:00 2001 From: fang Date: Tue, 21 Apr 2026 19:23:01 +0200 Subject: [PATCH 4/4] http: ensure certs list gets zero-initialized --- pkg/vere/io/http.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 52f1390e9a..ccded20392 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -2780,6 +2780,7 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) for_u->pro = (c3_o)pro; for_u->log = (c3_o)log; for_u->red = (c3_o)red; + for_u->cer_u = 0; if ( u3_nul != sec ) { // (unit ^) case