diff --git a/bin/named/config.c b/bin/named/config.c index 6f63b0130b..3c34dea044 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -93,6 +93,7 @@ options {\n\ #if HAVE_LIBNGHTTP2 "http-port 80;\n" "https-port 443;\n" + "http-listener-clients 300;\n" #endif "\ prefetch 2 9;\n\ diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index 8663eaf012..78c0bd032f 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -77,6 +77,8 @@ EXTERN in_port_t named_g_httpsport INIT(0); EXTERN in_port_t named_g_httpport INIT(0); EXTERN isc_dscp_t named_g_dscp INIT(-1); +EXTERN in_port_t named_g_http_listener_clients INIT(0); + EXTERN named_server_t *named_g_server INIT(NULL); /* diff --git a/bin/named/server.c b/bin/named/server.c index 0339f0bef8..dd2d64aeaf 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8629,6 +8629,11 @@ load_configuration(const char *filename, named_server_t *server, result = named_config_get(maps, "https-port", &obj); INSIST(result == ISC_R_SUCCESS); named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "http-listener-clients", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_http_listener_clients = cfg_obj_asuint32(obj); #endif /* @@ -11326,6 +11331,9 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key, const cfg_obj_t *eplist = NULL; const cfg_listelt_t *elt = NULL; size_t len = 1, i = 0; + uint32_t max_clients = named_g_http_listener_clients; + ns_server_t *server = NULL; + isc_quota_t *quota = NULL; REQUIRE(target != NULL && *target == NULL); REQUIRE((key == NULL) == (cert == NULL)); @@ -11339,13 +11347,22 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key, * of "/dns-query". */ if (http != NULL) { - CHECK(cfg_map_get(http, "endpoints", &eplist)); - len = cfg_list_length(eplist, false); + const cfg_obj_t *cfg_max_clients = NULL; + if (cfg_map_get(http, "endpoints", &eplist) == ISC_R_SUCCESS) { + INSIST(eplist != NULL); + len = cfg_list_length(eplist, false); + } + + if (cfg_map_get(http, "listener-clients", &cfg_max_clients) == + ISC_R_SUCCESS) { + INSIST(cfg_max_clients != NULL); + max_clients = cfg_obj_asuint32(cfg_max_clients); + } } endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len); - if (http != NULL) { + if (http != NULL && eplist != NULL) { for (elt = cfg_list_first(eplist); elt != NULL; elt = cfg_list_next(elt)) { const cfg_obj_t *ep = cfg_listelt_value(elt); @@ -11358,18 +11375,39 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key, INSIST(i == len); - result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls, - key, cert, endpoints, len, &delt); - if (result != ISC_R_SUCCESS) { - if (delt != NULL) { - ns_listenelt_destroy(delt); - } - return (result); + INSIST(named_g_server != NULL); + ns_server_attach(named_g_server->sctx, &server); + if (max_clients > 0) { + quota = isc_mem_get(mctx, sizeof(isc_quota_t)); + isc_quota_init(quota, max_clients); } + result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls, + key, cert, endpoints, len, quota, + &delt); + if (result != ISC_R_SUCCESS) { + goto error; + } + + if (quota != NULL) { + ISC_LIST_APPEND(server->http_quotas, quota, link); + } + ns_server_detach(&server); *target = delt; -cleanup: + return (result); +error: + if (delt != NULL) { + ns_listenelt_destroy(delt); + } + if (quota != NULL) { + isc_quota_destroy(quota); + isc_mem_put(mctx, quota, sizeof(*quota)); + } + + if (server != NULL) { + ns_server_detach(&server); + } return (result); } diff --git a/bin/tests/system/checkconf/good-doh-1.conf b/bin/tests/system/checkconf/good-doh-1.conf index 8f983778b1..bd23227ce5 100644 --- a/bin/tests/system/checkconf/good-doh-1.conf +++ b/bin/tests/system/checkconf/good-doh-1.conf @@ -16,12 +16,14 @@ tls local-tls { http local-http-server { endpoints { "/dns-query"; }; + listener-clients 100; }; options { listen-on { 10.53.0.1; }; http-port 80; https-port 443; + http-listener-clients 100; listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; }; listen-on port 8080 tls none http local-http-server { 10.53.0.1; }; }; diff --git a/bin/tests/system/checkconf/good-doh-4.conf b/bin/tests/system/checkconf/good-doh-4.conf new file mode 100644 index 0000000000..2d4c342d43 --- /dev/null +++ b/bin/tests/system/checkconf/good-doh-4.conf @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +# Use the default values only - just to make sure that we could +# override only values which we need and there is no required ones. +http empty-http-server { +}; + +options { + listen-on { 10.53.0.1; }; + http-port 80; + https-port 443; + http-listener-clients 100; + listen-on port 443 tls local-tls http empty-http-server { 10.53.0.1; }; + listen-on port 8080 tls none http empty-http-server { 10.53.0.1; }; +}; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index e97983cce6..d247962fc7 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1986,17 +1986,19 @@ bind9_check_httpserver(const cfg_obj_t *http, isc_log_t *logctx, /* Check endpoints are valid */ tresult = cfg_map_get(http, "endpoints", &eps); - RUNTIME_CHECK(tresult == ISC_R_SUCCESS); - for (elt = cfg_list_first(eps); elt != NULL; elt = cfg_list_next(elt)) { - const cfg_obj_t *ep = cfg_listelt_value(elt); - const char *path = cfg_obj_asstring(ep); - if (!isc_nm_http_path_isvalid(path)) { - cfg_obj_log(eps, logctx, ISC_LOG_ERROR, - "endpoint '%s' is not a " - "valid absolute HTTP path", - path); - if (result == ISC_R_SUCCESS) { - result = ISC_R_FAILURE; + if (tresult == ISC_R_SUCCESS) { + for (elt = cfg_list_first(eps); elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *ep = cfg_listelt_value(elt); + const char *path = cfg_obj_asstring(ep); + if (!isc_nm_http_path_isvalid(path)) { + cfg_obj_log(eps, logctx, ISC_LOG_ERROR, + "endpoint '%s' is not a " + "valid absolute HTTP path", + path); + if (result == ISC_R_SUCCESS) { + result = ISC_R_FAILURE; + } } } } diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h index 3431006595..6498c6e956 100644 --- a/lib/isc/include/isc/quota.h +++ b/lib/isc/include/isc/quota.h @@ -60,6 +60,7 @@ struct isc_quota { atomic_uint_fast32_t waiting; isc_mutex_t cblock; ISC_LIST(isc_quota_cb_t) cbs; + ISC_LINK(isc_quota_t) link; }; void diff --git a/lib/isc/quota.c b/lib/isc/quota.c index 709f174042..7474a3d603 100644 --- a/lib/isc/quota.c +++ b/lib/isc/quota.c @@ -31,6 +31,7 @@ isc_quota_init(isc_quota_t *quota, unsigned int max) { atomic_init("a->waiting, 0); ISC_LIST_INIT(quota->cbs); isc_mutex_init("a->cblock); + ISC_LINK_INIT(quota, link); quota->magic = QUOTA_MAGIC; } diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 7d283b0e4a..decc0cfe88 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1251,9 +1251,12 @@ static cfg_clausedef_t options_clauses[] = { { "tls-port", &cfg_type_uint32, 0 }, #if HAVE_LIBNGHTTP2 { "http-port", &cfg_type_uint32, 0 }, + { "http-listener-clients", &cfg_type_uint32, 0 }, { "https-port", &cfg_type_uint32, 0 }, #else { "http-port", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTCONFIGURED }, + { "http-listener-clients", &cfg_type_uint32, + CFG_CLAUSEFLAG_NOTCONFIGURED }, { "https-port", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif { "querylog", &cfg_type_boolean, 0 }, @@ -3902,6 +3905,8 @@ static cfg_type_t cfg_type_bracketed_http_endpoint_list = { static cfg_clausedef_t cfg_http_description_clauses[] = { { "endpoints", &cfg_type_bracketed_http_endpoint_list, 0 }, + { "listener-clients", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } }; static cfg_clausedef_t *http_description_clausesets[] = { diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index 8393d67980..d0c91480ea 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -48,6 +48,7 @@ struct ns_listenelt { isc_tlsctx_t *sslctx; char ** http_endpoints; size_t http_endpoints_number; + isc_quota_t * http_quota; ISC_LINK(ns_listenelt_t) link; }; @@ -73,7 +74,7 @@ isc_result_t ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, dns_acl_t *acl, bool tls, const char *key, const char *cert, char **endpoints, size_t nendpoints, - ns_listenelt_t **target); + isc_quota_t *quota, ns_listenelt_t **target); /*%< * Create a listen-on list element for HTTP(S). */ diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h index 6af3436abc..d104b85f33 100644 --- a/lib/ns/include/ns/server.h +++ b/lib/ns/include/ns/server.h @@ -83,6 +83,7 @@ struct ns_server { isc_quota_t recursionquota; isc_quota_t tcpquota; isc_quota_t xfroutquota; + ISC_LIST(isc_quota_t) http_quotas; /*% Test options and other configurables */ uint32_t options; diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index 2d0d60ca6d..38a1691c87 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -539,13 +539,13 @@ ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) { static isc_result_t ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps, - size_t neps) { + size_t neps, isc_quota_t *quota) { #if HAVE_LIBNGHTTP2 isc_result_t result; isc_nmsocket_t *sock = NULL; result = isc_nm_listenhttp(ifp->mgr->nm, &ifp->addr, ifp->mgr->backlog, - &ifp->mgr->sctx->tcpquota, sslctx, &sock); + quota, sslctx, &sock); if (result == ISC_R_SUCCESS) { for (size_t i = 0; i < neps; i++) { @@ -609,9 +609,9 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, ifp->dscp = elt->dscp; if (elt->is_http) { - result = ns_interface_listenhttp(ifp, elt->sslctx, - elt->http_endpoints, - elt->http_endpoints_number); + result = ns_interface_listenhttp( + ifp, elt->sslctx, elt->http_endpoints, + elt->http_endpoints_number, elt->http_quota); if (result != ISC_R_SUCCESS) { goto cleanup_interface; } diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 2d88c8654f..141416a93c 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -51,6 +51,7 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, elt->sslctx = sslctx; elt->http_endpoints = NULL; elt->http_endpoints_number = 0; + elt->http_quota = NULL; *target = elt; return (ISC_R_SUCCESS); @@ -60,7 +61,7 @@ isc_result_t ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, dns_acl_t *acl, bool tls, const char *key, const char *cert, char **endpoints, size_t nendpoints, - ns_listenelt_t **target) { + isc_quota_t *quota, ns_listenelt_t **target) { isc_result_t result; REQUIRE(target != NULL && *target == NULL); @@ -73,6 +74,7 @@ ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, (*target)->is_http = true; (*target)->http_endpoints = endpoints; (*target)->http_endpoints_number = nendpoints; + (*target)->http_quota = quota; } else { size_t i; for (i = 0; i < nendpoints; i++) { diff --git a/lib/ns/server.c b/lib/ns/server.c index a970a28e89..b9dbac6fc8 100644 --- a/lib/ns/server.c +++ b/lib/ns/server.c @@ -52,6 +52,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview, isc_quota_init(&sctx->xfroutquota, 10); isc_quota_init(&sctx->tcpquota, 10); isc_quota_init(&sctx->recursionquota, 100); + ISC_LIST_INIT(sctx->http_quotas); CHECKFATAL(dns_tkeyctx_create(mctx, &sctx->tkeyctx)); @@ -125,6 +126,7 @@ ns_server_detach(ns_server_t **sctxp) { if (isc_refcount_decrement(&sctx->references) == 1) { ns_altsecret_t *altsecret; + isc_quota_t *http_quota; while ((altsecret = ISC_LIST_HEAD(sctx->altsecrets)) != NULL) { ISC_LIST_UNLINK(sctx->altsecrets, altsecret, link); @@ -135,6 +137,18 @@ ns_server_detach(ns_server_t **sctxp) { isc_quota_destroy(&sctx->tcpquota); isc_quota_destroy(&sctx->xfroutquota); + http_quota = ISC_LIST_HEAD(sctx->http_quotas); + while (http_quota != NULL) { + isc_quota_t *next = NULL; + + next = ISC_LIST_NEXT(http_quota, link); + ISC_LIST_DEQUEUE(sctx->http_quotas, http_quota, link); + isc_quota_destroy(http_quota); + isc_mem_put(sctx->mctx, http_quota, + sizeof(*http_quota)); + http_quota = next; + } + if (sctx->server_id != NULL) { isc_mem_free(sctx->mctx, sctx->server_id); }