}
#endif /* HAVE_SSL */
+#ifdef HAVE_SSL
+void* daemon_setup_listen_dot_sslctx(struct daemon* daemon,
+ struct config_file* cfg)
+{
+ void* ctx;
+ (void)setup_listen_sslctx(&ctx, 1, 0, cfg, daemon->chroot);
+ return ctx;
+}
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+void* daemon_setup_listen_doh_sslctx(struct daemon* daemon,
+ struct config_file* cfg)
+{
+ void* ctx;
+ (void)setup_listen_sslctx(&ctx, 0, 1, cfg, daemon->chroot);
+ return ctx;
+}
+#endif /* HAVE_NGHTTP2_NGHTTP2_H */
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+#ifdef HAVE_NGTCP2
+void* daemon_setup_listen_quic_sslctx(struct daemon* daemon,
+ struct config_file* cfg)
+{
+ void* ctx;
+ char* chroot = daemon->chroot;
+ char* key = cfg->ssl_service_key;
+ char* pem = cfg->ssl_service_pem;
+ if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+ key += strlen(chroot);
+ if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+ pem += strlen(chroot);
+
+ if(!(ctx = quic_sslctx_create(key, pem, NULL))) {
+ fatal_exit("could not set up quic SSL_CTX");
+ }
+ return ctx;
+}
+#endif /* HAVE_NGTCP2 */
+#endif /* HAVE_SSL */
+
+#ifdef HAVE_SSL
+void* daemon_setup_connect_dot_sslctx(struct daemon* daemon,
+ struct config_file* cfg)
+{
+ void* ctx;
+ char* bundle, *chroot = daemon->chroot;
+ bundle = cfg->tls_cert_bundle;
+ if(chroot && bundle && strncmp(bundle, chroot, strlen(chroot)) == 0)
+ bundle += strlen(chroot);
+
+ if(!(ctx = connect_sslctx_create(NULL, NULL, bundle,
+ cfg->tls_win_cert)))
+ fatal_exit("could not set up connect SSL_CTX");
+ return ctx;
+}
+#endif /* HAVE_SSL */
+
/* setups the needed ssl contexts, fatal_exit() on any failure */
void
daemon_setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
{
#ifdef HAVE_SSL
- char* bundle, *chroot = daemon->chroot;
+ char* chroot = daemon->chroot;
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
char* key = cfg->ssl_service_key;
char* pem = cfg->ssl_service_pem;
fatal_exit("could not set session ticket SSL_CTX");
}
}
- (void)setup_listen_sslctx(&daemon->listen_dot_sslctx, 1, 0,
- cfg, chroot);
+ daemon->listen_dot_sslctx = daemon_setup_listen_dot_sslctx(
+ daemon, cfg);
#ifdef HAVE_NGHTTP2_NGHTTP2_H
if(cfg_has_https(cfg)) {
- (void)setup_listen_sslctx(&daemon->listen_doh_sslctx,
- 0, 1, cfg, chroot);
+ daemon->listen_doh_sslctx =
+ daemon_setup_listen_doh_sslctx(daemon, cfg);
}
#endif
#ifdef HAVE_NGTCP2
if(cfg_has_quic(cfg)) {
- if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
- key, pem, NULL))) {
- fatal_exit("could not set up quic SSL_CTX");
- }
+ daemon->listen_quic_sslctx =
+ daemon_setup_listen_quic_sslctx(daemon, cfg);
}
#endif /* HAVE_NGTCP2 */
log_err("Could not stat(%s): %s",
pem, strerror(errno));
}
- bundle = cfg->tls_cert_bundle;
- if(chroot && bundle && strncmp(bundle, chroot, strlen(chroot)) == 0)
- bundle += strlen(chroot);
- if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
- bundle, cfg->tls_win_cert)))
- fatal_exit("could not set up connect SSL_CTX");
+ daemon->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(
+ daemon, cfg);
#else /* HAVE_SSL */
(void)daemon;(void)cfg;
#endif /* HAVE_SSL */
#endif
}
-/** See if the SSL cert files have changed */
-static int
+int
ssl_cert_changed(struct daemon* daemon, struct config_file* cfg)
{
time_t mtime = 0;
long ns = 0;
+ char* chroot = daemon->chroot;
+ char* key = cfg->ssl_service_key;
+ char* pem = cfg->ssl_service_pem;
log_assert(daemon->ssl_service_key && cfg->ssl_service_key);
+ if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+ key += strlen(chroot);
+ if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+ pem += strlen(chroot);
+
if(strcmp(daemon->ssl_service_key, cfg->ssl_service_key) != 0)
return 1;
- if(strcmp(daemon->ssl_service_pem, cfg->ssl_service_pem) != 0)
+ if(daemon->ssl_service_pem && cfg->ssl_service_pem &&
+ strcmp(daemon->ssl_service_pem, cfg->ssl_service_pem) != 0)
return 1;
- if(!file_get_mtime(daemon->ssl_service_key, &mtime, &ns, NULL)) {
+ if(!file_get_mtime(key, &mtime, &ns, NULL)) {
log_err("Could not stat(%s): %s",
- daemon->ssl_service_key, strerror(errno));
+ key, strerror(errno));
/* It has probably changed, but file read is likely going to
* fail. */
return 0;
if(mtime != daemon->mtime_ssl_service_key ||
ns != daemon->mtime_ns_ssl_service_key)
return 1;
- if(!file_get_mtime(daemon->ssl_service_pem, &mtime, &ns, NULL)) {
+ if(!file_get_mtime(pem, &mtime, &ns, NULL)) {
log_err("Could not stat(%s): %s",
- daemon->ssl_service_pem, strerror(errno));
+ pem, strerror(errno));
/* It has probably changed, but file read is likely going to
* fail. */
return 0;
* are kept in here. They can then be deleted.
*/
struct fast_reload_construct {
+ /** ssl context for listening to dnstcp over ssl */
+ void* listen_dot_sslctx;
+ /** ssl context for connecting to dnstcp over ssl */
+ void* connect_dot_sslctx;
+ /** ssl context for listening to DoH */
+ void* listen_doh_sslctx;
+ /** ssl context for listening to quic */
+ void* listen_quic_sslctx;
+ /** the file name that the ssl context is made with, private key. */
+ char* ssl_service_key;
+ /** the file name that the ssl context is made with, certificate. */
+ char* ssl_service_pem;
+ /** modification time for ssl_service_key, in sec and ns. Like
+ * in a struct timespec, but without that for portability. */
+ time_t mtime_ssl_service_key;
+ long mtime_ns_ssl_service_key;
+ /** modification time for ssl_service_pem, in sec and ns. Like
+ * in a struct timespec, but without that for portability. */
+ time_t mtime_ssl_service_pem;
+ long mtime_ns_ssl_service_pem;
/** construct for views */
struct views* views;
/** construct for auth zones */
FR_CHECK_CHANGED_CFG("http_notls_downstream", http_notls_downstream, changed_str);
FR_CHECK_CHANGED_CFG("https-port", https_port, changed_str);
FR_CHECK_CHANGED_CFG("tls-port", ssl_port, changed_str);
- FR_CHECK_CHANGED_CFG_STR("tls-service-key", ssl_service_key, changed_str);
- FR_CHECK_CHANGED_CFG_STR("tls-service-pem", ssl_service_pem, changed_str);
- FR_CHECK_CHANGED_CFG_STR("tls-cert-bundle", tls_cert_bundle, changed_str);
FR_CHECK_CHANGED_CFG_STRLIST("proxy-protocol-port", proxy_protocol_port, changed_str);
FR_CHECK_CHANGED_CFG_STRLIST("tls-additional-port", tls_additional_port, changed_str);
FR_CHECK_CHANGED_CFG_STR("interface-automatic-ports", if_automatic_ports, changed_str);
wait_limits_free(&ct->wait_limits_netblock);
wait_limits_free(&ct->wait_limits_cookie_netblock);
domain_limits_free(&ct->domain_limits);
+#ifdef HAVE_SSL
+ /* The SSL contexts can be SSL_CTX_free here. It is reference
+ * counted. So ongoing transfers with can continue.
+ * Once they are done, the context is freed. */
+ SSL_CTX_free((SSL_CTX*)ct->listen_dot_sslctx);
+ SSL_CTX_free((SSL_CTX*)ct->connect_dot_sslctx);
+ SSL_CTX_free((SSL_CTX*)ct->listen_doh_sslctx);
+#endif /* HAVE_SSL */
+#ifdef HAVE_NGTCP2
+ SSL_CTX_free((SSL_CTX*)ct->listen_quic_sslctx);
+#endif
+ free(ct->ssl_service_key);
+ free(ct->ssl_service_pem);
/* Delete the log identity here so that the global value is not
* reset by config_delete. */
if(ct->oldcfg && ct->oldcfg->log_identity) {
return 1;
}
+/** Check if the sslctxs have changed. */
+static int
+fr_check_sslctx_change(struct fast_reload_thread* fr,
+ struct config_file* newcfg)
+{
+#ifdef HAVE_SSL
+ struct daemon* daemon = fr->worker->daemon;
+ if(newcfg->ssl_service_key && newcfg->ssl_service_key[0]) {
+ if(!daemon->ssl_service_key ||
+ ssl_cert_changed(daemon, newcfg))
+ return 1;
+ } else {
+ if(daemon->ssl_service_key)
+ return 1; /* it is removed */
+ }
+ if((daemon->cfg->tls_cert_bundle && !newcfg->tls_cert_bundle) ||
+ (!daemon->cfg->tls_cert_bundle && newcfg->tls_cert_bundle) ||
+ (daemon->cfg->tls_cert_bundle && newcfg->tls_cert_bundle &&
+ strcmp(daemon->cfg->tls_cert_bundle, newcfg->tls_cert_bundle)!=0))
+ return 1; /* The tls-cert-bundle has changed and return
+ true here makes it reload the connect_dot_sslctx. */
+#else
+ (void)fr; (void)newcfg;
+#endif /* HAVE_SSL */
+ return 0;
+}
+
+/** Create the SSL CTXs when they have changed. */
+static int
+ct_create_sslctxs(struct fast_reload_construct* ct,
+ struct config_file* newcfg, struct daemon* daemon)
+{
+#ifdef HAVE_SSL
+ char* chroot = daemon->chroot;
+ char* key = newcfg->ssl_service_key;
+ char* pem = newcfg->ssl_service_pem;
+
+ if(!(newcfg->ssl_service_key && newcfg->ssl_service_key[0])) {
+ /* Leave listen ctxs and file str at NULL */
+ ct->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(
+ daemon, newcfg);
+ return 1;
+ }
+
+ if(chroot && strncmp(key, chroot, strlen(chroot)) == 0)
+ key += strlen(chroot);
+ if(chroot && pem && strncmp(pem, chroot, strlen(chroot)) == 0)
+ pem += strlen(chroot);
+
+ ct->listen_dot_sslctx = daemon_setup_listen_dot_sslctx(daemon, newcfg);
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+ if(cfg_has_https(newcfg)) {
+ ct->listen_doh_sslctx = daemon_setup_listen_doh_sslctx(
+ daemon, newcfg);
+ }
+#endif
+#ifdef HAVE_NGTCP2
+ if(cfg_has_quic(newcfg)) {
+ ct->listen_quic_sslctx = daemon_setup_listen_quic_sslctx(
+ daemon, newcfg);
+ }
+#endif /* HAVE_NGTCP2 */
+ ct->connect_dot_sslctx = daemon_setup_connect_dot_sslctx(daemon,
+ newcfg);
+
+ /* Store mtime and names */
+ ct->ssl_service_key = strdup(newcfg->ssl_service_key);
+ if(!ct->ssl_service_key) {
+ log_err("ct_create_sslctxs: out of memory");
+ return 0;
+ }
+ ct->ssl_service_pem = strdup(newcfg->ssl_service_pem);
+ if(!ct->ssl_service_pem) {
+ log_err("ct_create_sslctxs: out of memory");
+ return 0;
+ }
+ if(!file_get_mtime(key, &ct->mtime_ssl_service_key,
+ &ct->mtime_ns_ssl_service_key, NULL))
+ log_err("Could not stat(%s): %s",
+ key, strerror(errno));
+ if(!file_get_mtime(pem, &ct->mtime_ssl_service_pem,
+ &ct->mtime_ns_ssl_service_pem, NULL))
+ log_err("Could not stat(%s): %s",
+ pem, strerror(errno));
+#else
+ (void)ct; (void)newcfg; (void)daemon;
+#endif /* HAVE_SSL */
+ return 1;
+}
+
/** fast reload thread, construct from config the new items */
static int
fr_construct_from_config(struct fast_reload_thread* fr,
{
int have_view_respip_cfg = 0;
+ fr->sslctxs_changed = fr_check_sslctx_change(fr, newcfg);
+ if(fr->sslctxs_changed) {
+ if(!ct_create_sslctxs(ct, newcfg, fr->worker->daemon)) {
+ fr_construct_clear(ct);
+ return 0;
+ }
+ }
if(!(ct->views = views_create())) {
fr_construct_clear(ct);
return 0;
* the xfer elements can continue to be their callbacks. */
}
+/** Swap two void* */
+static void
+void_ptr_swap(void** a, void **b)
+{
+ void* tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/** Swap two char* */
+static void
+char_ptr_swap(char** a, char **b)
+{
+ char* tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/** Swap and set ssl ctx information */
+static void
+sslctxs_swap(struct daemon* daemon, struct fast_reload_construct* ct)
+{
+ void_ptr_swap(&daemon->listen_dot_sslctx, &ct->listen_dot_sslctx);
+ void_ptr_swap(&daemon->connect_dot_sslctx, &ct->connect_dot_sslctx);
+#ifdef HAVE_NGHTTP2_NGHTTP2_H
+ void_ptr_swap(&daemon->listen_doh_sslctx, &ct->listen_doh_sslctx);
+#endif
+#ifdef HAVE_NGTCP2
+ void_ptr_swap(&daemon->listen_quic_sslctx, &ct->listen_quic_sslctx);
+#endif /* HAVE_NGTCP2 */
+ char_ptr_swap(&daemon->ssl_service_key, &ct->ssl_service_key);
+ char_ptr_swap(&daemon->ssl_service_pem, &ct->ssl_service_pem);
+ daemon->mtime_ssl_service_key = ct->mtime_ssl_service_key;
+ daemon->mtime_ns_ssl_service_key = ct->mtime_ns_ssl_service_key;
+ daemon->mtime_ssl_service_pem = ct->mtime_ssl_service_pem;
+ daemon->mtime_ns_ssl_service_pem = ct->mtime_ns_ssl_service_pem;
+}
+
#if defined(ATOMIC_POINTER_LOCK_FREE) && defined(HAVE_LINK_ATOMIC_STORE)
/** Fast reload thread, if atomics are available, copy the config items
* one by one with atomic store operations. */
daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods,
daemon->env);
#endif
+ if(fr->sslctxs_changed) {
+ sslctxs_swap(daemon, ct);
+ }
#ifdef USE_DNSTAP
if(env->cfg->dnstap) {
if(!fr->fr_nopause)
}
}
+/** Fast reload, the worker picks up changes in listen_dnsport. */
+static void
+fr_worker_pickup_listen_dnsport(struct worker* worker)
+{
+ struct listen_dnsport* front = worker->front;
+ struct daemon* daemon = worker->daemon;
+ if(worker->daemon->fast_reload_thread->sslctxs_changed) {
+ struct listen_list* ll;
+ void* dot_sslctx = daemon->listen_dot_sslctx;
+ void* doh_sslctx = daemon->listen_doh_sslctx;
+ void* quic_sslctx = daemon->listen_quic_sslctx;
+ for(ll = front->cps; ll; ll = ll->next) {
+ struct comm_point* cp = ll->com;
+ if(cp->type == comm_tcp_accept &&
+ cp->pp2_enabled /* true for http */) {
+ if(cp->ssl)
+ cp->ssl = doh_sslctx;
+ } else if(cp->type == comm_tcp_accept) {
+ if(cp->ssl)
+ cp->ssl = dot_sslctx;
+#ifdef HAVE_NGTCP2
+ } else if(cp->type == comm_doq) {
+ if(cp->ssl) {
+ cp->ssl = quic_sslctx;
+ if(cp->doq_socket)
+ cp->doq_socket->ctx =
+ (SSL_CTX*)quic_sslctx;
+ }
+#endif
+ }
+ }
+ }
+}
+
/** Fast reload, the worker picks up changes in outside_network. */
static void
fr_worker_pickup_outside_network(struct worker* worker)
outnet->tcp_reuse_timeout = cfg->tcp_reuse_timeout;
outnet->tcp_auth_query_timeout = cfg->tcp_auth_query_timeout;
outnet->delayclose = cfg->delay_close;
+ if(worker->daemon->fast_reload_thread->sslctxs_changed)
+ outnet->sslctx = worker->daemon->connect_dot_sslctx;
if(outnet->delayclose) {
#ifndef S_SPLINT_S
outnet->delay_tv.tv_sec = cfg->delay_close/1000;
worker->env.cachedb_enabled = worker->daemon->env->cachedb_enabled;
#endif
fr_worker_pickup_outside_network(worker);
+ fr_worker_pickup_listen_dnsport(worker);
#ifdef USE_DNSTAP
fr_worker_pickup_dnstap_changes(worker);
#endif