Merge branch '1144-dns-over-https-server' into 'main'

Resolve "Encrypted DNS - RFC 8484, DNS over HTTPS, DOH (also DoT comments)"

Closes #1144

See merge request isc-projects/bind9!4644
This commit is contained in:
Ondřej Surý 2021-02-03 12:01:47 +00:00
commit adf5051afc
51 changed files with 7316 additions and 198 deletions

View file

@ -70,11 +70,6 @@ stages:
- linux
- amd64
.linux-i386: &linux_i386
tags:
- linux
- i386
.linux-stress-amd64: &linux_stress_amd64
tags:
- amd64
@ -133,10 +128,6 @@ stages:
image: "$CI_REGISTRY_IMAGE:debian-sid-amd64"
<<: *linux_amd64
.debian-sid-i386: &debian_sid_i386_image
image: "$CI_REGISTRY_IMAGE:debian-sid-i386"
<<: *linux_i386
# openSUSE Tumbleweed
.tumbleweed-latest-amd64: &tumbleweed_latest_amd64_image
@ -285,6 +276,7 @@ stages:
"with-openssl=C:/OpenSSL"
"with-libxml2=C:/libxml2"
"with-libuv=C:/libuv"
"with-nghttp2=C:/nghttp2"
"without-python"
"with-system-tests"
x64'
@ -836,30 +828,6 @@ unit:gcc:tarball:
- schedules
- tags
# Jobs for regular GCC builds on Debian "sid" (i386)
gcc:sid:i386:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON}"
EXTRA_CONFIGURE: "--with-libidn2"
<<: *debian_sid_i386_image
<<: *build_job
system:gcc:sid:i386:
<<: *debian_sid_i386_image
<<: *system_test_job
needs:
- job: gcc:sid:i386
artifacts: true
unit:gcc:sid:i386:
<<: *debian_sid_i386_image
<<: *unit_test_job
needs:
- job: gcc:sid:i386
artifacts: true
# Jobs for debug GCC builds on openSUSE Tumbleweed (amd64)
gcc:tumbleweed:amd64:

View file

@ -1,3 +1,10 @@
5576. [experimental] Initial server-side implementation of DNS-over-HTTPS
(DoH). Support for both TLS-encrypted and unencrypted
HTTP/2 connections has been added to the network manager
and integrated into named. (Note: there is currently no
client-side support for DNS-over-HTTPS; this will be
added to dig in a future release.) [GL #1144]
5575. [bug] When migrating to dnssec-policy, BIND considered keys
with the "Inactive" and/or "Delete" timing metadata as
possible active keys. This has been fixed. [GL #2406]

View file

@ -367,3 +367,25 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------------
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View file

@ -94,6 +94,8 @@ options {\n\
# pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\
port 53;\n\
tls-port 853;\n\
http-port 80;\n\
https-port 443;\n\
prefetch 2 9;\n\
recursing-file \"named.recursing\";\n\
recursive-clients 1000;\n\

View file

@ -73,6 +73,8 @@ EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS);
EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER);
EXTERN in_port_t named_g_port INIT(0);
EXTERN in_port_t named_g_tlsport INIT(0);
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 named_server_t *named_g_server INIT(NULL);

View file

@ -705,7 +705,7 @@ parse_T_opt(char *option) {
static void
parse_port(char *arg) {
enum { DNSPORT, TLSPORT } ptype = DNSPORT;
enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT;
char *value = arg;
int port;
@ -714,6 +714,12 @@ parse_port(char *arg) {
} else if (strncmp(arg, "tls=", 4) == 0) {
value = arg + 4;
ptype = TLSPORT;
} else if (strncmp(arg, "https=", 6) == 0) {
value = arg + 6;
ptype = HTTPSPORT;
} else if (strncmp(arg, "http=", 5) == 0) {
value = arg + 6;
ptype = HTTPPORT;
}
port = parse_int(value, "port");
@ -728,6 +734,12 @@ parse_port(char *arg) {
case TLSPORT:
named_g_tlsport = port;
break;
case HTTPSPORT:
named_g_httpsport = port;
break;
case HTTPPORT:
named_g_httpport = port;
break;
default:
INSIST(0);
ISC_UNREACHABLE();

View file

@ -86,6 +86,15 @@ DYNDB
dyndb string quoted_string {
unspecified-text };
HTTP
^^^^
::
http string {
endpoints { quoted_string; ... };
};
KEY
^^^
@ -264,6 +273,8 @@ OPTIONS
glue-cache boolean;// deprecated
heartbeat-interval integer;
hostname ( quoted_string | none );
http-port integer;
https-port integer;
inline-signing boolean;
interface-interval duration;
ipv4only-contact string;
@ -275,10 +286,12 @@ OPTIONS
key-directory quoted_string;
lame-ttl duration;
listen-on [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
listen-on-v6 [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
lmdb-mapsize sizeval;
lock-file ( quoted_string | none );

View file

@ -115,7 +115,11 @@ Options
``portnum``; if not not specified, the default is port 53. If
``value`` is of the form ``tls=<portnum>``, the server will
listen for TLS queries on ``portnum``; the default is 853.
If ``value`` is of the form ``https=<portnum>``, the server will
listen for HTTPS queries on ``portnum``; the default is 443.
If ``value`` is of the form ``http=<portnum>``, the server will
listen for HTTP queries on ``portnum``; the default is 80.
``-s``
This option writes memory usage statistics to ``stdout`` on exit.

View file

@ -398,13 +398,19 @@ static void
named_server_reload(isc_task_t *task, isc_event_t *event);
static isc_result_t
ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenelt_t **target);
listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
const char *cert, in_port_t port, isc_mem_t *mctx,
ns_listenelt_t **target);
static isc_result_t
ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenlist_t **target);
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenelt_t **target);
static isc_result_t
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenlist_t **target);
static isc_result_t
configure_forward(const cfg_obj_t *config, dns_view_t *view,
@ -8573,6 +8579,16 @@ load_configuration(const char *filename, named_server_t *server,
maps[i++] = named_g_defaults;
maps[i] = NULL;
obj = NULL;
result = named_config_get(maps, "http-port", &obj);
INSIST(result == ISC_R_SUCCESS);
named_g_httpport = (in_port_t)cfg_obj_asuint32(obj);
obj = NULL;
result = named_config_get(maps, "https-port", &obj);
INSIST(result == ISC_R_SUCCESS);
named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj);
/*
* If bind.keys exists, load it. If "dnssec-validation auto"
* is turned on, the root key found there will be used as a
@ -8989,7 +9005,7 @@ load_configuration(const char *filename, named_server_t *server,
}
if (clistenon != NULL) {
/* check return code? */
(void)ns_listenlist_fromconfig(
(void)listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
named_g_mctx, AF_INET, &listenon);
} else {
@ -9017,7 +9033,7 @@ load_configuration(const char *filename, named_server_t *server,
}
if (clistenon != NULL) {
/* check return code? */
(void)ns_listenlist_fromconfig(
(void)listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
named_g_mctx, AF_INET6, &listenon);
} else {
@ -10985,9 +11001,9 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) {
}
static isc_result_t
ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenlist_t **target) {
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenlist_t **target) {
isc_result_t result;
const cfg_listelt_t *element;
ns_listenlist_t *dlist = NULL;
@ -11004,8 +11020,8 @@ ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
{
ns_listenelt_t *delt = NULL;
const cfg_obj_t *listener = cfg_listelt_value(element);
result = ns_listenelt_fromconfig(listener, config, actx, mctx,
family, &delt);
result = listenelt_fromconfig(listener, config, actx, mctx,
family, &delt);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
@ -11019,66 +11035,114 @@ cleanup:
return (result);
}
static const cfg_obj_t *
find_maplist(const cfg_obj_t *config, const char *listname, const char *name) {
isc_result_t result;
const cfg_obj_t *maplist = NULL;
const cfg_listelt_t *elt = NULL;
REQUIRE(config != NULL);
REQUIRE(name != NULL);
result = cfg_map_get(config, listname, &maplist);
if (result != ISC_R_SUCCESS) {
return (NULL);
}
for (elt = cfg_list_first(maplist); elt != NULL;
elt = cfg_list_next(elt)) {
const cfg_obj_t *map = cfg_listelt_value(elt);
if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) ==
0) {
return (map);
}
}
return (NULL);
}
/*
* Create a listen list from the corresponding configuration
* data structure.
*/
static isc_result_t
ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx,
uint16_t family, ns_listenelt_t **target) {
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
ns_listenelt_t **target) {
isc_result_t result;
const cfg_obj_t *tlsobj, *portobj, *dscpobj;
in_port_t port;
const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
const cfg_obj_t *portobj = NULL, *dscpobj = NULL;
const cfg_obj_t *http_server = NULL;
in_port_t port = 0;
isc_dscp_t dscp = -1;
const char *key = NULL, *cert = NULL;
bool tls = false;
bool do_tls = false, http = false;
ns_listenelt_t *delt = NULL;
REQUIRE(target != NULL && *target == NULL);
/* XXXWPK TODO be more verbose on failures. */
tlsobj = cfg_tuple_get(listener, "tls");
if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) {
if (!strcmp(cfg_obj_asstring(tlsobj), "ephemeral")) {
tls = true;
} else {
const cfg_obj_t *tlsconfigs = NULL;
const cfg_listelt_t *element;
(void)cfg_map_get(config, "tls", &tlsconfigs);
for (element = cfg_list_first(tlsconfigs);
element != NULL; element = cfg_list_next(element))
{
cfg_obj_t *tconfig = cfg_listelt_value(element);
const cfg_obj_t *name =
cfg_map_getname(tconfig);
if (!strcmp(cfg_obj_asstring(name),
cfg_obj_asstring(tlsobj))) {
tls = true;
const cfg_obj_t *keyo = NULL,
*certo = NULL;
(void)cfg_map_get(tconfig, "key-file",
&keyo);
if (keyo == NULL) {
return (ISC_R_FAILURE);
}
(void)cfg_map_get(tconfig, "cert-file",
&certo);
if (certo == NULL) {
return (ISC_R_FAILURE);
}
key = cfg_obj_asstring(keyo);
cert = cfg_obj_asstring(certo);
break;
}
const char *tlsname = cfg_obj_asstring(tlsobj);
if (strcmp(tlsname, "ephemeral") != 0) {
const cfg_obj_t *keyobj = NULL, *certobj = NULL;
const cfg_obj_t *tlsmap = NULL;
tlsmap = find_maplist(config, "tls", tlsname);
if (tlsmap == NULL) {
return (ISC_R_FAILURE);
}
CHECK(cfg_map_get(tlsmap, "key-file", &keyobj));
key = cfg_obj_asstring(keyobj);
CHECK(cfg_map_get(tlsmap, "cert-file", &certobj));
cert = cfg_obj_asstring(certobj);
}
if (!tls) {
do_tls = true;
}
httpobj = cfg_tuple_get(listener, "http");
if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
const char *httpname = cfg_obj_asstring(httpobj);
http_server = find_maplist(config, "http", httpname);
if (http_server == NULL) {
cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR,
"http '%s' is not defined",
cfg_obj_asstring(httpobj));
return (ISC_R_FAILURE);
}
http = true;
}
portobj = cfg_tuple_get(listener, "port");
if (!cfg_obj_isuint32(portobj)) {
if (tls) {
if (http && do_tls) {
if (named_g_httpsport != 0) {
port = named_g_httpsport;
} else {
result = named_config_getport(
config, "https-port", &port);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
} else if (http && !do_tls) {
if (named_g_httpport != 0) {
port = named_g_port;
} else {
result = named_config_getport(
config, "http-port", &port);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
} else if (do_tls) {
if (named_g_tlsport != 0) {
port = named_g_tlsport;
} else {
@ -11103,6 +11167,7 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR,
"port value '%u' is out of range",
cfg_obj_asuint32(portobj));
return (ISC_R_RANGE);
}
@ -11122,10 +11187,13 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
}
result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key, cert,
&delt);
if (result != ISC_R_SUCCESS) {
return (result);
if (http) {
INSIST(http_server != NULL);
CHECK(listenelt_http(http_server, do_tls, key, cert, port, mctx,
&delt));
} else {
CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, key,
cert, &delt));
}
result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config,
@ -11136,7 +11204,55 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
return (result);
}
*target = delt;
return (ISC_R_SUCCESS);
cleanup:
return (result);
}
static isc_result_t
listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
const char *cert, in_port_t port, isc_mem_t *mctx,
ns_listenelt_t **target) {
isc_result_t result = ISC_R_SUCCESS;
ns_listenelt_t *delt = NULL;
char **endpoints = NULL;
const cfg_obj_t *eplist = NULL;
const cfg_listelt_t *elt = NULL;
size_t len, i = 0;
REQUIRE(target != NULL && *target == NULL);
REQUIRE((key == NULL) == (cert == NULL));
if (port == 0) {
port = tls ? named_g_httpsport : named_g_httpport;
}
CHECK(cfg_map_get(http, "endpoints", &eplist));
len = cfg_list_length(eplist, false);
endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len);
for (elt = cfg_list_first(eplist); elt != NULL;
elt = cfg_list_next(elt)) {
const cfg_obj_t *ep = cfg_listelt_value(elt);
const char *path = cfg_obj_asstring(ep);
endpoints[i++] = isc_mem_strdup(mctx, path);
}
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);
}
*target = delt;
cleanup:
return (result);
}
isc_result_t

View file

@ -0,0 +1,27 @@
/*
* 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";
};
http local-http-server {
endpoints { "/dns-query"; };
};
options {
listen-on { 10.53.0.1; };
http-port 80;
https-port 443;
listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; };
listen-on port 8080 http local-http-server { 10.53.0.1; };
};

View file

@ -0,0 +1,19 @@
/*
* 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";
};
options {
listen-on port 853 tls local-tls { 10.53.0.1; };
};

View file

@ -668,6 +668,8 @@ copy_setports() {
atsign="@"
sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \
-e "s/${atsign}TLSPORT${atsign}/${TLSPORT}/g" \
-e "s/${atsign}HTTPPORT${atsign}/${HTTPSPORT}/g" \
-e "s/${atsign}HTTPSPORT${atsign}/${HTTPSPORT}/g" \
-e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \
-e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \
-e "s/${atsign}EXTRAPORT3${atsign}/${EXTRAPORT3}/g" \

View file

@ -82,6 +82,8 @@ done
echo "export PORT=$(get_port "$baseport")"
echo "export TLSPORT=$(get_port)"
echo "export HTTPPORT=$(get_port)"
echo "export HTTPSPORT=$(get_port)"
echo "export EXTRAPORT1=$(get_port)"
echo "export EXTRAPORT2=$(get_port)"
echo "export EXTRAPORT3=$(get_port)"

View file

@ -149,7 +149,7 @@ stop_servers() {
echostart "S:$systest:$(date_with_args)"
echoinfo "T:$systest:1:A"
echoinfo "A:$systest:System test $systest"
echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}"
echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${HTTPPORT},${HTTPSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}"
$PERL ${srcdir}/testsock.pl -p "$PORT" || {
echowarn "I:$systest:Network interface aliases not set up. Skipping test."

View file

@ -138,7 +138,8 @@ const FileData installFiles[] =
{"libdns.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libirs.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libeay32.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"libuv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"nghttp2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
{"uv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#ifdef HAVE_LIBXML2
{"libxml2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE},
#endif

View file

@ -124,7 +124,7 @@ AS_IF([test "$enable_static" != "no" && test "$enable_developer" != "yes"],
#
# Set the default CFLAGS and CPPFLAGS
#
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wcast-qual -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"
STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow"
# These should be always errors
STD_CFLAGS="$STD_CFLAGS -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=format-security -Werror=parentheses -Werror=implicit -Werror=strict-prototypes"
@ -574,6 +574,15 @@ LIBS="$LIBS $LIBUV_LIBS"
AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import uv_udp_connect uv_translate_sys_error])
AX_RESTORE_FLAGS([libuv])
# libnghttp2
AC_MSG_CHECKING([for libnghttp2])
PKG_CHECK_MODULES([LIBNGHTTP2], [libnghttp2 >= 1.6.0], [],
[AC_MSG_ERROR([libnghttp2 not found])])
AX_SAVE_FLAGS([libnghttp2])
CFLAGS="$CFLAGS $LIBNGHTTP2_CFLAGS"
LIBS="$LIBS $LIBNGHTTP2_LIBS"
#
# flockfile is usually provided by pthreads
#

View file

@ -1256,6 +1256,20 @@ default is used.
for server testing; a server using a port other than 53 is not
able to communicate with the global DNS.
``tls-port``
This is the TCP port number the server uses to receive and send
DNS-over-TLS protocol traffic. The default is 853.
``https-port``
This is the TCP port number the server uses to receive and send
DNS-over-HTTPS protocol traffic. The default is 443.
``http-port``
This is the TCP port number the server uses to receive and send
unencrypted DNS traffic via HTTP (a configuration that may be useful
when encryption is handled by third-party software or by a reverse
proxy).
``dscp``
This is the global Differentiated Services Code Point (DSCP) value to
classify outgoing DNS traffic, on operating systems that support DSCP.
@ -2434,17 +2448,36 @@ for details on how to specify IP address lists.
Interfaces
^^^^^^^^^^
The interfaces and ports that the server answers queries from may be
specified using the ``listen-on`` and ``listen-on-v6`` options.
The interfaces, ports, and protocols that the server can use to answer
queries may be specified using the ``listen-on`` and ``listen-on-v6`` options.
``listen-on`` takes an optional port, an optional TLS configuration
identifier, and an ``address_match_list`` of IPv4 addresses. (IPv6
addresses are ignored, with a logged warning.) The server listens on all
interfaces allowed by the address match list. If a TLS configuration is
specified, ``named`` will listen for DNS-over-TLS (DoT) connections, using
the key and certificate specified in the referenced ``tls`` statement. If a
port number is not specified, the default is 53 for standard DNS and 853
for DNS-over-TLS.
``listen-on`` and ``listen-on-v6`` statements can each take an optional
port, TLS configuration identifier, and/or HTTP configuration identifier,
in addition to an ``address_match_list``.
The ``address_match_list`` in ``listen-on`` specifies the IPv4 addresses
on which the server will listen. (IPv6 addresses are ignored, with a
logged warning.) The server listens on all interfaces allowed by the
address match list. If no ``listen-on`` is specified, the default is
to listen for standard DNS queries on port 53 of all IPv4 interfaces.
``listen-on-v6`` takes an ``address_match_list`` of IPv6 addresses.
The server listens on all interfaces allowed by the address match list.
If no ``listen-on-v6`` is specified, the default is to listen for standard
DNS queries on port 53 of all IPv6 interfaces.
If a TLS configuration is specified, ``named`` will listen for DNS-over-TLS
(DoT) connections, using the key and certificate specified in the
referenced ``tls`` statement.
If an HTTP configuration is specified, ``named`` will listen for
DNS-over-HTTPS (DoH) connections using the HTTP endpoint specified in the
referenced ``http`` statement. Normally, ``http`` and ``tls``
configurations will be used together, but ``tls`` may be omitted if
encryption is being handled by external software.
If a port number is not specified, the default is 53 for standard DNS, 853
for DNS-over-TLS, and 443 for DNS-over-HTTPS.
Multiple ``listen-on`` statements are allowed. For example:
@ -2453,20 +2486,17 @@ Multiple ``listen-on`` statements are allowed. For example:
listen-on { 5.6.7.8; };
listen-on port 1234 { !1.2.3.4; 1.2/16; };
listen-on port 8853 tls ephemeral { 4.3.2.1; };
listen-on port 8453 tls ephemeral http myserver { 8.7.6.5; };
enables the name server to listen for standard DNS queries on port 53 of the
IP address 5.6.7.8 and on port 1234 of an address on the machine in net 1.2
that is not 1.2.3.4, and to listen for DNS-over-TLS connections on port
8853 of the IP address 4.3.2.1, using an ephemeral TLS key and certificate
created for the currently running ``named`` process.
If no ``listen-on`` is specified, the server listens for standard DNS
on port 53 of all IPv4 interfaces.
The ``listen-on-v6`` option is used to specify the interfaces and the ports
on which the server listens for incoming queries sent using IPv6. If not
specified, the server listens for standard DNS queries on port 53 of all
IPv6 interfaces.
The first two lines instruct the name server to listen for standard DNS
queries on port 53 of the IP address 5.6.7.8 and on port 1234 of an address
on the machine in net 1.2 that is not 1.2.3.4. The third line instructs the
server to listen for DNS-over-TLS connections on port 8853 of the IP
address 4.3.2.1 using an ephemeral TLS key and certificate created for the
currently running ``named`` process. The fourth line enables DNS-over-HTTPS
connections on port 8453 of address 8.7.6.5, using the same ephemeral
key and certificate, and the HTTP endpoint or endpoints configured in
an ``http`` statement with the name ``myserver``.
Multiple ``listen-on-v6`` options can be used. For example:
@ -2475,12 +2505,19 @@ Multiple ``listen-on-v6`` options can be used. For example:
listen-on-v6 { any; };
listen-on-v6 port 1234 { !2001:db8::/32; any; };
listen-on port 8853 tls example-tls { 2001:db8::100; };
listen-on port 8453 tls example-tls http myserver { 2001:db8::100; };
listen-on port 8000 http myserver { 2001:db8::100; };
enables the name server to listen for standard DNS queries on port 53 of
any IPv6 addresses and on port 1234 of IPv6 addresses that are not in the
prefix 2001:db8::/32, and for DNS-over-TLS connections on port 8853 of
the address 2001:db8::100, using a TLS key and certificate specified in
the a ``tls`` statement with the name ``example-tls``.
The first two lines instruct the name server to listen for standard DNS
queries on port 53 of any IPv6 addresses, and on port 1234 of IPv6
addresses that are not in the prefix 2001:db8::/32. The third line
instructs the server to listen for for DNS-over-TLS connections on port
8853 of the address 2001:db8::100, using a TLS key and certificate specified
in the a ``tls`` statement with the name ``example-tls``. The fourth
instructs the server to listen for DNS-over-HTTPS connections, again using
``example-tls``, on the HTTP endpoint specified in ``http myserver``. The
fifth line, in which the ``tls`` parameter is omitted, instructs the server
to listen for *unencrypted* DNS queries over HTTP.
To instruct the server not to listen on any IPv6 addresses, use:
@ -4624,6 +4661,45 @@ The following options can be specified in a ``tls`` statement:
The built-in ``ephemeral`` TLS connection object represents a temporary
key and certificate created for the current ``named`` session only.
.. _http:
``http`` Statement Grammar
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. include:: ../misc/http.grammar.rst
``http`` Statement Definition and Usage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``http`` statement is used to configure HTTP endpoints on which
to listen for DNS-over-HTTPS (DoH) queries. This configuration can
then be referenced by a ``listen-on`` or ``listen-on-v6`` statement to
cause ``named`` to listen for incoming requests over HTTPS.
``http`` can only be set at the top level of ``named.conf``.
The following options can be specified in an ``http`` statement:
``endpoints``
A list of HTTP query paths on which to listen. A typical path
is "/dns-query".
for example, the following configuration enables DNS-over-HTTPS queries on
all local addresses:
::
http local {
endpoints { "/dns-query"; };
};
options {
....
listen-on tls ephemeral http local { any; };
listen-on-v6 tls ephemeral http local { any; };
};
.. _trust_anchors:
``trust-anchors`` Statement Grammar

View file

@ -115,6 +115,10 @@ for queries. If \fBvalue\fP is of the form \fB<portnum>\fP or
\fBportnum\fP; if not not specified, the default is port 53. If
\fBvalue\fP is of the form \fBtls=<portnum>\fP, the server will
listen for TLS queries on \fBportnum\fP; the default is 853.
If \fBvalue\fP is of the form \fBhttps=<portnum>\fP, the server will
listen for HTTPS queries on \fBportnum\fP; the default is 443.
If \fBvalue\fP is of the form \fBhttp=<portnum>\fP, the server will
listen for HTTP queries on \fBportnum\fP; the default is 80.
.TP
.B \fB\-s\fP
This option writes memory usage statistics to \fBstdout\fP on exit.

View file

@ -132,6 +132,19 @@ dyndb string quoted_string {
.fi
.UNINDENT
.UNINDENT
.SS HTTP
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
http string {
endpoints { quoted_string; ... };
};
.ft P
.fi
.UNINDENT
.UNINDENT
.SS KEY
.INDENT 0.0
.INDENT 3.5
@ -327,6 +340,8 @@ options {
glue\-cache boolean;// deprecated
heartbeat\-interval integer;
hostname ( quoted_string | none );
http\-port integer;
https\-port integer;
inline\-signing boolean;
interface\-interval duration;
ipv4only\-contact string;
@ -338,10 +353,12 @@ options {
key\-directory quoted_string;
lame\-ttl duration;
listen\-on [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
listen\-on\-v6 [ port integer ] [ dscp
integer ] [ tls string ] {
integer ] [ tls string ] [ http
string ] {
address_match_element; ... };
lmdb\-mapsize sizeval;
lock\-file ( quoted_string | none );

View file

@ -37,7 +37,8 @@ OPTIONS_FILES = \
tls.grammar.rst \
trust-anchors.grammar.rst \
managed-keys.grammar.rst \
trusted-keys.grammar.rst
trusted-keys.grammar.rst \
http.grammar.rst
EXTRA_DIST = \
$(OPTIONS_FILES) \
@ -175,4 +176,7 @@ managed-keys.grammar.rst: options.active
trusted-keys.grammar.rst: options.active
$(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active trusted-keys > $@
http.grammar.rst: options.active
$(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active http > $@
endif

View file

@ -0,0 +1,5 @@
::
http <string> {
endpoints { <quoted_string>; ... };
};

View file

@ -42,6 +42,10 @@ dnssec-policy <string> {
dyndb <string> <quoted_string> {
<unspecified-text> }; // may occur multiple times
http <string> {
endpoints { <quoted_string>; ... };
}; // may occur multiple times
key <string> {
algorithm <string>;
secret <string>;
@ -193,6 +197,8 @@ options {
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
http-port <integer>;
https-port <integer>;
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
@ -204,10 +210,12 @@ options {
key-directory <quoted_string>;
lame-ttl <duration>;
listen-on [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... }; // may occur multiple times
listen-on-v6 [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>;
lock-file ( <quoted_string> | none );

View file

@ -41,6 +41,10 @@ dnssec-policy <string> {
dyndb <string> <quoted_string> {
<unspecified-text> }; // may occur multiple times
http <string> {
endpoints { <quoted_string>; ... };
}; // may occur multiple times
key <string> {
algorithm <string>;
secret <string>;
@ -192,6 +196,8 @@ options {
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
http-port <integer>;
https-port <integer>;
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
@ -203,10 +209,12 @@ options {
key-directory <quoted_string>;
lame-ttl <duration>;
listen-on [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... }; // may occur multiple times
listen-on-v6 [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>;
lock-file ( <quoted_string> | none );

View file

@ -119,6 +119,8 @@
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
http-port <integer>;
https-port <integer>;
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
@ -130,10 +132,12 @@
key-directory <quoted_string>;
lame-ttl <duration>;
listen-on [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... };
listen-on-v6 [ port <integer> ] [ dscp
<integer> ] [ tls <string> ] {
<integer> ] [ tls <string> ] [ http
<string> ] {
<address_match_element>; ... };
lmdb-mapsize <sizeval>;
lock-file ( <quoted_string> | none );

View file

@ -52,6 +52,13 @@ New Features
an optional ``tls`` option which specifies either a previously configured
``tls`` statement or ``ephemeral``. [GL #2392]
- ``named`` now supports DNS-over-HTTPS (DoH). Both TLS-encrypted and
unencrypted HTTP/2 connections are supported (the latter may be used to
offload encryption to other software).
Note that there is no client-side support for HTTPS as yet; this will be
added to ``dig`` in a future release. [GL #1144]
Removed Features
~~~~~~~~~~~~~~~~

View file

@ -90,6 +90,7 @@ libisc_la_HEADERS = \
include/isc/tls.h \
include/isc/tm.h \
include/isc/types.h \
include/isc/url.h \
include/isc/utf8.h \
include/isc/util.h \
pthreads/include/isc/condition.h\
@ -124,11 +125,13 @@ libisc_la_SOURCES = \
$(libisc_la_HEADERS) \
$(pk11_HEADERS) \
$(pkcs11_HEADERS) \
netmgr/http.c \
netmgr/netmgr-int.h \
netmgr/netmgr.c \
netmgr/tcp.c \
netmgr/tcpdns.c \
netmgr/tlsdns.c \
netmgr/tlsstream.c \
netmgr/udp.c \
netmgr/uv-compat.c \
netmgr/uv-compat.h \
@ -149,6 +152,7 @@ libisc_la_SOURCES = \
unix/stdtime.c \
unix/syslog.c \
unix/time.c \
url.c \
pk11.c \
pk11_result.c \
aes.c \

View file

@ -172,6 +172,8 @@ typedef struct isc_memmethods {
void *(*memreallocate)(isc_mem_t *mctx, void *ptr,
size_t size _ISC_MEM_FLARG);
char *(*memstrdup)(isc_mem_t *mctx, const char *s _ISC_MEM_FLARG);
char *(*memstrndup)(isc_mem_t *mctx, const char *s,
size_t size _ISC_MEM_FLARG);
void (*memfree)(isc_mem_t *mctx, void *ptr _ISC_MEM_FLARG);
} isc_memmethods_t;
@ -226,7 +228,9 @@ struct isc_mempool {
#define isc_mem_reallocate(c, p, s) \
ISCMEMFUNC(reallocate)((c), (p), (s)_ISC_MEM_FILELINE)
#define isc_mem_strdup(c, p) ISCMEMFUNC(strdup)((c), (p)_ISC_MEM_FILELINE)
#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c)_ISC_MEM_FILELINE)
#define isc_mem_strndup(c, p, l) \
ISCMEMFUNC(strndup)((c), (p), (l)_ISC_MEM_FILELINE)
#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c)_ISC_MEM_FILELINE)
#define isc_mem_put(c, p, s) \
do { \
@ -596,6 +600,7 @@ void *ISCMEMFUNC(allocate)(isc_mem_t *, size_t _ISC_MEM_FLARG);
void *ISCMEMFUNC(reallocate)(isc_mem_t *, void *, size_t _ISC_MEM_FLARG);
void ISCMEMFUNC(free)(isc_mem_t *, void *_ISC_MEM_FLARG);
char *ISCMEMFUNC(strdup)(isc_mem_t *, const char *_ISC_MEM_FLARG);
char *ISCMEMFUNC(strndup)(isc_mem_t *, const char *, size_t _ISC_MEM_FLARG);
void *ISCMEMPOOLFUNC(get)(isc_mempool_t *_ISC_MEM_FLARG);
void ISCMEMPOOLFUNC(put)(isc_mempool_t *, void *_ISC_MEM_FLARG);

View file

@ -473,6 +473,17 @@ isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats);
* full range of socket-related stats counter numbers.
*/
isc_result_t
isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
size_t extrahandlesize, int backlog, isc_quota_t *quota,
isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
isc_result_t
isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
unsigned int timeout, size_t extrahandlesize);
isc_result_t
isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
@ -494,3 +505,46 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
* The connected socket can only be accessed via the handle passed to
* 'cb'.
*/
typedef void (*isc_nm_http_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *data, void *cbarg);
/*%<
* Callback function to be used when receiving an HTTP request.
*
* 'handle' the handle that can be used to send back the answer.
* 'eresult' the result of the event.
* 'data' contains the received data, if any. It will be freed
* after return by caller.
* 'cbarg' the callback argument passed to listen function.
*/
isc_result_t
isc_nm_http_connect_send_request(isc_nm_t *mgr, const char *uri, bool POST,
isc_region_t *message, isc_nm_recv_cb_t cb,
void *cbarg, isc_tlsctx_t *ctx,
unsigned int timeout);
isc_result_t
isc_nm_httpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg,
isc_tlsctx_t *ctx, unsigned int timeout,
size_t extrahandlesize);
isc_result_t
isc_nm_httprequest(isc_nmhandle_t *handle, isc_region_t *region,
isc_nm_recv_cb_t reply_cb, void *cbarg);
isc_result_t
isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
isc_quota_t *quota, isc_tlsctx_t *ctx,
isc_nmsocket_t **sockp);
isc_result_t
isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri,
isc_nm_http_cb_t cb, void *cbarg,
size_t extrahandlesize);
isc_result_t
isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri,
isc_nm_recv_cb_t cb, void *cbarg,
size_t extrahandlesize);

80
lib/isc/include/isc/url.h Normal file
View file

@ -0,0 +1,80 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*
* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdint.h>
#include <isc/result.h>
/*
* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
#define HTTP_PARSER_STRICT 1
#endif
typedef enum {
ISC_UF_SCHEMA = 0,
ISC_UF_HOST = 1,
ISC_UF_PORT = 2,
ISC_UF_PATH = 3,
ISC_UF_QUERY = 4,
ISC_UF_FRAGMENT = 5,
ISC_UF_USERINFO = 6,
ISC_UF_MAX = 7
} isc_url_field_t;
/* Result structure for isc_url_parse().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
typedef struct {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[ISC_UF_MAX];
} isc_url_parser_t;
isc_result_t
isc_url_parse(const char *buf, size_t buflen, bool is_connect,
isc_url_parser_t *up);
/*%<
* Parse a URL; return nonzero on failure
*/

View file

@ -244,13 +244,15 @@ static void *
isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
static char *
isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
static char *
isc___mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG);
static void
isc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
static isc_memmethods_t memmethods = {
isc___mem_get, isc___mem_put, isc___mem_putanddetach,
isc___mem_allocate, isc___mem_reallocate, isc___mem_strdup,
isc___mem_free,
isc___mem_strndup, isc___mem_free,
};
#if ISC_MEM_TRACKLINES
@ -1439,6 +1441,29 @@ isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
return (ns);
}
char *
isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG) {
REQUIRE(VALID_CONTEXT(mctx0));
REQUIRE(s != NULL);
isc__mem_t *mctx = (isc__mem_t *)mctx0;
size_t len;
char *ns;
len = strlen(s) + 1;
if (len > size) {
len = size;
}
ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS);
if (ns != NULL) {
strlcpy(ns, s, len);
}
return (ns);
}
void
isc_mem_setdestroycheck(isc_mem_t *ctx0, bool flag) {
REQUIRE(VALID_CONTEXT(ctx0));
@ -2467,6 +2492,13 @@ isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
return (mctx->methods->memstrdup(mctx, s FLARG_PASS));
}
char *
isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
REQUIRE(ISCAPI_MCTX_VALID(mctx));
return (mctx->methods->memstrndup(mctx, s, size FLARG_PASS));
}
void
isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) {
REQUIRE(ISCAPI_MCTX_VALID(mctx));

2591
lib/isc/netmgr/http.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,7 @@
#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/rwlock.h>
#include <isc/sockaddr.h>
#include <isc/stats.h>
#include <isc/thread.h>
@ -155,6 +156,8 @@ isc__nm_dump_active(isc_nm_t *nm);
#define isc__nmsocket_prep_destroy(sock) isc___nmsocket_prep_destroy(sock)
#endif
typedef struct isc_nm_http2_session isc_nm_http2_session_t;
/*
* Single network event loop worker.
*/
@ -207,6 +210,8 @@ struct isc_nmhandle {
isc_nmsocket_t *sock;
size_t ah_pos; /* Position in the socket's 'active handles' array */
isc_nm_http2_session_t *httpsession;
isc_sockaddr_t peer;
isc_sockaddr_t local;
isc_nm_opaquecb_t doreset; /* reset extra callback, external */
@ -252,6 +257,13 @@ typedef enum isc__netievent_type {
netievent_tcpdnsclose,
netievent_tcpdnsstop,
netievent_tlsclose,
netievent_tlssend,
netievent_tlsstartread,
netievent_tlsconnect,
netievent_tlsdobio,
netievent_tlscancel,
netievent_tlsdnsaccept,
netievent_tlsdnsconnect,
netievent_tlsdnssend,
@ -262,6 +274,10 @@ typedef enum isc__netievent_type {
netievent_tlsdnscycle,
netievent_tlsdnsshutdown,
netievent_httpstop,
netievent_httpsend,
netievent_httpclose,
netievent_close,
netievent_shutdown,
netievent_stop,
@ -286,11 +302,22 @@ typedef enum isc__netievent_type {
typedef union {
isc_nm_recv_cb_t recv;
isc_nm_http_cb_t http;
isc_nm_cb_t send;
isc_nm_cb_t connect;
isc_nm_accept_cb_t accept;
} isc__nm_cb_t;
typedef struct isc_nm_http2_server_handler isc_nm_http2_server_handler_t;
struct isc_nm_http2_server_handler {
char *path;
isc_nm_http_cb_t cb;
void *cbarg;
size_t extrahandlesize;
LINK(isc_nm_http2_server_handler_t) link;
};
/*
* Wrapper around uv_req_t with 'our' fields in it. req->data should
* always point to its parent. Note that we always allocate more than
@ -642,8 +669,12 @@ typedef enum isc_nmsocket_type {
isc_nm_tcplistener,
isc_nm_tcpdnslistener,
isc_nm_tcpdnssocket,
isc_nm_tlslistener,
isc_nm_tlssocket,
isc_nm_tlsdnslistener,
isc_nm_tlsdnssocket
isc_nm_tlsdnssocket,
isc_nm_httplistener,
isc_nm_httpstream
} isc_nmsocket_type;
/*%
@ -670,12 +701,74 @@ enum {
STATID_ACTIVE = 10
};
typedef struct isc_nmsocket_tls_send_req {
isc_nmsocket_t *tlssock;
isc_region_t data;
} isc_nmsocket_tls_send_req_t;
typedef enum isc_doh_request_type {
ISC_HTTP_REQ_GET,
ISC_HTTP_REQ_POST,
ISC_HTTP_REQ_UNSUPPORTED
} isc_http2_request_type_t;
typedef enum isc_http2_scheme_type {
ISC_HTTP_SCHEME_HTTP,
ISC_HTTP_SCHEME_HTTP_SECURE,
ISC_HTTP_SCHEME_UNSUPPORTED
} isc_http2_scheme_type_t;
typedef struct isc_nm_http_doh_cbarg {
isc_nm_recv_cb_t cb;
void *cbarg;
LINK(struct isc_nm_http_doh_cbarg) link;
} isc_nm_http_doh_cbarg_t;
typedef struct isc_nmsocket_h2 {
isc_nmsocket_t *psock; /* owner of the structure */
char *request_path;
char *query_data;
size_t query_data_len;
bool query_too_large;
isc_nm_http2_server_handler_t *handler;
uint8_t *buf;
size_t bufsize;
size_t bufpos;
int32_t stream_id;
isc_nm_http2_session_t *session;
isc_nmsocket_t *httpserver;
isc_http2_request_type_t request_type;
isc_http2_scheme_type_t request_scheme;
size_t content_length;
bool content_type_verified;
bool accept_type_verified;
isc_nm_http_cb_t handler_cb;
void *handler_cbarg;
LINK(struct isc_nmsocket_h2) link;
ISC_LIST(isc_nm_http2_server_handler_t) handlers;
ISC_LIST(isc_nm_http_doh_cbarg_t) handlers_cbargs;
isc_rwlock_t handlers_lock;
char response_content_length_str[128];
struct isc_nmsocket_h2_connect_data {
char *uri;
bool post;
} connect;
} isc_nmsocket_h2_t;
struct isc_nmsocket {
/*% Unlocked, RO */
int magic;
int tid;
isc_nmsocket_type type;
isc_nm_t *mgr;
/*% Parent socket for multithreaded listeners */
isc_nmsocket_t *parent;
/*% Listener socket this connection was accepted on */
@ -705,6 +798,28 @@ struct isc_nmsocket {
isc__nm_uvreq_t *pending_req;
} tls;
/*% TLS stuff */
struct tlsstream {
bool server;
BIO *app_bio;
SSL *ssl;
SSL_CTX *ctx;
BIO *ssl_bio;
isc_nmsocket_t *tlslistener;
enum {
TLS_INIT,
TLS_HANDSHAKE,
TLS_IO,
TLS_ERROR,
TLS_CLOSING,
TLS_CLOSED
} state;
size_t nsending;
/* List of active send requests. */
ISC_LIST(isc__nm_uvreq_t) sends;
} tlsstream;
isc_nmsocket_h2_t h2;
/*%
* quota is the TCP client, attached when a TCP connection
* is established. pquota is a non-attached pointer to the
@ -906,6 +1021,7 @@ struct isc_nmsocket {
void *accept_cbarg;
atomic_int_fast32_t active_child_connections;
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
@ -1070,8 +1186,8 @@ isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0);
*/
void
isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg);
isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
/*%<
* Back-end implementation of isc_nm_send() for UDP handles.
*/
@ -1132,8 +1248,8 @@ isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0);
*/
void
isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg);
isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
/*%<
* Back-end implementation of isc_nm_send() for TCP handles.
*/
@ -1220,6 +1336,28 @@ isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0);
* stoplisten, send, read, pause, close).
*/
void
isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0);
/*%<
* Callback handlers for asynchronouse TLS events.
*/
void
isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
void
@ -1291,6 +1429,14 @@ isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
void
isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
void
isc__nm_tls_cancelread(isc_nmhandle_t *handle);
/*%<
* Back-end implementation of isc_nm_send() for TLSDNS handles.
*/
@ -1344,6 +1490,71 @@ isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle);
* Stop reading on a connected TLSDNS handle.
*/
void
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
void
isc__nm_tls_close(isc_nmsocket_t *sock);
/*%<
* Close a TLS socket.
*/
void
isc__nm_tls_pauseread(isc_nmhandle_t *handle);
/*%<
* Pause reading on this handle, while still remembering the callback.
*/
void
isc__nm_tls_resumeread(isc_nmhandle_t *handle);
/*%<
* Resume reading from the handle.
*
*/
void
isc__nm_tls_cleanup_data(isc_nmsocket_t *sock);
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
void
isc__nm_http_stoplistening(isc_nmsocket_t *sock);
void
isc__nm_http_clear_handlers(isc_nmsocket_t *sock);
void
isc__nm_http_clear_session(isc_nmsocket_t *sock);
void
isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg);
void
isc__nm_http_close(isc_nmsocket_t *sock);
void
isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0);
bool
isc__nm_parse_doh_query_string(const char *query_string, const char **start,
size_t *len);
char *
isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
const size_t base64url_len, size_t *res_len);
char *
isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
const size_t base64_len, size_t *res_len);
#define isc__nm_uverr2result(x) \
isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__)
isc_result_t
@ -1444,6 +1655,12 @@ NETIEVENT_SOCKET_TYPE(tcpclose);
NETIEVENT_SOCKET_TYPE(tcplisten);
NETIEVENT_SOCKET_TYPE(tcppauseread);
NETIEVENT_SOCKET_TYPE(tcpstop);
NETIEVENT_SOCKET_TYPE(tlsclose);
/* NETIEVENT_SOCKET_TYPE(tlsconnect); */ /* unique type, defined independently
*/
NETIEVENT_SOCKET_TYPE(tlsdobio);
NETIEVENT_SOCKET_TYPE(tlsstartread);
NETIEVENT_SOCKET_HANDLE_TYPE(tlscancel);
NETIEVENT_SOCKET_TYPE(udpclose);
NETIEVENT_SOCKET_TYPE(udplisten);
NETIEVENT_SOCKET_TYPE(udpread);
@ -1470,9 +1687,14 @@ NETIEVENT_SOCKET_HANDLE_TYPE(tlsdnscancel);
NETIEVENT_SOCKET_QUOTA_TYPE(tlsdnsaccept);
NETIEVENT_SOCKET_TYPE(tlsdnscycle);
NETIEVENT_SOCKET_TYPE(httpstop);
NETIEVENT_SOCKET_REQ_TYPE(httpsend);
NETIEVENT_SOCKET_TYPE(httpclose);
NETIEVENT_SOCKET_REQ_TYPE(tcpconnect);
NETIEVENT_SOCKET_REQ_TYPE(tcpsend);
NETIEVENT_SOCKET_TYPE(tcpstartread);
NETIEVENT_SOCKET_REQ_TYPE(tlssend);
NETIEVENT_SOCKET_REQ_TYPE(udpconnect);
NETIEVENT_SOCKET_REQ_RESULT_TYPE(connectcb);
@ -1498,6 +1720,11 @@ NETIEVENT_SOCKET_DECL(tcplisten);
NETIEVENT_SOCKET_DECL(tcppauseread);
NETIEVENT_SOCKET_DECL(tcpstartread);
NETIEVENT_SOCKET_DECL(tcpstop);
NETIEVENT_SOCKET_DECL(tlsclose);
NETIEVENT_SOCKET_DECL(tlsconnect);
NETIEVENT_SOCKET_DECL(tlsdobio);
NETIEVENT_SOCKET_DECL(tlsstartread);
NETIEVENT_SOCKET_HANDLE_DECL(tlscancel);
NETIEVENT_SOCKET_DECL(udpclose);
NETIEVENT_SOCKET_DECL(udplisten);
NETIEVENT_SOCKET_DECL(udpread);
@ -1524,8 +1751,13 @@ NETIEVENT_SOCKET_HANDLE_DECL(tlsdnscancel);
NETIEVENT_SOCKET_QUOTA_DECL(tlsdnsaccept);
NETIEVENT_SOCKET_DECL(tlsdnscycle);
NETIEVENT_SOCKET_DECL(httpstop);
NETIEVENT_SOCKET_REQ_DECL(httpsend);
NETIEVENT_SOCKET_DECL(httpclose);
NETIEVENT_SOCKET_REQ_DECL(tcpconnect);
NETIEVENT_SOCKET_REQ_DECL(tcpsend);
NETIEVENT_SOCKET_REQ_DECL(tlssend);
NETIEVENT_SOCKET_REQ_DECL(udpconnect);
NETIEVENT_SOCKET_REQ_RESULT_DECL(connectcb);

View file

@ -716,6 +716,13 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
NETIEVENT_CASE(tcpdnsread);
NETIEVENT_CASE(tcpdnsstop);
NETIEVENT_CASE(tlsstartread);
NETIEVENT_CASE(tlssend);
NETIEVENT_CASE(tlsclose);
NETIEVENT_CASE(tlsconnect);
NETIEVENT_CASE(tlsdobio);
NETIEVENT_CASE(tlscancel);
NETIEVENT_CASE(tlsdnscycle);
NETIEVENT_CASE(tlsdnsaccept);
NETIEVENT_CASE(tlsdnslisten);
@ -727,6 +734,10 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
NETIEVENT_CASE(tlsdnsstop);
NETIEVENT_CASE(tlsdnsshutdown);
NETIEVENT_CASE(httpstop);
NETIEVENT_CASE(httpsend);
NETIEVENT_CASE(httpclose);
NETIEVENT_CASE(connectcb);
NETIEVENT_CASE(readcb);
NETIEVENT_CASE(sendcb);
@ -776,6 +787,11 @@ NETIEVENT_SOCKET_DEF(tcplisten);
NETIEVENT_SOCKET_DEF(tcppauseread);
NETIEVENT_SOCKET_DEF(tcpstartread);
NETIEVENT_SOCKET_DEF(tcpstop);
NETIEVENT_SOCKET_DEF(tlsclose);
NETIEVENT_SOCKET_DEF(tlsconnect);
NETIEVENT_SOCKET_DEF(tlsdobio);
NETIEVENT_SOCKET_DEF(tlsstartread);
NETIEVENT_SOCKET_HANDLE_DEF(tlscancel);
NETIEVENT_SOCKET_DEF(udpclose);
NETIEVENT_SOCKET_DEF(udplisten);
NETIEVENT_SOCKET_DEF(udpread);
@ -802,8 +818,13 @@ NETIEVENT_SOCKET_QUOTA_DEF(tlsdnsaccept);
NETIEVENT_SOCKET_DEF(tlsdnscycle);
NETIEVENT_SOCKET_DEF(tlsdnsshutdown);
NETIEVENT_SOCKET_DEF(httpstop);
NETIEVENT_SOCKET_REQ_DEF(httpsend);
NETIEVENT_SOCKET_DEF(httpclose);
NETIEVENT_SOCKET_REQ_DEF(tcpconnect);
NETIEVENT_SOCKET_REQ_DEF(tcpsend);
NETIEVENT_SOCKET_REQ_DEF(tlssend);
NETIEVENT_SOCKET_REQ_DEF(udpconnect);
NETIEVENT_SOCKET_REQ_RESULT_DEF(connectcb);
@ -986,6 +1007,34 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) {
isc_mutex_destroy(&sock->lock);
isc_condition_destroy(&sock->cond);
isc_condition_destroy(&sock->scond);
isc__nm_tls_cleanup_data(sock);
if (sock->type == isc_nm_httplistener) {
isc__nm_http_clear_handlers(sock);
isc_rwlock_destroy(&sock->h2.handlers_lock);
}
if (sock->h2.request_path != NULL) {
isc_mem_free(sock->mgr->mctx, sock->h2.request_path);
sock->h2.request_path = NULL;
}
if (sock->h2.query_data != NULL) {
isc_mem_free(sock->mgr->mctx, sock->h2.query_data);
sock->h2.query_data = NULL;
}
if (sock->h2.connect.uri != NULL) {
isc_mem_free(sock->mgr->mctx, sock->h2.connect.uri);
sock->h2.query_data = NULL;
}
if (sock->h2.buf != NULL) {
isc_mem_free(sock->mgr->mctx, sock->h2.buf);
sock->h2.buf = NULL;
}
isc__nm_http_clear_session(sock);
#ifdef NETMGR_TRACE
LOCK(&sock->mgr->lock);
ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link);
@ -1094,9 +1143,15 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
case isc_nm_tcpdnssocket:
isc__nm_tcpdns_close(sock);
return;
case isc_nm_tlssocket:
isc__nm_tls_close(sock);
break;
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_close(sock);
return;
case isc_nm_httpstream:
isc__nm_http_close(sock);
return;
default:
break;
}
@ -1139,7 +1194,9 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) {
REQUIRE((*sockp)->type == isc_nm_udplistener ||
(*sockp)->type == isc_nm_tcplistener ||
(*sockp)->type == isc_nm_tcpdnslistener ||
(*sockp)->type == isc_nm_tlsdnslistener);
(*sockp)->type == isc_nm_tlsdnslistener ||
(*sockp)->type == isc_nm_tlslistener ||
(*sockp)->type == isc_nm_httplistener);
isc__nmsocket_detach(sockp);
}
@ -1202,6 +1259,8 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
case isc_nm_tcpdnslistener:
case isc_nm_tlsdnssocket:
case isc_nm_tlsdnslistener:
case isc_nm_httpstream:
case isc_nm_httplistener:
if (family == AF_INET) {
sock->statsindex = tcp4statsindex;
} else {
@ -1218,6 +1277,9 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
isc_condition_init(&sock->scond);
isc_refcount_init(&sock->references, 1);
memset(&sock->tlsstream, 0, sizeof(sock->tlsstream));
ISC_LIST_INIT(sock->tlsstream.sends);
NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %lu\n", sock,
isc_refcount_current(&sock->references));
@ -1228,6 +1290,28 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
atomic_store(&sock->active_child_connections, 0);
if (type == isc_nm_httplistener) {
ISC_LIST_INIT(sock->h2.handlers);
ISC_LIST_INIT(sock->h2.handlers_cbargs);
isc_rwlock_init(&sock->h2.handlers_lock, 0, 1);
}
sock->h2.session = NULL;
sock->h2.httpserver = NULL;
sock->h2.query_data = NULL;
sock->h2.query_data_len = 0;
sock->h2.query_too_large = false;
sock->h2.request_path = NULL;
sock->h2.request_type = ISC_HTTP_REQ_UNSUPPORTED;
sock->h2.request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED;
sock->h2.content_length = 0;
sock->h2.content_type_verified = false;
sock->h2.accept_type_verified = false;
sock->h2.handler_cb = NULL;
sock->h2.handler_cbarg = NULL;
sock->h2.connect.uri = NULL;
sock->h2.buf = NULL;
sock->magic = NMSOCK_MAGIC;
}
@ -1353,7 +1437,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
#endif
UNLOCK(&sock->lock);
if (sock->type == isc_nm_tcpsocket ||
if (sock->type == isc_nm_tcpsocket || sock->type == isc_nm_tlssocket ||
(sock->type == isc_nm_udpsocket && atomic_load(&sock->client)) ||
(sock->type == isc_nm_tcpdnssocket && atomic_load(&sock->client)) ||
(sock->type == isc_nm_tlsdnssocket && atomic_load(&sock->client)))
@ -1369,6 +1453,10 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
sock->statichandle = handle;
}
if (sock->type == isc_nm_httpstream) {
handle->httpsession = sock->h2.session;
}
return (handle);
}
@ -1390,6 +1478,7 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
return (handle->sock->type == isc_nm_tcpsocket ||
handle->sock->type == isc_nm_tcpdnssocket ||
handle->sock->type == isc_nm_tlssocket ||
handle->sock->type == isc_nm_tlsdnssocket);
}
@ -1667,9 +1756,15 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
case isc_nm_tcpdnssocket:
isc__nm_tcpdns_send(handle, region, cb, cbarg);
break;
case isc_nm_tlssocket:
isc__nm_tls_send(handle, region, cb, cbarg);
break;
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_send(handle, region, cb, cbarg);
break;
case isc_nm_httpstream:
isc__nm_http_send(handle, region, cb, cbarg);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
@ -1697,6 +1792,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
case isc_nm_tcpdnssocket:
isc__nm_tcpdns_read(handle, cb, cbarg);
break;
case isc_nm_tlssocket:
isc__nm_tls_read(handle, cb, cbarg);
break;
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_read(handle, cb, cbarg);
break;
@ -1723,6 +1821,9 @@ isc_nm_cancelread(isc_nmhandle_t *handle) {
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_cancelread(handle);
break;
case isc_nm_tlssocket:
isc__nm_tls_cancelread(handle);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
@ -1739,6 +1840,9 @@ isc_nm_pauseread(isc_nmhandle_t *handle) {
case isc_nm_tcpsocket:
isc__nm_tcp_pauseread(handle);
break;
case isc_nm_tlssocket:
isc__nm_tls_pauseread(handle);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
@ -1755,6 +1859,9 @@ isc_nm_resumeread(isc_nmhandle_t *handle) {
case isc_nm_tcpsocket:
isc__nm_tcp_resumeread(handle);
break;
case isc_nm_tlssocket:
isc__nm_tls_resumeread(handle);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
@ -1775,9 +1882,15 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
case isc_nm_tcplistener:
isc__nm_tcp_stoplistening(sock);
break;
case isc_nm_tlslistener:
isc__nm_tls_stoplistening(sock);
break;
case isc_nm_tlsdnslistener:
isc__nm_tlsdns_stoplistening(sock);
break;
case isc_nm_httplistener:
isc__nm_http_stoplistening(sock);
break;
default:
INSIST(0);
ISC_UNREACHABLE();
@ -2322,10 +2435,18 @@ nmsocket_type_totext(isc_nmsocket_type type) {
return ("isc_nm_tcpdnslistener");
case isc_nm_tcpdnssocket:
return ("isc_nm_tcpdnssocket");
case isc_nm_tlssocket:
return ("isc_nm_tlssocket");
case isc_nm_tlslistener:
return ("isc_nm_tlslistener");
case isc_nm_tlsdnslistener:
return ("isc_nm_tlsdnslistener");
case isc_nm_tlsdnssocket:
return ("isc_nm_tlsdnssocket");
case isc_nm_httplistener:
return ("isc_nm_httplistener");
case isc_nm_httpstream:
return ("isc_nm_httpstream");
default:
INSIST(0);
ISC_UNREACHABLE();

View file

@ -167,6 +167,27 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
REQUIRE(isc__nm_in_netthread());
REQUIRE(sock->tid == isc_nm_tid());
result = isc__nm_socket(req->peer.type.sa.sa_family, SOCK_STREAM, 0,
&sock->fd);
/*
* The socket() call can fail spuriously on FreeBSD 12, so we need to
* handle the failure early and gracefully.
*/
if (result != ISC_R_SUCCESS) {
atomic_store(&sock->closed, true);
isc__nm_uvreq_t *cbreq = NULL;
cbreq = isc__nm_uvreq_get(sock->mgr, sock);
cbreq->cb.connect = req->cb.connect;
cbreq->cbarg = req->cbarg;
isc_nmhandle_attach(req->handle, &cbreq->handle);
isc__nmsocket_clearcb(sock);
isc__nm_connectcb(sock, cbreq, result);
goto error;
}
result = isc__nm_socket_connectiontimeout(sock->fd,
sock->connect_timeout);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
worker = &sock->mgr->workers[sock->tid];
atomic_store(&sock->connecting, true);
@ -210,7 +231,7 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
done:
result = isc__nm_uverr2result(r);
error:
LOCK(&sock->lock);
sock->result = result;
SIGNAL(&sock->cond);
@ -239,10 +260,13 @@ isc__nm_async_tcpconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
REQUIRE(sock->parent == NULL);
REQUIRE(sock->tid == isc_nm_tid());
sock->fd = (uv_os_sock_t)(-1);
result = tcp_connect_direct(sock, req);
if (result != ISC_R_SUCCESS) {
atomic_store(&sock->active, false);
isc__nm_tcp_close(sock);
if (sock->fd != (uv_os_sock_t)(-1)) {
isc__nm_tcp_close(sock);
}
isc__nm_uvreq_put(&req, sock);
}
@ -309,36 +333,19 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nmsocket_t *sock = NULL;
isc__netievent_tcpconnect_t *ievent = NULL;
isc__nm_uvreq_t *req = NULL;
sa_family_t sa_family;
uv_os_sock_t fd;
REQUIRE(VALID_NM(mgr));
REQUIRE(local != NULL);
REQUIRE(peer != NULL);
sa_family = peer->addr.type.sa.sa_family;
/*
* The socket() call can fail spuriously on FreeBSD 12, so we need to
* handle the failure early and gracefully.
*/
result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &fd);
if (result != ISC_R_SUCCESS) {
return (result);
}
sock = isc_mem_get(mgr->mctx, sizeof(*sock));
isc__nmsocket_init(sock, mgr, isc_nm_tcpsocket, local);
sock->extrahandlesize = extrahandlesize;
sock->connect_timeout = timeout;
sock->result = ISC_R_DEFAULT;
sock->fd = fd;
atomic_init(&sock->client, true);
result = isc__nm_socket_connectiontimeout(fd, timeout);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
req = isc__nm_uvreq_get(mgr, sock);
req->cb.connect = cb;
req->cbarg = cbarg;
@ -1155,8 +1162,8 @@ failure:
}
void
isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg) {
isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));

936
lib/isc/netmgr/tlsstream.c Normal file
View file

@ -0,0 +1,936 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <libgen.h>
#include <unistd.h>
#include <uv.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
#include <isc/condition.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/netmgr.h>
#include <isc/once.h>
#include <isc/quota.h>
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/sockaddr.h>
#include <isc/stdtime.h>
#include <isc/thread.h>
#include <isc/util.h>
#include "netmgr-int.h"
#include "uv-compat.h"
#define TLS_BUF_SIZE 65536
static isc_result_t
tls_error_to_result(int tls_err) {
switch (tls_err) {
case SSL_ERROR_ZERO_RETURN:
return (ISC_R_EOF);
default:
return (ISC_R_UNEXPECTED);
}
}
static void
tls_do_bio(isc_nmsocket_t *sock);
static void
tls_close_direct(isc_nmsocket_t *sock);
static void
async_tls_do_bio(isc_nmsocket_t *sock);
/*
* The socket is closing, outerhandle has been detached, listener is
* inactive, or the netmgr is closing: any operation on it should abort
* with ISC_R_CANCELED.
*/
static bool
inactive(isc_nmsocket_t *sock) {
return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
sock->outerhandle == NULL ||
(sock->listener != NULL &&
!isc__nmsocket_active(sock->listener)) ||
atomic_load(&sock->mgr->closing));
}
static void
update_result(isc_nmsocket_t *sock, const isc_result_t result) {
LOCK(&sock->lock);
sock->result = result;
SIGNAL(&sock->cond);
if (!atomic_load(&sock->active)) {
WAIT(&sock->scond, &sock->lock);
}
UNLOCK(&sock->lock);
if (sock->parent) {
LOCK(&sock->parent->lock);
sock->parent->result = result;
UNLOCK(&sock->parent->lock);
}
}
static void
tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmsocket_tls_send_req_t *send_req =
(isc_nmsocket_tls_send_req_t *)cbarg;
isc_nmsocket_t *sock = send_req->tlssock;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
REQUIRE(VALID_NMSOCK(sock));
/* XXXWPK TODO */
UNUSED(eresult);
isc_mem_put(handle->sock->mgr->mctx, send_req->data.base,
send_req->data.length);
isc_mem_put(handle->sock->mgr->mctx, send_req, sizeof(*send_req));
sock->tlsstream.nsending--;
async_tls_do_bio(sock);
isc__nmsocket_detach(&sock);
}
static void
tls_failed_read_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
const isc_result_t result, const bool close) {
REQUIRE(VALID_NMSOCK(sock));
if (!sock->tlsstream.server &&
(sock->tlsstream.state == TLS_INIT ||
sock->tlsstream.state == TLS_HANDSHAKE) &&
sock->connect_cb != NULL)
{
INSIST(handle == NULL);
handle = isc__nmhandle_get(sock, NULL, NULL);
sock->connect_cb(handle, result, sock->connect_cbarg);
update_result(sock, result);
isc__nmsocket_clearcb(sock);
isc_nmhandle_detach(&handle);
} else if (sock->recv_cb != NULL) {
isc__nm_uvreq_t *req = NULL;
req = isc__nm_uvreq_get(sock->mgr, sock);
req->cb.recv = sock->recv_cb;
req->cbarg = sock->recv_cbarg;
req->handle = NULL;
if (handle) {
REQUIRE(VALID_NMHANDLE(handle));
isc_nmhandle_attach(handle, &req->handle);
} else {
req->handle = isc__nmhandle_get(sock, NULL, NULL);
}
isc__nmsocket_clearcb(sock);
isc__nm_readcb(sock, req, result);
}
sock->tlsstream.state = TLS_ERROR;
if (close) {
isc__nmsocket_prep_destroy(sock);
}
}
static void
async_tls_do_bio(isc_nmsocket_t *sock) {
isc__netievent_tlsdobio_t *ievent =
isc__nm_get_netievent_tlsdobio(sock->mgr, sock);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
static void
tls_do_bio(isc_nmsocket_t *sock) {
isc_result_t result = ISC_R_SUCCESS;
int pending, tls_err = 0;
int rv;
isc__nm_uvreq_t *req;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_nm_tid());
/* We will resume read if TLS layer wants us to */
if (sock->outerhandle != NULL) {
REQUIRE(VALID_NMHANDLE(sock->outerhandle));
isc_nm_pauseread(sock->outerhandle);
}
if (sock->tlsstream.state == TLS_INIT) {
(void)SSL_do_handshake(sock->tlsstream.ssl);
sock->tlsstream.state = TLS_HANDSHAKE;
} else if (sock->tlsstream.state == TLS_ERROR) {
result = ISC_R_FAILURE;
goto low_level_error;
} else if (sock->tlsstream.state == TLS_CLOSED) {
return;
}
/* Data from TLS to client */
char buf[1];
if (sock->tlsstream.state == TLS_IO && sock->recv_cb != NULL &&
!atomic_load(&sock->readpaused))
{
(void)SSL_peek(sock->tlsstream.ssl, buf, 1);
while ((pending = SSL_pending(sock->tlsstream.ssl)) > 0) {
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
isc_region_t region = {
isc_mem_get(sock->mgr->mctx, pending), pending
};
isc_region_t dregion;
memset(region.base, 0, region.length);
rv = SSL_read(sock->tlsstream.ssl, region.base,
region.length);
/* Pending succeded, so should read */
RUNTIME_CHECK(rv == pending);
dregion = (isc_region_t){ region.base, rv };
sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
&dregion, sock->recv_cbarg);
isc_mem_put(sock->mgr->mctx, region.base,
region.length);
}
}
/* Peek to move the session forward */
(void)SSL_peek(sock->tlsstream.ssl, buf, 1);
/* Data from TLS to network */
pending = BIO_pending(sock->tlsstream.app_bio);
if (pending > 0) {
/*TODO Should we keep the track of these requests in a list? */
isc_nmsocket_tls_send_req_t *send_req = NULL;
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
send_req = isc_mem_get(sock->mgr->mctx, sizeof(*send_req));
send_req->data.base = isc_mem_get(sock->mgr->mctx, pending);
send_req->data.length = pending;
send_req->tlssock = NULL;
isc__nmsocket_attach(sock, &send_req->tlssock);
rv = BIO_read(sock->tlsstream.app_bio, send_req->data.base,
pending);
/* There's something pending, read must succeed */
RUNTIME_CHECK(rv == pending);
INSIST(VALID_NMHANDLE(sock->outerhandle));
isc_nm_send(sock->outerhandle, &send_req->data, tls_senddone,
send_req);
/* We'll continue in tls_senddone */
return;
}
/* Get the potential error code */
rv = SSL_peek(sock->tlsstream.ssl, buf, 1);
if (rv < 0) {
tls_err = SSL_get_error(sock->tlsstream.ssl, rv);
}
/* Only after doing the IO we can check if SSL handshake is done */
if (sock->tlsstream.state == TLS_HANDSHAKE &&
SSL_is_init_finished(sock->tlsstream.ssl) == 1)
{
isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
if (sock->tlsstream.server) {
sock->listener->accept_cb(sock->statichandle,
ISC_R_SUCCESS,
sock->listener->accept_cbarg);
} else {
sock->connect_cb(tlshandle, ISC_R_SUCCESS,
sock->connect_cbarg);
update_result(tlshandle->sock, ISC_R_SUCCESS);
}
isc_nmhandle_detach(&tlshandle);
sock->tlsstream.state = TLS_IO;
async_tls_do_bio(sock);
return;
}
switch (tls_err) {
case 0:
return;
case SSL_ERROR_WANT_WRITE:
if (sock->tlsstream.nsending == 0) {
/*
* Launch tls_do_bio asynchronously. If we're sending
* already the send callback will call it.
*/
async_tls_do_bio(sock);
} else {
return;
}
break;
case SSL_ERROR_WANT_READ:
INSIST(VALID_NMHANDLE(sock->outerhandle));
isc_nm_resumeread(sock->outerhandle);
break;
default:
result = tls_error_to_result(tls_err);
goto error;
}
while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) != NULL) {
INSIST(VALID_UVREQ(req));
rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base,
req->uvbuf.len);
if (rv < 0) {
if (sock->tlsstream.nsending == 0) {
async_tls_do_bio(sock);
}
return;
}
if (rv != (int)req->uvbuf.len) {
if (!sock->tlsstream.server &&
(sock->tlsstream.state == TLS_HANDSHAKE ||
TLS_INIT))
{
isc_nmhandle_t *tlshandle =
isc__nmhandle_get(sock, NULL, NULL);
sock->connect_cb(tlshandle, result,
sock->connect_cbarg);
update_result(tlshandle->sock, result);
isc_nmhandle_detach(&tlshandle);
}
sock->tlsstream.state = TLS_ERROR;
async_tls_do_bio(sock);
return;
}
ISC_LIST_UNLINK(sock->tlsstream.sends, req, link);
req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
}
return;
error:
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_ERROR, "SSL error in BIO: %d %s", tls_err,
isc_result_totext(result));
low_level_error:
if (sock->tlsstream.state == TLS_HANDSHAKE) {
isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
if (!sock->tlsstream.server) {
sock->connect_cb(tlshandle, result,
sock->connect_cbarg);
update_result(tlshandle->sock, result);
}
isc_nmhandle_detach(&tlshandle);
} else if (sock->tlsstream.state == TLS_IO) {
if (ISC_LIST_HEAD(sock->tlsstream.sends) != NULL) {
while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) !=
NULL) {
req->cb.send(sock->statichandle, result,
req->cbarg);
ISC_LIST_UNLINK(sock->tlsstream.sends, req,
link);
isc__nm_uvreq_put(&req, sock);
}
} else if (sock->recv_cb != NULL) {
tls_failed_read_cb(sock, sock->statichandle, result,
false);
} else {
tls_close_direct(sock);
}
}
sock->tlsstream.state = TLS_ERROR;
}
static void
tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
int rv;
REQUIRE(VALID_NMSOCK(tlssock));
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(tlssock->tid == isc_nm_tid());
if (result != ISC_R_SUCCESS) {
tls_failed_read_cb(tlssock, tlssock->statichandle, result,
true);
return;
}
rv = BIO_write(tlssock->tlsstream.app_bio, region->base,
region->length);
if (rv != (int)region->length) {
/* XXXWPK log it? */
tlssock->tlsstream.state = TLS_ERROR;
}
tls_do_bio(tlssock);
}
static isc_result_t
initialize_tls(isc_nmsocket_t *sock, bool server) {
REQUIRE(sock->tid == isc_nm_tid());
if (BIO_new_bio_pair(&(sock->tlsstream.ssl_bio), TLS_BUF_SIZE,
&(sock->tlsstream.app_bio), TLS_BUF_SIZE) != 1)
{
SSL_free(sock->tlsstream.ssl);
return (ISC_R_TLSERROR);
}
SSL_set_bio(sock->tlsstream.ssl, sock->tlsstream.ssl_bio,
sock->tlsstream.ssl_bio);
if (server) {
SSL_set_accept_state(sock->tlsstream.ssl);
} else {
SSL_set_connect_state(sock->tlsstream.ssl);
}
sock->tlsstream.nsending = 0;
isc_nm_read(sock->outerhandle, tls_readcb, sock);
tls_do_bio(sock);
return (ISC_R_SUCCESS);
}
static isc_result_t
tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
isc_nmsocket_t *tlssock = NULL;
int r;
/* If accept() was unsuccessful we can't do anything */
if (result != ISC_R_SUCCESS) {
return (result);
}
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
REQUIRE(VALID_NMSOCK(tlslistensock));
REQUIRE(tlslistensock->type == isc_nm_tlslistener);
/*
* We need to create a 'wrapper' tlssocket for this connection.
*/
tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
handle->sock->iface);
/* We need to initialize SSL now to reference SSL_CTX properly */
tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx;
tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx);
ISC_LIST_INIT(tlssock->tlsstream.sends);
if (tlssock->tlsstream.ssl == NULL) {
update_result(tlssock, ISC_R_TLSERROR);
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (ISC_R_TLSERROR);
}
tlssock->extrahandlesize = tlslistensock->extrahandlesize;
isc__nmsocket_attach(tlslistensock, &tlssock->listener);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
tlssock->peer = handle->sock->peer;
tlssock->read_timeout = atomic_load(&handle->sock->mgr->init);
tlssock->tid = isc_nm_tid();
tlssock->tlsstream.server = true;
tlssock->tlsstream.state = TLS_INIT;
r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
RUNTIME_CHECK(r == 0);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx;
result = initialize_tls(tlssock, true);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
/* TODO: catch failure code, detach tlssock, and log the error */
return (result);
}
isc_result_t
isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
size_t extrahandlesize, int backlog, isc_quota_t *quota,
SSL_CTX *sslctx, isc_nmsocket_t **sockp) {
isc_result_t result;
isc_nmsocket_t *tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock));
isc_nmsocket_t *tsock = NULL;
REQUIRE(VALID_NM(mgr));
isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
tlssock->result = ISC_R_DEFAULT;
tlssock->accept_cb = accept_cb;
tlssock->accept_cbarg = accept_cbarg;
tlssock->extrahandlesize = extrahandlesize;
tlssock->tlsstream.ctx = sslctx;
tlssock->tlsstream.ssl = NULL;
/*
* tlssock will be a TLS 'wrapper' around an unencrypted stream.
* We set tlssock->outer to a socket listening for a TCP connection.
*/
result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock,
extrahandlesize, backlog, quota,
&tlssock->outer);
if (result != ISC_R_SUCCESS) {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (result);
}
/* wait for listen result */
isc__nmsocket_attach(tlssock->outer, &tsock);
LOCK(&tlssock->outer->lock);
while (tlssock->outer->rchildren != tlssock->outer->nchildren) {
WAIT(&tlssock->outer->cond, &tlssock->outer->lock);
}
result = tlssock->outer->result;
tlssock->result = result;
atomic_store(&tlssock->active, true);
INSIST(tlssock->outer->tlsstream.tlslistener == NULL);
isc__nmsocket_attach(tlssock, &tlssock->outer->tlsstream.tlslistener);
BROADCAST(&tlssock->outer->scond);
UNLOCK(&tlssock->outer->lock);
isc__nmsocket_detach(&tsock);
INSIST(result != ISC_R_DEFAULT);
if (result == ISC_R_SUCCESS) {
atomic_store(&tlssock->listening, true);
*sockp = tlssock;
}
return (result);
}
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
int rv;
isc__netievent_tlssend_t *ievent = (isc__netievent_tlssend_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc__nm_uvreq_t *req = ievent->req;
ievent->req = NULL;
REQUIRE(VALID_UVREQ(req));
REQUIRE(sock->tid == isc_nm_tid());
UNUSED(worker);
if (inactive(sock)) {
req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
isc__nm_uvreq_put(&req, sock);
return;
}
if (!ISC_LIST_EMPTY(sock->tlsstream.sends)) {
/* We're not the first */
ISC_LIST_APPEND(sock->tlsstream.sends, req, link);
tls_do_bio(sock);
return;
}
rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base, req->uvbuf.len);
if (rv < 0) {
/*
* We might need to read, we might need to write, or the
* TLS socket might be dead - in any case, we need to
* enqueue the uvreq and let the TLS BIO layer do the rest.
*/
ISC_LIST_APPEND(sock->tlsstream.sends, req, link);
tls_do_bio(sock);
return;
}
if (rv != (int)req->uvbuf.len) {
sock->tlsstream.state = TLS_ERROR;
async_tls_do_bio(sock);
return;
}
req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
tls_do_bio(sock);
return;
}
void
isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg) {
isc__netievent_tlssend_t *ievent = NULL;
isc__nm_uvreq_t *uvreq = NULL;
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_tlssocket);
if (inactive(sock)) {
cb(handle, ISC_R_CANCELED, cbarg);
return;
}
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
uvreq->uvbuf.base = (char *)region->base;
uvreq->uvbuf.len = region->length;
/*
* We need to create an event and pass it using async channel
*/
ievent = isc__nm_get_netievent_tlssend(sock->mgr, sock, uvreq);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
void
isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsstartread_t *ievent =
(isc__netievent_tlsstartread_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
REQUIRE(sock->tid == isc_nm_tid());
UNUSED(worker);
tls_do_bio(sock);
}
void
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
REQUIRE(handle->sock->statichandle == handle);
REQUIRE(handle->sock->tid == isc_nm_tid());
isc__netievent_tlsstartread_t *ievent = NULL;
isc_nmsocket_t *sock = handle->sock;
if (inactive(sock)) {
cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
return;
}
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
ievent = isc__nm_get_netievent_tlsstartread(sock->mgr, sock);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
void
isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
isc_nmsocket_t *sock = handle->sock;
atomic_store(&sock->readpaused, true);
}
void
isc__nm_tls_resumeread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
isc_nmsocket_t *sock = handle->sock;
atomic_store(&sock->readpaused, false);
async_tls_do_bio(sock);
}
static void
timer_close_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
tls_close_direct(sock);
}
static void
tls_close_direct(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->tid == isc_nm_tid());
/* if (!sock->tlsstream.server) { */
/* INSIST(sock->tlsstream.state != TLS_HANDSHAKE && */
/* sock->tlsstream.state != TLS_INIT); */
/* } */
sock->tlsstream.state = TLS_CLOSING;
if (sock->timer_running) {
uv_timer_stop(&sock->timer);
sock->timer_running = false;
}
/* We don't need atomics here, it's all in single network thread
*/
if (sock->timer_initialized) {
/*
* We need to fire the timer callback to clean it up,
* it will then call us again (via detach) so that we
* can finally close the socket.
*/
sock->timer_initialized = false;
uv_timer_stop(&sock->timer);
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
} else {
/*
* At this point we're certain that there are no
* external references, we can close everything.
*/
if (sock->outerhandle != NULL) {
isc_nm_pauseread(sock->outerhandle);
isc_nmhandle_detach(&sock->outerhandle);
}
if (sock->listener != NULL) {
isc__nmsocket_detach(&sock->listener);
}
if (sock->tlsstream.ssl != NULL) {
SSL_free(sock->tlsstream.ssl);
sock->tlsstream.ssl = NULL;
/* These are destroyed when we free SSL* */
sock->tlsstream.ctx = NULL;
sock->tlsstream.ssl_bio = NULL;
}
if (sock->tlsstream.app_bio != NULL) {
BIO_free(sock->tlsstream.app_bio);
sock->tlsstream.app_bio = NULL;
}
sock->tlsstream.state = TLS_CLOSED;
atomic_store(&sock->closed, true);
isc__nmsocket_detach(&sock);
}
}
void
isc__nm_tls_close(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
true)) {
return;
}
if (sock->tid == isc_nm_tid()) {
tls_close_direct(sock);
} else {
isc__netievent_tlsclose_t *ievent =
isc__nm_get_netievent_tlsclose(sock->mgr, sock);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
}
void
isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0;
REQUIRE(ievent->sock->tid == isc_nm_tid());
UNUSED(worker);
tls_close_direct(ievent->sock);
}
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlslistener);
atomic_store(&sock->listening, false);
atomic_store(&sock->closed, true);
sock->recv_cb = NULL;
sock->recv_cbarg = NULL;
if (sock->tlsstream.ssl != NULL) {
SSL_free(sock->tlsstream.ssl);
sock->tlsstream.ssl = NULL;
sock->tlsstream.ctx = NULL;
}
if (sock->outer != NULL) {
isc_nm_stoplistening(sock->outer);
isc__nmsocket_detach(&sock->outer);
}
}
isc_result_t
isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
unsigned int timeout, size_t extrahandlesize) {
isc_nmsocket_t *nsock = NULL, *tsock = NULL;
isc__netievent_tlsconnect_t *ievent = NULL;
isc_result_t result = ISC_R_DEFAULT;
REQUIRE(VALID_NM(mgr));
nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
nsock->extrahandlesize = extrahandlesize;
nsock->result = ISC_R_DEFAULT;
nsock->connect_cb = cb;
nsock->connect_cbarg = cbarg;
nsock->connect_timeout = timeout;
nsock->tlsstream.ctx = ctx;
ievent = isc__nm_get_netievent_tlsconnect(mgr, nsock);
ievent->local = local->addr;
ievent->peer = peer->addr;
ievent->ctx = ctx;
isc__nmsocket_attach(nsock, &tsock);
if (isc__nm_in_netthread()) {
nsock->tid = isc_nm_tid();
isc__nm_async_tlsconnect(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
isc__nm_put_netievent_tlsconnect(mgr, ievent);
} else {
nsock->tid = isc_random_uniform(mgr->nworkers);
isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
}
LOCK(&nsock->lock);
result = nsock->result;
while (result == ISC_R_DEFAULT) {
WAIT(&nsock->cond, &nsock->lock);
result = nsock->result;
}
atomic_store(&nsock->active, true);
BROADCAST(&nsock->scond);
UNLOCK(&nsock->lock);
INSIST(VALID_NMSOCK(nsock));
isc__nmsocket_detach(&tsock);
INSIST(result != ISC_R_DEFAULT);
return (result);
}
static void
tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
REQUIRE(VALID_NMSOCK(tlssock));
if (result != ISC_R_SUCCESS) {
tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
update_result(tlssock, result);
tls_close_direct(tlssock);
return;
}
INSIST(VALID_NMHANDLE(handle));
tlssock->peer = isc_nmhandle_peeraddr(handle);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
result = initialize_tls(tlssock, false);
if (result != ISC_R_SUCCESS) {
tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
update_result(tlssock, result);
tls_close_direct(tlssock);
return;
}
}
void
isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsconnect_t *ievent =
(isc__netievent_tlsconnect_t *)ev0;
isc_nmsocket_t *tlssock = ievent->sock;
isc_result_t result;
int r;
isc_nmhandle_t *tlshandle = NULL;
UNUSED(worker);
/*
* We need to initialize SSL now to reference SSL_CTX properly.
*/
tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx);
if (tlssock->tlsstream.ssl == NULL) {
result = ISC_R_TLSERROR;
goto error;
}
tlssock->tid = isc_nm_tid();
r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
RUNTIME_CHECK(r == 0);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
tlssock->tlsstream.state = TLS_INIT;
result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
(isc_nmiface_t *)&ievent->peer,
tls_connect_cb, tlssock,
tlssock->connect_timeout, 0);
if (result != ISC_R_SUCCESS) {
goto error;
}
return;
error:
tlshandle = isc__nmhandle_get(tlssock, NULL, NULL);
atomic_store(&tlssock->closed, true);
tlssock->connect_cb(tlshandle, result, tlssock->connect_cbarg);
isc_nmhandle_detach(&tlshandle);
update_result(tlssock, result);
tls_close_direct(tlssock);
}
void
isc__nm_tls_cancelread(isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
isc__netievent_tlscancel_t *ievent = NULL;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_tlssocket);
ievent = isc__nm_get_netievent_tlscancel(sock->mgr, sock, handle);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
void
isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlscancel_t *ievent = (isc__netievent_tlscancel_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc_nmhandle_t *handle = ievent->handle;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(worker->id == sock->tid);
REQUIRE(sock->tid == isc_nm_tid());
UNUSED(worker);
tls_failed_read_cb(sock, handle, ISC_R_EOF, false);
if (sock->outerhandle) {
isc__nm_tcp_cancelread(sock->outerhandle);
}
}
void
isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0) {
UNUSED(worker);
isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0;
tls_do_bio(ievent->sock);
}
void
isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) {
if (sock->tlsstream.tlslistener) {
REQUIRE(VALID_NMSOCK(sock->tlsstream.tlslistener));
isc__nmsocket_detach(&sock->tlsstream.tlslistener);
}
}

View file

@ -478,8 +478,8 @@ free:
* another thread.
*/
void
isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg) {
isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = handle->sock;
isc_nmsocket_t *psock = NULL, *rsock = sock;
isc_sockaddr_t *peer = &handle->peer;

View file

@ -20,6 +20,7 @@ TESTS = \
buffer_test \
counter_test \
crc64_test \
doh_test \
errno_test \
file_test \
hash_test \
@ -44,17 +45,27 @@ TESTS = \
symtab_test \
task_test \
taskpool_test \
tcp_test \
tcp_quota_test \
tcp_test \
tcpdns_test \
tlsdns_test \
time_test \
timer_test \
tlsdns_test \
udp_test
check_PROGRAMS = \
$(TESTS)
doh_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
doh_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS) \
$(OPENSSL_LIBS)
hmac_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS)
@ -77,8 +88,8 @@ random_test_LDADD = \
tcp_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBUV_CFLAGS)
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcp_test_LDADD = \
$(LDADD) \
@ -86,8 +97,8 @@ tcp_test_LDADD = \
tcp_quota_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBUV_CFLAGS)
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcp_quota_test_LDADD = \
$(LDADD) \
@ -95,8 +106,8 @@ tcp_quota_test_LDADD = \
tcpdns_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBUV_CFLAGS)
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcpdns_test_LDADD = \
$(LDADD) \
@ -104,8 +115,8 @@ tcpdns_test_LDADD = \
tlsdns_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBUV_CFLAGS)
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tlsdns_test_LDADD = \
$(LDADD) \
@ -113,8 +124,8 @@ tlsdns_test_LDADD = \
udp_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(OPENSSL_CFLAGS) \
$(LIBUV_CFLAGS)
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
udp_test_LDADD = \
$(LDADD) \

1771
lib/isc/tests/doh_test.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -127,8 +127,10 @@ isc_tlsctx_createclient(isc_tlsctx_t **ctxp) {
#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
#else
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
SSL_CTX_set_options(
ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
SSL_OP_NO_TLSv1_1 | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
#endif
*ctxp = ctx;

667
lib/isc/url.c Normal file
View file

@ -0,0 +1,667 @@
/*
* 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 https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*
* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <isc/url.h>
#include <isc/util.h>
#ifndef BIT_AT
#define BIT_AT(a, i) \
(!!((unsigned int)(a)[(unsigned int)(i) >> 3] & \
(1 << ((unsigned int)(i)&7))))
#endif
#if HTTP_PARSER_STRICT
#define T(v) 0
#else
#define T(v) v
#endif
static const uint8_t normal_url_char[32] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
};
#undef T
typedef enum {
s_dead = 1, /* important that this is > 0 */
s_start_req_or_res,
s_res_or_resp_H,
s_start_res,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_http_major,
s_res_http_dot,
s_res_http_minor,
s_res_http_end,
s_res_first_status_code,
s_res_status_code,
s_res_status_start,
s_res_status,
s_res_line_almost_done,
s_start_req,
s_req_method,
s_req_spaces_before_url,
s_req_schema,
s_req_schema_slash,
s_req_schema_slash_slash,
s_req_server_start,
s_req_server,
s_req_server_with_at,
s_req_path,
s_req_query_string_start,
s_req_query_string,
s_req_fragment_start,
s_req_fragment,
s_req_http_start,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_http_I,
s_req_http_IC,
s_req_http_major,
s_req_http_dot,
s_req_http_minor,
s_req_http_end,
s_req_line_almost_done,
s_header_field_start,
s_header_field,
s_header_value_discard_ws,
s_header_value_discard_ws_almost_done,
s_header_value_discard_lws,
s_header_value_start,
s_header_value,
s_header_value_lws,
s_header_almost_done,
s_chunk_size_start,
s_chunk_size,
s_chunk_parameters,
s_chunk_size_almost_done,
s_headers_almost_done,
s_headers_done,
/*
* Important: 's_headers_done' must be the last 'header' state. All
* states beyond this must be 'body' states. It is used for overflow
* checking. See the PARSING_HEADER() macro.
*/
s_chunk_data,
s_chunk_data_almost_done,
s_chunk_data_done,
s_body_identity,
s_body_identity_eof,
s_message_done
} state_t;
typedef enum {
s_http_host_dead = 1,
s_http_userinfo_start,
s_http_userinfo,
s_http_host_start,
s_http_host_v6_start,
s_http_host,
s_http_host_v6,
s_http_host_v6_end,
s_http_host_v6_zone_start,
s_http_host_v6_zone,
s_http_host_port_start,
s_http_host_port
} host_state_t;
/* Macros for character classes; depends on strict-mode */
#define IS_MARK(c) \
((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \
(c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')')
#define IS_USERINFO_CHAR(c) \
(isalnum(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || (c) == ':' || \
(c) == '&' || (c) == '=' || (c) == '+' || (c) == '$' || (c) == ',')
#if HTTP_PARSER_STRICT
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '-')
#else
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80))
#define IS_HOST_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif
/*
* Our URL parser.
*
* This is designed to be shared by http_parser_execute() for URL validation,
* hence it has a state transition + byte-for-byte interface. In addition, it
* is meant to be embedded in http_parser_parse_url(), which does the dirty
* work of turning state transitions URL components for its API.
*
* This function should only be invoked with non-space characters. It is
* assumed that the caller cares about (and can detect) the transition between
* URL and non-URL states by looking for these.
*/
static state_t
parse_url_char(state_t s, const char ch) {
if (ch == ' ' || ch == '\r' || ch == '\n') {
return (s_dead);
}
#if HTTP_PARSER_STRICT
if (ch == '\t' || ch == '\f') {
return (s_dead);
}
#endif
switch (s) {
case s_req_spaces_before_url:
/* Proxied requests are followed by scheme of an absolute URI
* (alpha). All methods except CONNECT are followed by '/' or
* '*'.
*/
if (ch == '/' || ch == '*') {
return (s_req_path);
}
if (isalpha(ch)) {
return (s_req_schema);
}
break;
case s_req_schema:
if (isalpha(ch)) {
return (s);
}
if (ch == ':') {
return (s_req_schema_slash);
}
break;
case s_req_schema_slash:
if (ch == '/') {
return (s_req_schema_slash_slash);
}
break;
case s_req_schema_slash_slash:
if (ch == '/') {
return (s_req_server_start);
}
break;
case s_req_server_with_at:
if (ch == '@') {
return (s_dead);
}
/* FALLTHROUGH */
case s_req_server_start:
case s_req_server:
if (ch == '/') {
return (s_req_path);
}
if (ch == '?') {
return (s_req_query_string_start);
}
if (ch == '@') {
return (s_req_server_with_at);
}
if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
return (s_req_server);
}
break;
case s_req_path:
if (IS_URL_CHAR(ch)) {
return (s);
}
switch (ch) {
case '?':
return (s_req_query_string_start);
case '#':
return (s_req_fragment_start);
}
break;
case s_req_query_string_start:
case s_req_query_string:
if (IS_URL_CHAR(ch)) {
return (s_req_query_string);
}
switch (ch) {
case '?':
/* allow extra '?' in query string */
return (s_req_query_string);
case '#':
return (s_req_fragment_start);
}
break;
case s_req_fragment_start:
if (IS_URL_CHAR(ch)) {
return (s_req_fragment);
}
switch (ch) {
case '?':
return (s_req_fragment);
case '#':
return (s);
}
break;
case s_req_fragment:
if (IS_URL_CHAR(ch)) {
return (s);
}
switch (ch) {
case '?':
case '#':
return (s);
}
break;
default:
break;
}
/*
* We should never fall out of the switch above unless there's an
* error.
*/
return (s_dead);
}
static host_state_t
http_parse_host_char(host_state_t s, const char ch) {
switch (s) {
case s_http_userinfo:
case s_http_userinfo_start:
if (ch == '@') {
return (s_http_host_start);
}
if (IS_USERINFO_CHAR(ch)) {
return (s_http_userinfo);
}
break;
case s_http_host_start:
if (ch == '[') {
return (s_http_host_v6_start);
}
if (IS_HOST_CHAR(ch)) {
return (s_http_host);
}
break;
case s_http_host:
if (IS_HOST_CHAR(ch)) {
return (s_http_host);
}
/* FALLTHROUGH */
case s_http_host_v6_end:
if (ch == ':') {
return (s_http_host_port_start);
}
break;
case s_http_host_v6:
if (ch == ']') {
return (s_http_host_v6_end);
}
/* FALLTHROUGH */
case s_http_host_v6_start:
if (isxdigit(ch) || ch == ':' || ch == '.') {
return (s_http_host_v6);
}
if (s == s_http_host_v6 && ch == '%') {
return (s_http_host_v6_zone_start);
}
break;
case s_http_host_v6_zone:
if (ch == ']') {
return (s_http_host_v6_end);
}
/* FALLTHROUGH */
case s_http_host_v6_zone_start:
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
if (isalnum(ch) || ch == '%' || ch == '.' || ch == '-' ||
ch == '_' || ch == '~')
{
return (s_http_host_v6_zone);
}
break;
case s_http_host_port:
case s_http_host_port_start:
if (isdigit(ch)) {
return (s_http_host_port);
}
break;
default:
break;
}
return (s_http_host_dead);
}
static isc_result_t
http_parse_host(const char *buf, isc_url_parser_t *up, int found_at) {
host_state_t s;
const char *p = NULL;
size_t buflen = up->field_data[ISC_UF_HOST].off +
up->field_data[ISC_UF_HOST].len;
REQUIRE((up->field_set & (1 << ISC_UF_HOST)) != 0);
up->field_data[ISC_UF_HOST].len = 0;
s = found_at ? s_http_userinfo_start : s_http_host_start;
for (p = buf + up->field_data[ISC_UF_HOST].off; p < buf + buflen; p++) {
host_state_t new_s = http_parse_host_char(s, *p);
if (new_s == s_http_host_dead) {
return (ISC_R_FAILURE);
}
switch (new_s) {
case s_http_host:
if (s != s_http_host) {
up->field_data[ISC_UF_HOST].off =
(uint16_t)(p - buf);
}
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_v6:
if (s != s_http_host_v6) {
up->field_data[ISC_UF_HOST].off =
(uint16_t)(p - buf);
}
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
up->field_data[ISC_UF_HOST].len++;
break;
case s_http_host_port:
if (s != s_http_host_port) {
up->field_data[ISC_UF_PORT].off =
(uint16_t)(p - buf);
up->field_data[ISC_UF_PORT].len = 0;
up->field_set |= (1 << ISC_UF_PORT);
}
up->field_data[ISC_UF_PORT].len++;
break;
case s_http_userinfo:
if (s != s_http_userinfo) {
up->field_data[ISC_UF_USERINFO].off =
(uint16_t)(p - buf);
up->field_data[ISC_UF_USERINFO].len = 0;
up->field_set |= (1 << ISC_UF_USERINFO);
}
up->field_data[ISC_UF_USERINFO].len++;
break;
default:
break;
}
s = new_s;
}
/* Make sure we don't end somewhere unexpected */
switch (s) {
case s_http_host_start:
case s_http_host_v6_start:
case s_http_host_v6:
case s_http_host_v6_zone_start:
case s_http_host_v6_zone:
case s_http_host_port_start:
case s_http_userinfo:
case s_http_userinfo_start:
return (ISC_R_FAILURE);
default:
break;
}
return (ISC_R_SUCCESS);
}
isc_result_t
isc_url_parse(const char *buf, size_t buflen, bool is_connect,
isc_url_parser_t *up) {
state_t s;
isc_url_field_t uf, old_uf;
int found_at = 0;
const char *p = NULL;
if (buflen == 0) {
return (ISC_R_FAILURE);
}
up->port = up->field_set = 0;
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
old_uf = ISC_UF_MAX;
for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p);
/* Figure out the next field that we're operating on */
switch (s) {
case s_dead:
return (ISC_R_FAILURE);
/* Skip delimiters */
case s_req_schema_slash:
case s_req_schema_slash_slash:
case s_req_server_start:
case s_req_query_string_start:
case s_req_fragment_start:
continue;
case s_req_schema:
uf = ISC_UF_SCHEMA;
break;
case s_req_server_with_at:
found_at = 1;
/* FALLTHROUGH */
case s_req_server:
uf = ISC_UF_HOST;
break;
case s_req_path:
uf = ISC_UF_PATH;
break;
case s_req_query_string:
uf = ISC_UF_QUERY;
break;
case s_req_fragment:
uf = ISC_UF_FRAGMENT;
break;
default:
INSIST(0);
ISC_UNREACHABLE();
}
/* Nothing's changed; soldier on */
if (uf == old_uf) {
up->field_data[uf].len++;
continue;
}
up->field_data[uf].off = (uint16_t)(p - buf);
up->field_data[uf].len = 1;
up->field_set |= (1 << uf);
old_uf = uf;
}
/* host must be present if there is a schema */
/* parsing http:///toto will fail */
if ((up->field_set & (1 << ISC_UF_SCHEMA)) &&
(up->field_set & (1 << ISC_UF_HOST)) == 0)
{
return (ISC_R_FAILURE);
}
if (up->field_set & (1 << ISC_UF_HOST)) {
isc_result_t result;
result = http_parse_host(buf, up, found_at);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
/* CONNECT requests can only contain "hostname:port" */
if (is_connect &&
up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT))) {
return (ISC_R_FAILURE);
}
if (up->field_set & (1 << ISC_UF_PORT)) {
uint16_t off;
uint16_t len;
const char *pp = NULL;
const char *end = NULL;
unsigned long v;
off = up->field_data[ISC_UF_PORT].off;
len = up->field_data[ISC_UF_PORT].len;
end = buf + off + len;
/*
* NOTE: The characters are already validated and are in the
* [0-9] range
*/
INSIST(off + len <= buflen);
v = 0;
for (pp = buf + off; pp < end; pp++) {
v *= 10;
v += *pp - '0';
/* Ports have a max value of 2^16 */
if (v > 0xffff) {
return (ISC_R_RANGE);
}
}
up->port = (uint16_t)v;
}
return (ISC_R_SUCCESS);
}

View file

@ -448,7 +448,14 @@ isc_nm_cancelread
isc_nm_closedown
isc_nm_destroy
isc_nm_detach
isc_nm_http_add_doh_endpoint
isc_nm_http_add_endpoint
isc_nm_http_connect_send_request
isc_nm_httpconnect
isc_nm_httprequest
isc_nm_listenhttp
isc_nm_listentcpdns
isc_nm_listentls
isc_nm_listentlsdns
isc_nm_listentcp
isc_nm_listenudp
@ -706,6 +713,7 @@ isc_tlsctx_createserver
isc_tlsctx_free
isc_tm_timegm
isc_tm_strptime
isc_url_parse
isc_utf8_bom
isc_utf8_valid
isc_win32os_versioncheck

View file

@ -260,6 +260,9 @@
<ClInclude Include="..\include\isc\types.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\url.h">
<Filter>Library Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isc\utf8.h">
<Filter>Library Header Files</Filter>
</ClInclude>
@ -608,12 +611,15 @@
<ClCompile Include="..\timer.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\tls.c">
<ClCompile Include="..\tlsstream.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\tm.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\url.c">
<Filter>Library Source Files</Filter>
</ClCompile>
<ClCompile Include="..\utf8.c">
<Filter>Library Source Files</Filter>
</ClCompile>

View file

@ -62,11 +62,11 @@
@IF PKCS11
<PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ELSE PKCS11
<PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@END PKCS11
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
@ -80,7 +80,7 @@
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
<ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
</Link>
@ -116,6 +116,9 @@ echo Copying the libxml DLL.
copy @LIBXML2_DLL@ ..\Build\Debug\
@END LIBXML2
echo Copying nghttp2 DLL.
copy @NGHTTP2_DLL@ ..\Build\Debug\
@IF GSSAPI
echo Copying the GSSAPI and KRB5 DLLs.
@ -163,11 +166,11 @@ copy InstallFiles ..\Build\Debug\
@IF PKCS11
<PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ELSE PKCS11
<PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@END PKCS11
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<WholeProgramOptimization>false</WholeProgramOptimization>
@ -184,7 +187,7 @@ copy InstallFiles ..\Build\Debug\
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
<ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
@ -211,6 +214,9 @@ copy @OPENSSL_PATH@\LICENSE ..\Build\Release\OpenSSL-LICENSE
echo Copying libuv DLL.
copy @LIBUV_DLL@ ..\Build\Release\
echo Copying nghttp2 DLL.
copy @NGHTTP2_DLL@ ..\Build\Release\
@IF LIBXML2
echo Copying the libxml DLL.
@ -337,6 +343,7 @@ copy InstallFiles ..\Build\Release\
<ClInclude Include="..\include\isc\tls.h" />
<ClInclude Include="..\include\isc\tm.h" />
<ClInclude Include="..\include\isc\types.h" />
<ClInclude Include="..\include\isc\url.h" />
<ClInclude Include="..\include\isc\utf8.h" />
<ClInclude Include="..\include\isc\util.h" />
@IF PKCS11
@ -408,13 +415,15 @@ copy InstallFiles ..\Build\Release\
<ClCompile Include="..\mem.c" />
<ClCompile Include="..\mutexblock.c" />
<ClCompile Include="..\netaddr.c" />
<ClCompile Include="..\netmgr\http.c" />
<ClCompile Include="..\netmgr\netmgr.c" />
<ClCompile Include="..\netmgr\tcp.c" />
<ClCompile Include="..\netmgr\udp.c" />
<ClCompile Include="..\netmgr\uverr2result.c" />
<ClCompile Include="..\netmgr\uv-compat.c" />
<ClCompile Include="..\netmgr\tcpdns.c" />
<ClCompile Include="..\netmgr\tlsstream.c" />
<ClCompile Include="..\netmgr\udp.c" />
<ClCompile Include="..\netmgr\tlsdns.c" />
<ClCompile Include="..\netmgr\uv-compat.c" />
<ClCompile Include="..\netmgr\uverr2result.c" />
<ClCompile Include="..\netscope.c" />
<ClCompile Include="..\nonce.c" />
<ClCompile Include="..\openssl_shim.c" />
@ -442,6 +451,7 @@ copy InstallFiles ..\Build\Release\
<ClCompile Include="..\timer.c" />
<ClCompile Include="..\tls.c" />
<ClCompile Include="..\tm.c" />
<ClCompile Include="..\url.c" />
<ClCompile Include="..\utf8.c" />
@IF PKCS11
<ClCompile Include="..\pk11.c" />

View file

@ -81,6 +81,7 @@ static cfg_type_t cfg_type_bracketed_dscpsockaddrlist;
static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
static cfg_type_t cfg_type_bracketed_netaddrlist;
static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
static cfg_type_t cfg_type_bracketed_qstring_list;
static cfg_type_t cfg_type_controls;
static cfg_type_t cfg_type_controls_sockaddr;
static cfg_type_t cfg_type_destinationlist;
@ -91,6 +92,7 @@ static cfg_type_t cfg_type_dnstap;
static cfg_type_t cfg_type_dnstapoutput;
static cfg_type_t cfg_type_dyndb;
static cfg_type_t cfg_type_plugin;
static cfg_type_t cfg_type_http_description;
static cfg_type_t cfg_type_ixfrdifftype;
static cfg_type_t cfg_type_ixfrratio;
static cfg_type_t cfg_type_key;
@ -108,6 +110,7 @@ static cfg_type_t cfg_type_optional_allow;
static cfg_type_t cfg_type_optional_class;
static cfg_type_t cfg_type_optional_dscp;
static cfg_type_t cfg_type_optional_facility;
static cfg_type_t cfg_type_optional_http;
static cfg_type_t cfg_type_optional_keyref;
static cfg_type_t cfg_type_optional_port;
static cfg_type_t cfg_type_optional_uint32;
@ -151,6 +154,7 @@ static cfg_tuplefielddef_t listenon_fields[] = {
{ "port", &cfg_type_optional_port, 0 },
{ "dscp", &cfg_type_optional_dscp, 0 },
{ "tls", &cfg_type_optional_tls, 0 },
{ "http", &cfg_type_optional_http, 0 },
{ "acl", &cfg_type_bracketed_aml, 0 },
{ NULL, NULL, 0 }
};
@ -1088,6 +1092,7 @@ static cfg_clausedef_t namedconf_clauses[] = {
{ "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
{ "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
{ "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI },
{ "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI },
{ "logging", &cfg_type_logging, 0 },
{ "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT },
{ "masters", &cfg_type_primaries, CFG_CLAUSEFLAG_MULTI },
@ -1221,6 +1226,8 @@ static cfg_clausedef_t options_clauses[] = {
{ "pid-file", &cfg_type_qstringornone, 0 },
{ "port", &cfg_type_uint32, 0 },
{ "tls-port", &cfg_type_uint32, 0 },
{ "http-port", &cfg_type_uint32, 0 },
{ "https-port", &cfg_type_uint32, 0 },
{ "querylog", &cfg_type_boolean, 0 },
{ "random-device", &cfg_type_qstringornone, 0 },
{ "recursing-file", &cfg_type_qstring, 0 },
@ -3853,3 +3860,31 @@ static cfg_type_t cfg_type_optional_tls = {
"tlsoptional", parse_optional_keyvalue, print_keyvalue,
doc_optional_keyvalue, &cfg_rep_string, &tls_kw
};
/* http and https */
static cfg_type_t cfg_type_bracketed_qstring_list = { "bracketed_qstring_list",
cfg_parse_bracketed_list,
cfg_print_bracketed_list,
cfg_doc_bracketed_list,
&cfg_rep_list,
&cfg_type_qstring };
static cfg_clausedef_t cfg_http_description_clauses[] = {
{ "endpoints", &cfg_type_bracketed_qstring_list, 0 }, { NULL, NULL, 0 }
};
static cfg_clausedef_t *http_description_clausesets[] = {
cfg_http_description_clauses, NULL
};
static cfg_type_t cfg_type_http_description = {
"http_desc", cfg_parse_named_map, cfg_print_map,
cfg_doc_map, &cfg_rep_map, http_description_clausesets
};
static keyword_type_t http_kw = { "http", &cfg_type_astring };
static cfg_type_t cfg_type_optional_http = {
"http_optional", parse_optional_keyvalue, print_keyvalue,
doc_optional_keyvalue, &cfg_rep_string, &http_kw
};

View file

@ -80,6 +80,8 @@ struct ns_interface {
isc_socket_t * tcpsocket; /*%< TCP socket. */
isc_nmsocket_t *udplistensocket;
isc_nmsocket_t *tcplistensocket;
isc_nmsocket_t *http_listensocket;
isc_nmsocket_t *http_secure_listensocket;
isc_dscp_t dscp; /*%< "listen-on" DSCP value */
isc_refcount_t ntcpaccepting; /*%< Number of clients
* ready to accept new

View file

@ -42,9 +42,12 @@ typedef struct ns_listenlist ns_listenlist_t;
struct ns_listenelt {
isc_mem_t * mctx;
in_port_t port;
bool is_http;
isc_dscp_t dscp; /* -1 = not set, 0..63 */
dns_acl_t * acl;
isc_tlsctx_t *sslctx;
char ** http_endpoints;
size_t http_endpoints_number;
ISC_LINK(ns_listenelt_t) link;
};
@ -66,6 +69,15 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
* Create a listen-on list element.
*/
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);
/*%<
* Create a listen-on list element for HTTP(S).
*/
void
ns_listenelt_destroy(ns_listenelt_t *elt);
/*%<

View file

@ -437,6 +437,10 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
goto failure;
}
ifp->tcplistensocket = NULL;
ifp->http_listensocket = NULL;
ifp->http_secure_listensocket = NULL;
*ifpret = ifp;
return (ISC_R_SUCCESS);
@ -539,6 +543,54 @@ ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) {
return (result);
}
static isc_result_t
ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps,
size_t neps) {
isc_result_t result;
isc_nmsocket_t *sock = NULL;
size_t i = 0;
result = isc_nm_listenhttp(ifp->mgr->nm, (isc_nmiface_t *)&ifp->addr,
ifp->mgr->backlog, &ifp->mgr->sctx->tcpquota,
sslctx, &sock);
if (result == ISC_R_SUCCESS) {
for (i = 0; i < neps; i++) {
result = isc_nm_http_add_doh_endpoint(
sock, eps[i], ns__client_request, ifp,
sizeof(ns_client_t));
}
}
if (result != ISC_R_SUCCESS) {
isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
"creating %s socket: %s",
sslctx ? "HTTPS" : "HTTP",
isc_result_totext(result));
return (result);
}
if (sslctx) {
ifp->http_secure_listensocket = sock;
} else {
ifp->http_listensocket = sock;
}
/*
* We call this now to update the tcp-highwater statistic:
* this is necessary because we are adding to the TCP quota just
* by listening.
*/
result = ns__client_tcpconn(NULL, ISC_R_SUCCESS, ifp);
if (result != ISC_R_SUCCESS) {
isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
"updating TCP stats: %s",
isc_result_totext(result));
}
return (result);
}
static isc_result_t
ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
const char *name, ns_interface_t **ifpret, bool accept_tcp,
@ -555,6 +607,17 @@ 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);
if (result != ISC_R_SUCCESS) {
goto cleanup_interface;
}
*ifpret = ifp;
return (result);
}
if (elt->sslctx != NULL) {
result = ns_interface_listentls(ifp, elt->sslctx);
if (result != ISC_R_SUCCESS) {
@ -611,6 +674,14 @@ ns_interface_shutdown(ns_interface_t *ifp) {
isc_nm_stoplistening(ifp->tcplistensocket);
isc_nmsocket_close(&ifp->tcplistensocket);
}
if (ifp->http_listensocket != NULL) {
isc_nm_stoplistening(ifp->http_listensocket);
isc_nmsocket_close(&ifp->http_listensocket);
}
if (ifp->http_secure_listensocket != NULL) {
isc_nm_stoplistening(ifp->http_secure_listensocket);
isc_nmsocket_close(&ifp->http_secure_listensocket);
}
if (ifp->clientmgr != NULL) {
ns_clientmgr_destroy(&ifp->clientmgr);
}

View file

@ -30,24 +30,59 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
ns_listenelt_t **target) {
ns_listenelt_t *elt = NULL;
isc_result_t result = ISC_R_SUCCESS;
isc_tlsctx_t *sslctx = NULL;
REQUIRE(target != NULL && *target == NULL);
elt = isc_mem_get(mctx, sizeof(*elt));
elt->mctx = mctx;
ISC_LINK_INIT(elt, link);
elt->port = port;
elt->dscp = dscp;
elt->acl = acl;
elt->sslctx = NULL;
if (tls) {
result = isc_tlsctx_createserver(key, cert, &elt->sslctx);
result = isc_tlsctx_createserver(key, cert, &sslctx);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
elt = isc_mem_get(mctx, sizeof(*elt));
elt->mctx = mctx;
ISC_LINK_INIT(elt, link);
elt->port = port;
elt->is_http = false;
elt->dscp = dscp;
elt->acl = acl;
elt->sslctx = sslctx;
elt->http_endpoints = NULL;
elt->http_endpoints_number = 0;
*target = elt;
return (ISC_R_SUCCESS);
}
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_result_t result;
REQUIRE(target != NULL && *target == NULL);
REQUIRE(endpoints != NULL && *endpoints != NULL);
REQUIRE(nendpoints > 0);
result = ns_listenelt_create(mctx, http_port, dscp, acl, tls, key, cert,
target);
if (result == ISC_R_SUCCESS) {
(*target)->is_http = true;
(*target)->http_endpoints = endpoints;
(*target)->http_endpoints_number = nendpoints;
} else {
size_t i;
for (i = 0; i < nendpoints; i++) {
isc_mem_free(mctx, endpoints[i]);
}
isc_mem_free(mctx, endpoints);
}
return (result);
}
void
ns_listenelt_destroy(ns_listenelt_t *elt) {
if (elt->acl != NULL) {
@ -56,6 +91,14 @@ ns_listenelt_destroy(ns_listenelt_t *elt) {
if (elt->sslctx != NULL) {
isc_tlsctx_free(&elt->sslctx);
}
if (elt->http_endpoints != NULL) {
size_t i;
INSIST(elt->http_endpoints_number > 0);
for (i = 0; i < elt->http_endpoints_number; i++) {
isc_mem_free(elt->mctx, elt->http_endpoints[i]);
}
isc_mem_free(elt->mctx, elt->http_endpoints);
}
isc_mem_put(elt->mctx, elt, sizeof(*elt));
}

View file

@ -66,6 +66,7 @@ ns_interfacemgr_shutdown
ns_lib_init
ns_lib_shutdown
ns_listenelt_create
ns_listenelt_create_http
ns_listenelt_destroy
ns_listenlist_attach
ns_listenlist_create

View file

@ -1887,6 +1887,7 @@
./lib/isc/include/isc/tls.h C 2021
./lib/isc/include/isc/tm.h C 2014,2016,2018,2019,2020,2021
./lib/isc/include/isc/types.h C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2017,2018,2019,2020,2021
./lib/isc/include/isc/url.h C 2021
./lib/isc/include/isc/utf8.h C 2020,2021
./lib/isc/include/isc/util.h C 1998,1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2015,2016,2017,2018,2019,2020,2021
./lib/isc/include/pk11/constants.h C 2014,2016,2017,2018,2019,2020,2021
@ -1904,11 +1905,13 @@
./lib/isc/mem_p.h C 2018,2019,2020,2021
./lib/isc/mutexblock.c C 1999,2000,2001,2004,2005,2007,2011,2012,2016,2018,2019,2020,2021
./lib/isc/netaddr.c C 1999,2000,2001,2002,2004,2005,2007,2010,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021
./lib/isc/netmgr/http.c C 2021
./lib/isc/netmgr/netmgr-int.h C 2019,2020,2021
./lib/isc/netmgr/netmgr.c C 2019,2020,2021
./lib/isc/netmgr/tcp.c C 2019,2020,2021
./lib/isc/netmgr/tcpdns.c C 2019,2020,2021
./lib/isc/netmgr/tlsdns.c C 2020,2021
./lib/isc/netmgr/tlsstream.c C 2019,2020,2021
./lib/isc/netmgr/udp.c C 2019,2020,2021
./lib/isc/netmgr/uv-compat.c C 2020,2021
./lib/isc/netmgr/uv-compat.h C 2019,2020,2021
@ -1952,6 +1955,7 @@
./lib/isc/tests/buffer_test.c C 2014,2015,2016,2017,2018,2019,2020,2021
./lib/isc/tests/counter_test.c C 2014,2016,2018,2019,2020,2021
./lib/isc/tests/crc64_test.c C 2018,2019,2020,2021
./lib/isc/tests/doh_test.c C 2020,2021
./lib/isc/tests/errno_test.c C 2016,2018,2019,2020,2021
./lib/isc/tests/file_test.c C 2014,2016,2017,2018,2019,2020,2021
./lib/isc/tests/hash_test.c C 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
@ -2018,6 +2022,7 @@
./lib/isc/unix/stdtime.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021
./lib/isc/unix/syslog.c C 2001,2004,2005,2007,2016,2018,2019,2020,2021
./lib/isc/unix/time.c C 1998,1999,2000,2001,2003,2004,2005,2006,2007,2008,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021
./lib/isc/url.c C 2021
./lib/isc/utf8.c C 2020,2021
./lib/isc/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020,2021
./lib/isc/win32/condition.c C 1998,1999,2000,2001,2004,2006,2007,2016,2018,2019,2020,2021

View file

@ -221,6 +221,7 @@ my @substinc = ("GSSAPI_INC",
"IDN_INC",
"LIBXML2_INC",
"LIBUV_INC",
"NGHTTP2_INC",
"OPENSSL_INC",
"READLINE_INC",
"ZLIB_INC");
@ -235,6 +236,7 @@ my @substlib = ("GSSAPI_LIB",
"KRB5_LIB",
"LIBXML2_LIB",
"LIBUV_LIB",
"NGHTTP2_LIB",
"OPENSSL_LIBCRYPTO",
"OPENSSL_LIBSSL",
"READLINE_LIB",
@ -253,6 +255,7 @@ my @substdll = ("COMERR_DLL",
"K5SPRT_DLL",
"LIBXML2_DLL",
"LIBUV_DLL",
"NGHTTP2_DLL",
"OPENSSL_DLLCRYPTO",
"OPENSSL_DLLSSL",
"WSHELP_DLL",
@ -341,6 +344,7 @@ my @withlist = ("aes",
"idn",
"openssl",
"libxml2",
"nghttp2",
"pkcs11",
"pssuspend",
"python",
@ -389,6 +393,7 @@ my @help = (
" with-samples build with sample programs\n",
" with-openssl[=PATH] build with OpenSSL yes|path (mandatory)\n",
" with-libuv[=PATH] build with libuv yes|path (mandatory)\n",
" with-nghttp2[=PATH] build with nghttp2 yes|path (mandatory)\n",
" with-pkcs11[=PATH] build with PKCS#11 support yes|no|provider-path\n",
" with-gssapi[=PATH] build with MIT KfW GSSAPI yes|no|path\n",
" with-libxml2[=PATH] build with libxml2 library yes|no|path\n",
@ -431,6 +436,8 @@ my $use_stests = "no";
my $use_samples = "no";
my $use_libuv = "auto";
my $libuv_path = "../../";
my $nghttp2_path = "../../";
my $use_nghttp2 = "auto";
my $use_openssl = "auto";
my $openssl_path = "../../";
my $use_pkcs11 = "no";
@ -758,6 +765,13 @@ sub mywith {
$use_libuv = "yes";
$libuv_path = $val;
}
} elsif ($key =~ /^nghttp2$/i) {
if ($val =~ /^no$/i) {
die "nghttp2 is required\n";
} elsif ($val !~ /^yes$/i) {
$use_nghttp2 = "yes";
$nghttp2_path = $val;
}
} elsif ($key =~ /^pkcs11$/i) {
if ($val =~ /^yes$/i) {
$use_pkcs11 = "yes";
@ -949,6 +963,7 @@ if ($verbose) {
print "querytrace: disabled\n";
}
print "libuv-path: $libuv_path\n";
print "nghttp2-path: $nghttp2_path\n";
print "openssl-path: $openssl_path\n";
if ($use_tests eq "yes") {
print "tests: enabled\n";
@ -1327,6 +1342,63 @@ if ($use_libuv eq "yes") {
# $configdefh{"HAVE_UV_IMPORT"} = 1;
}
# with-nghttp2
if ($use_nghttp2 eq "auto") {
if ($verbose) {
print "checking for an nghttp2 built directory at sibling root\n";
}
opendir DIR, $nghttp2_path || die "No Directory: $!\n";
my @dirlist = grep (/^nghttp2-[0-9]+\.[0-9]+\.[0-9]+$/i, readdir(DIR));
closedir(DIR);
# Make sure we have something
if (scalar(@dirlist) == 0) {
die "can't find an nghttp2 at sibling root\n";
}
# Now see if we have a directory or just a file.
# Make sure we are case insensitive
my $file;
foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
if (-f File::Spec->catfile($nghttp2_path,
$file,
"include", "nghttp2", "nghttp2.h")) {
$nghttp2_path = File::Spec->catdir($nghttp2_path, $file);
$use_nghttp2 = "yes";
last;
}
}
# If we have one use it otherwise report the error
if ($use_nghttp2 eq "auto") {
die "can't find an nghttp2 built directory at sibling root\n";
}
}
if ($use_nghttp2 eq "yes") {
$nghttp2_path = File::Spec->rel2abs($nghttp2_path);
if ($verbose) {
print "checking for nghttp2 directory at \"$nghttp2_path\"\n";
}
if (!-f File::Spec->catfile($nghttp2_path,
"include", "nghttp2", "nghttp2.h")) {
die "can't find nghttp2 nghttp2.h include\n";
}
my $nghttp2_inc = File::Spec->catdir($nghttp2_path, "include");
my $nghttp2_bindir = File::Spec->catdir($nghttp2_path, "bin");
my $nghttp2_libdir = File::Spec->catdir($nghttp2_path, "lib");
my $nghttp2_dll = File::Spec->catfile($nghttp2_bindir, "nghttp2.dll");
my $nghttp2_lib = File::Spec->catfile($nghttp2_libdir, "nghttp2.lib");
if (!-f $nghttp2_lib) {
die "can't find nghttp2.lib library\n";
}
if (!-f $nghttp2_dll) {
die "can't find nghttp2.dll library\n";
}
$configinc{"NGHTTP2_INC"} = "$nghttp2_inc";
$configlib{"NGHTTP2_LIB"} = "$nghttp2_lib";
$configdll{"NGHTTP2_DLL"} = "$nghttp2_dll";
}
# with-openssl
if ($use_openssl eq "auto") {
if ($verbose) {
@ -2433,6 +2505,7 @@ sub makeinstallfile {
print LOUT "libdns.dll-BCFT\n";
print LOUT "libirs.dll-BCFT\n";
print LOUT "libns.dll-BCFT\n";
print LOUT "nghttp2.dll-BCFT\n";
print LOUT "uv.dll-BCFT\n";
if ($use_openssl eq "yes") {
my $v;