+5575. [func] Initial support for DNS-over-HTTP(S). BIND now
+ includes DNS-over-HTTP(S) layer built on top of nghttp2.
+ Both encrypted (via TLS) and unencrypted HTTP/2 connections
+ are supported.
+
5574. [func] Incoming zone transfers can now use TLS.
Addresses in a "primaries" list take an optional
"tls" argument, specifying either a previously
# 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\
EXTERN isc_socketmgr_t *named_g_socketmgr INIT(NULL);
EXTERN isc_nm_t *named_g_nm INIT(NULL);
EXTERN cfg_parser_t *named_g_parser INIT(NULL);
-EXTERN cfg_parser_t *named_g_addparser INIT(NULL);
-EXTERN const char *named_g_version INIT(PACKAGE_VERSION);
-EXTERN const char *named_g_product INIT(PACKAGE_NAME);
-EXTERN const char *named_g_description INIT(PACKAGE_DESCRIPTION);
-EXTERN const char *named_g_srcid INIT(PACKAGE_SRCID);
-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_http_secure_port INIT(0);
-EXTERN isc_dscp_t named_g_dscp INIT(-1);
+EXTERN cfg_parser_t *named_g_addparser INIT(NULL);
+EXTERN const char *named_g_version INIT(PACKAGE_VERSION);
+EXTERN const char *named_g_product INIT(PACKAGE_NAME);
+EXTERN const char *named_g_description INIT(PACKAGE_DESCRIPTION);
+EXTERN const char *named_g_srcid INIT(PACKAGE_SRCID);
+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_http_secure_port INIT(0);
+EXTERN in_port_t named_g_http_port INIT(0);
+EXTERN isc_dscp_t named_g_dscp INIT(-1);
EXTERN named_server_t *named_g_server INIT(NULL);
static void
parse_port(char *arg) {
- enum { DNSPORT, TLSPORT, HTTP_SECURE_PORT } ptype = DNSPORT;
+ enum { DNSPORT, TLSPORT, HTTP_SECURE_PORT, HTTP_PORT } ptype = DNSPORT;
char *value = arg;
int port;
} else if (strncmp(arg, "https=", 6) == 0) {
value = arg + 6;
ptype = HTTP_SECURE_PORT;
+ } else if (strncmp(arg, "http=", 5) == 0) {
+ value = arg + 6;
+ ptype = HTTP_PORT;
}
port = parse_int(value, "port");
case HTTP_SECURE_PORT:
named_g_http_secure_port = port;
break;
+ case HTTP_PORT:
+ named_g_http_port = port;
+ break;
default:
INSIST(0);
ISC_UNREACHABLE();
dyndb string quoted_string {
unspecified-text };
+HTTP
+^^^^
+
+::
+
+ http string {
+ endpoints { quoted_string; ... }; // experimental
+ };
+
KEY
^^^
glue-cache boolean;// deprecated
heartbeat-interval integer;
hostname ( quoted_string | none );
- https-endpoint quoted_string https-server string;
+ http-port integer;
https-port integer;
- https-server string [ port integer ] tls string { (
- quoted_string [ port integer ] [ dscp integer ] |
- ipv4_address [ port integer ] [ dscp integer ] |
- ipv6_address [ port integer ] [ dscp integer ] ); ... };
inline-signing boolean;
interface-interval duration;
ipv4only-contact string;
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 );
forwarders [ port integer ] [ dscp integer ] { ( ipv4_address
| ipv6_address ) [ port integer ] [ dscp integer ]; ... };
glue-cache boolean;// deprecated
- https-endpoint quoted_string https-server string;
inline-signing boolean;
ipv4only-contact string;
ipv4only-enable boolean;
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.
#include <dst/result.h>
#include <isccfg/grammar.h>
+#include <isccfg/httpconf.h>
#include <isccfg/kaspconf.h>
#include <isccfg/namedconf.h>
+#include <isccfg/tlsconf.h>
#include <ns/client.h>
#include <ns/hooks.h>
static void
named_server_reload(isc_task_t *task, isc_event_t *event);
+static isc_result_t
+ns_listenelt_from_http(isc_cfg_http_obj_t *http, isc_cfg_tls_obj_t *tls,
+ in_port_t port, isc_mem_t *mctx,
+ ns_listenelt_t **target);
+
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);
+ uint16_t family, isc_cfg_http_storage_t *http_servers,
+ isc_cfg_tls_data_storage_t *tls_storage,
+ 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);
+ uint16_t family, isc_cfg_http_storage_t *http_servers,
+ isc_cfg_tls_data_storage_t *tls_storage,
+ ns_listenlist_t **target);
static isc_result_t
configure_forward(const cfg_obj_t *config, dns_view_t *view,
unsigned int initial, idle, keepalive, advertised;
dns_aclenv_t *env =
ns_interfacemgr_getaclenv(named_g_server->interfacemgr);
+ isc_cfg_tls_data_storage_t tls_storage;
+ isc_cfg_http_storage_t http_storage;
ISC_LIST_INIT(kasplist);
ISC_LIST_INIT(viewlist);
ISC_LIST_INIT(cachelist);
ISC_LIST_INIT(altsecrets);
+ cfg_tls_storage_init(named_g_mctx, &tls_storage);
+ cfg_http_storage_init(named_g_mctx, &http_storage);
+
/* Create the ACL configuration context */
if (named_g_aclconfctx != NULL) {
cfg_aclconfctx_detach(&named_g_aclconfctx);
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_http_port = (in_port_t)cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = named_config_get(maps, "https-port", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ named_g_http_secure_port = (in_port_t)cfg_obj_asuint32(obj);
+
+ CHECK(cfg_tls_storage_load(config, &tls_storage));
+ CHECK(cfg_http_storage_load(config, &http_storage));
+
/*
* If bind.keys exists, load it. If "dnssec-validation auto"
* is turned on, the root key found there will be used as a
/* check return code? */
(void)ns_listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
- named_g_mctx, AF_INET, &listenon);
+ named_g_mctx, AF_INET, &http_storage,
+ &tls_storage, &listenon);
} else {
/*
* Not specified, use default.
/* check return code? */
(void)ns_listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
- named_g_mctx, AF_INET6, &listenon);
+ named_g_mctx, AF_INET6, &http_storage,
+ &tls_storage, &listenon);
} else {
/*
* Not specified, use default.
isc_task_endexclusive(server->task);
}
+ cfg_http_storage_uninit(&http_storage);
+ cfg_tls_storage_uninit(&tls_storage);
+
isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
"load_configuration: %s", isc_result_totext(result));
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) {
+ uint16_t family, isc_cfg_http_storage_t *http_servers,
+ isc_cfg_tls_data_storage_t *tls_storage,
+ ns_listenlist_t **target) {
isc_result_t result;
const cfg_listelt_t *element;
ns_listenlist_t *dlist = NULL;
ns_listenelt_t *delt = NULL;
const cfg_obj_t *listener = cfg_listelt_value(element);
result = ns_listenelt_fromconfig(listener, config, actx, mctx,
- family, &delt);
+ family, http_servers,
+ tls_storage, &delt);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
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) {
+ uint16_t family, isc_cfg_http_storage_t *http_servers,
+ isc_cfg_tls_data_storage_t *tls_storage,
+ ns_listenelt_t **target) {
isc_result_t result;
- const cfg_obj_t *tlsobj, *portobj, *dscpobj;
- in_port_t port;
+ const cfg_obj_t *tlsobj, *portobj, *dscpobj, *httpobj;
+ in_port_t port = 0;
isc_dscp_t dscp = -1;
const char *key = NULL, *cert = NULL;
- bool tls = false;
+ bool tls = false, http = false;
ns_listenelt_t *delt = NULL;
+ isc_cfg_http_obj_t *http_server = NULL;
+ isc_cfg_tls_obj_t *tls_cert = NULL;
REQUIRE(target != NULL && *target == NULL);
/* XXXWPK TODO be more verbose on failures. */
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;
- }
+ tls_cert = cfg_tls_storage_find(
+ cfg_obj_asstring(tlsobj), tls_storage);
+ if (tls_cert != NULL) {
+ tls = true;
+ key = tls_cert->key_file;
+ cert = tls_cert->cert_file;
+ INSIST(key != NULL);
+ INSIST(cert != NULL);
}
}
if (!tls) {
return (ISC_R_FAILURE);
}
}
+ httpobj = cfg_tuple_get(listener, "http");
+ if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
+ if (tls && tls_cert == NULL) {
+ return (ISC_R_FAILURE);
+ }
+ http = true;
+ http_server = cfg_http_find(cfg_obj_asstring(httpobj),
+ http_servers);
+ if (http_server == NULL) {
+ isc_log_write(
+ named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "HTTP(S) server \"%s\" is nowhere to be found",
+ cfg_obj_asstring(httpobj));
+ return (ISC_R_FAILURE);
+ }
+ }
portobj = cfg_tuple_get(listener, "port");
if (!cfg_obj_isuint32(portobj)) {
- if (tls) {
+ if (http && tls) {
+ if (named_g_http_secure_port != 0) {
+ port = named_g_http_secure_port;
+ } else {
+ result = named_config_getport(
+ config, "https-port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ } else if (http && !tls) {
+ if (named_g_http_port != 0) {
+ port = named_g_port;
+ } else {
+ result = named_config_getport(
+ config, "http-port", &port);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ } else if (tls) {
if (named_g_tlsport != 0) {
port = named_g_tlsport;
} else {
dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
}
- result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key, cert,
- &delt);
+ if (http) {
+ INSIST(http_server != NULL);
+ result = ns_listenelt_from_http(http_server, tls_cert, port,
+ mctx, &delt);
+ } else {
+ result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key,
+ cert, &delt);
+ }
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
+/*
+ * Create a listen list for HTTP/HTTPS
+ */
+static isc_result_t
+ns_listenelt_from_http(isc_cfg_http_obj_t *http, isc_cfg_tls_obj_t *tls,
+ in_port_t port, isc_mem_t *mctx,
+ ns_listenelt_t **target) {
+ isc_result_t result = ISC_R_SUCCESS;
+ ns_listenelt_t *delt = NULL;
+ const char *key = NULL, *cert = NULL;
+ char **http_endpoints = NULL;
+ size_t http_endpoints_number;
+ isc_cfg_http_endpoint_t *ep;
+ size_t i = 0;
+ REQUIRE(target != NULL && *target == NULL);
+
+ if (tls) {
+ INSIST(tls->key_file != NULL);
+ INSIST(tls->cert_file != NULL);
+ key = tls->key_file;
+ cert = tls->cert_file;
+ }
+
+ if (port == 0) {
+ port = tls != NULL ? named_g_http_secure_port
+ : named_g_http_port;
+ }
+
+ for (ep = ISC_LIST_HEAD(http->endpoints), i = 0; ep != NULL;
+ ep = ISC_LIST_NEXT(ep, link), i++)
+ ;
+
+ INSIST(i > 0);
+
+ http_endpoints_number = i;
+ http_endpoints = isc_mem_allocate(mctx, sizeof(http_endpoints[0]) *
+ http_endpoints_number);
+ for (ep = ISC_LIST_HEAD(http->endpoints), i = 0; ep != NULL;
+ ep = ISC_LIST_NEXT(ep, link), i++)
+ {
+ http_endpoints[i] = isc_mem_strdup(mctx, ep->path);
+ }
+
+ INSIST(i == http_endpoints_number);
+
+ result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, key,
+ cert, http_endpoints,
+ http_endpoints_number, &delt);
+
+ if (result != ISC_R_SUCCESS) {
+ if (delt != NULL) {
+ ns_listenelt_destroy(delt);
+ }
+ return result;
+ }
+
+ *target = delt;
+ return (result);
+}
+
isc_result_t
named_server_dumpstats(named_server_t *server) {
isc_result_t result;
cert-file "cert.pem";
};
+http local-http-server {
+ endpoints { "/dns-query"; };
+};
+
options {
listen-on { 10.53.0.1; };
- https-server local-server port 443 tls local-tls { 10.53.0.1; };
- https-endpoint "/dns-query" https-server local-server;
+ 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; };
};
+++ /dev/null
-/*
- * 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 { 10.53.0.1; };
- https-server local-server port 443 tls local-tls { 10.53.0.1; };
-};
-
-view one {
- https-endpoint "/dns-query" https-server local-server;
-};
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" \
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)"
echostart "S:$systest:$(date_with_args)"
echoinfo "T:$systest:1:A"
echoinfo "A:$systest:System test $systest"
-echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${HTTPSPORT},${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."
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.
heartbeat\-interval integer;
hostname ( quoted_string | none );
https\-endpoint quoted_string https\-server string;
+ http\-port integer;
https\-port integer;
https\-server string [ port integer ] tls string { (
quoted_string [ port integer ] [ dscp integer ] |
dyndb <string> <quoted_string> {
<unspecified-text> }; // may occur multiple times
+http <string> {
+ endpoints { <quoted_string>; ... }; // experimental
+}; // may occur multiple times
+
key <string> {
algorithm <string>;
secret <string>;
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
- https-endpoint <quoted_string> https-server <string>;
+ http-port <integer>;
https-port <integer>;
- https-server <string> [ port <integer> ] tls <string> { (
- <quoted_string> [ port <integer> ] [ dscp <integer> ] |
- <ipv4_address> [ port <integer> ] [ dscp <integer> ] |
- <ipv6_address> [ port <integer> ] [ dscp <integer> ] ); ... };
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
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 );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
| <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... };
glue-cache <boolean>; // deprecated
- https-endpoint <quoted_string> https-server <string>;
inline-signing <boolean>;
ipv4only-contact <string>;
ipv4only-enable <boolean>;
dyndb <string> <quoted_string> {
<unspecified-text> }; // may occur multiple times
+http <string> {
+ endpoints { <quoted_string>; ... }; // experimental
+}; // may occur multiple times
+
key <string> {
algorithm <string>;
secret <string>;
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
- https-endpoint <quoted_string> https-server <string>;
+ http-port <integer>;
https-port <integer>;
- https-server <string> [ port <integer> ] tls <string> { (
- <quoted_string> [ port <integer> ] [ dscp <integer> ] |
- <ipv4_address> [ port <integer> ] [ dscp <integer> ] |
- <ipv6_address> [ port <integer> ] [ dscp <integer> ] ); ... };
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
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 );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
| <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... };
glue-cache <boolean>; // deprecated
- https-endpoint <quoted_string> https-server <string>;
inline-signing <boolean>;
ipv4only-contact <string>;
ipv4only-enable <boolean>;
glue-cache <boolean>; // deprecated
heartbeat-interval <integer>;
hostname ( <quoted_string> | none );
- https-endpoint <quoted_string> https-server <string>;
+ http-port <integer>;
https-port <integer>;
- https-server <string> [ port <integer> ] tls <string> { (
- <quoted_string> [ port <integer> ] [ dscp <integer> ] |
- <ipv4_address> [ port <integer> ] [ dscp <integer> ] |
- <ipv6_address> [ port <integer> ] [ dscp <integer> ] ); ... };
inline-signing <boolean>;
interface-interval <duration>;
ipv4only-contact <string>;
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 );
an optional ``tls`` option which specifies either a previously configured
``tls`` statement or ``ephemeral``. [GL #2392]
+- ``named`` now has initial support for DNS-over-HTTP(S). Both
+ encrypted (via TLS) and unencrypted HTTP/2 connections are supported.
+ The latter are mostly there for debugging/troubleshooting
+ purposes and for the means of encryption offloading to third-party
+ software (as might be desirable in some environments to aid in TLS
+ certificates management).
+
Removed Features
~~~~~~~~~~~~~~~~
*/
typedef void (*isc_nm_http_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
- isc_region_t *postdata, isc_region_t *getdata,
- void *cbarg);
+ 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.
- * 'postdata' contains the received POST data, if any. It will be freed
+ * 'data' contains the received data, if any. It will be freed
* after return by caller.
- * 'getdata' contains the received GET data (past '?'), if any. It will be
- * freed after return by caller.
* 'cbarg' the callback argument passed to listen function.
*/
isc_result_t
-isc_nm_doh_request(isc_nm_t *mgr, const char *uri, isc_region_t *message,
- isc_nm_recv_cb_t cb, void *cbarg, isc_ssl_ctx_t *ctx);
+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_httpsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
- const char *uri, isc_nm_cb_t cb, void *cbarg,
- unsigned int timeout, size_t extrahandlesize);
+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_listenhttps(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
- isc_quota_t *quota, isc_ssl_ctx_t *ctx,
- isc_nmsocket_t **sockp);
+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,
* information regarding copyright ownership.
*/
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
#include <nghttp2/nghttp2.h>
#include <signal.h>
#include <string.h>
#include <isc/base64.h>
#include <isc/netmgr.h>
#include <isc/print.h>
+#include <isc/tls.h>
#include <isc/url.h>
#include "netmgr-int.h"
#define AUTHEXTRA 7
+#define MAX_DNS_MESSAGE_SIZE (UINT16_MAX)
+
+#define HEADER_MATCH(header, name, namelen) \
+ (((namelen) == sizeof(header) - 1) && \
+ (strncasecmp((header), (const char *)(name), (namelen)) == 0))
+
+typedef struct isc_nm_http2_response_status {
+ size_t code;
+ size_t content_length;
+ bool content_type_valid;
+} isc_nm_http2_response_status_t;
+
typedef struct http2_client_stream {
- isc_nm_recv_cb_t cb;
- void *cbarg;
+ isc_nm_recv_cb_t read_cb;
+ void *read_cbarg;
+
+ isc_nm_cb_t connect_cb;
+ void *connect_cbarg;
char *uri;
isc_url_parser_t up;
size_t authoritylen;
char *path;
- uint8_t rbuf[65535];
+ uint8_t rbuf[MAX_DNS_MESSAGE_SIZE];
size_t rbufsize;
size_t pathlen;
int32_t stream_id;
- isc_region_t *postdata;
+
+ bool post; /* POST or GET */
+ isc_region_t postdata;
size_t postdata_pos;
+ char *GET_path;
+ size_t GET_path_len;
+
+ isc_nm_http2_response_status_t response_status;
+
+ LINK(struct http2_client_stream) link;
} http2_client_stream_t;
#define HTTP2_SESSION_MAGIC ISC_MAGIC('H', '2', 'S', 'S')
bool reading;
nghttp2_session *ngsession;
- http2_client_stream_t *cstream;
+ bool client;
+
+ ISC_LIST(http2_client_stream_t) cstreams;
ISC_LIST(isc_nmsocket_h2_t) sstreams;
+#ifdef NETMGR_TRACE
+ size_t sstreams_count;
+#endif /* NETMGR_TRACE */
isc_nmhandle_t *handle;
isc_nmsocket_t *serversocket;
isc_region_t r;
- uint8_t buf[65535];
+ uint8_t buf[MAX_DNS_MESSAGE_SIZE];
size_t bufsize;
- SSL_CTX *ctx;
+ bool ssl_ctx_created;
+ SSL_CTX *ssl_ctx;
};
+typedef enum isc_http2_error_responses {
+ ISC_HTTP_ERROR_SUCCESS, /* 200 */
+ ISC_HTTP_ERROR_NOT_FOUND, /* 404 */
+ ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, /* 413 */
+ ISC_HTTP_ERROR_URI_TOO_LONG, /* 414 */
+ ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, /* 415 */
+ ISC_HTTP_ERROR_BAD_REQUEST, /* 400 */
+ ISC_HTTP_ERROR_NOT_IMPLEMENTED, /* 501 */
+ ISC_HTTP_ERROR_GENERIC, /* 500 Internal Server Error */
+ ISC_HTTP_ERROR_MAX
+} isc_http2_error_responses_t;
+
static bool
+inactive(isc_nmsocket_t *sock) {
+ return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
+ atomic_load(&sock->mgr->closing) ||
+ (sock->server != NULL && !isc__nmsocket_active(sock->server)));
+}
+
+static void
http2_do_bio(isc_nm_http2_session_t *session);
+static void
+failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ isc_nm_http2_session_t *session);
+
+static void
+failed_read_cb(isc_result_t result, isc_nm_http2_session_t *session);
+
+static http2_client_stream_t *
+find_http2_client_stream(int32_t stream_id, isc_nm_http2_session_t *session) {
+ http2_client_stream_t *cstream = NULL;
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ for (cstream = ISC_LIST_HEAD(session->cstreams); cstream != NULL;
+ cstream = ISC_LIST_NEXT(cstream, link))
+ {
+ if (cstream->stream_id == stream_id) {
+ break;
+ }
+ }
+
+ return cstream;
+}
+
static isc_result_t
get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp,
- const char *uri, uint16_t *port) {
+ const char *uri) {
http2_client_stream_t *stream = NULL;
- int rv;
+ isc_result_t result;
REQUIRE(streamp != NULL && *streamp == NULL);
REQUIRE(uri != NULL);
- REQUIRE(port != NULL);
stream = isc_mem_get(mctx, sizeof(http2_client_stream_t));
- *stream = (http2_client_stream_t){ .stream_id = -1 };
+ *stream = (http2_client_stream_t){ .stream_id = -1, .rbufsize = 0 };
stream->uri = isc_mem_strdup(mctx, uri);
- rv = isc_url_parse(stream->uri, strlen(stream->uri), 0, &stream->up);
- if (rv != 0) {
- isc_mem_put(mctx, stream, sizeof(http2_client_stream_t));
+ result = isc_url_parse(stream->uri, strlen(stream->uri), 0,
+ &stream->up);
+ if (result != ISC_R_SUCCESS) {
isc_mem_free(mctx, stream->uri);
- return (ISC_R_FAILURE);
+ isc_mem_put(mctx, stream, sizeof(http2_client_stream_t));
+ return (result);
}
stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
stream->up.field_data[ISC_UF_QUERY].len);
}
- *port = 443;
- if ((stream->up.field_set & (1 << ISC_UF_PORT)) != 0) {
- *port = stream->up.port;
- }
+ stream->GET_path = NULL;
+ stream->GET_path_len = 0;
+ stream->postdata = (isc_region_t){ .base = NULL, .length = 0 };
+ memset(&stream->response_status, 0, sizeof(stream->response_status));
*streamp = stream;
static void
put_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t *stream) {
isc_mem_put(mctx, stream->path, stream->pathlen);
- isc_mem_put(mctx, stream->authority, stream->authoritylen + AUTHEXTRA);
+ isc_mem_put(mctx, stream->authority,
+ stream->up.field_data[ISC_UF_HOST].len + AUTHEXTRA);
+ isc_mem_free(mctx, stream->uri);
+ if (stream->GET_path) {
+ isc_mem_free(mctx, stream->GET_path);
+ stream->GET_path = NULL;
+ stream->GET_path_len = 0;
+ }
+ if (stream->postdata.base != NULL) {
+ isc_mem_put(mctx, stream->postdata.base,
+ stream->postdata.length);
+ }
isc_mem_put(mctx, stream, sizeof(http2_client_stream_t));
}
static void
delete_http2_session(isc_nm_http2_session_t *session) {
+ INSIST(ISC_LIST_EMPTY(session->sstreams));
+ INSIST(ISC_LIST_EMPTY(session->cstreams));
+ session->magic = 0;
+ if (session->ssl_ctx_created) {
+ SSL_CTX_free(session->ssl_ctx);
+ }
+ isc_mem_putanddetach(&session->mctx, session,
+ sizeof(isc_nm_http2_session_t));
+}
+
+static void
+finish_http2_session(isc_nm_http2_session_t *session) {
if (session->handle != NULL) {
isc_nm_pauseread(session->handle);
isc_nmhandle_detach(&session->handle);
nghttp2_session_del(session->ngsession);
session->ngsession = NULL;
}
- if (session->cstream != NULL) {
- put_http2_client_stream(session->mctx, session->cstream);
- session->cstream = NULL;
+ if (!ISC_LIST_EMPTY(session->cstreams)) {
+ http2_client_stream_t *cstream =
+ ISC_LIST_HEAD(session->cstreams);
+ while (cstream != NULL) {
+ http2_client_stream_t *next = ISC_LIST_NEXT(cstream,
+ link);
+ ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
+ put_http2_client_stream(session->mctx, cstream);
+
+ cstream = next;
+ }
+ }
+ INSIST(ISC_LIST_EMPTY(session->cstreams));
+
+ /* detach from server socket */
+ if (session->serversocket != NULL) {
+ isc__nmsocket_detach(&session->serversocket);
}
/*
*/
if (session->sending) {
session->closed = true;
- } else if (!session->reading) {
- session->magic = 0;
- isc_mem_putanddetach(&session->mctx, session,
- sizeof(isc_nm_http2_session_t));
}
}
UNUSED(ngsession);
UNUSED(flags);
- if (session->cstream != NULL) {
- if (session->cstream->stream_id == stream_id) {
- /* TODO buffer overrun! */
- memmove(session->cstream->rbuf +
- session->cstream->rbufsize,
- data, len);
- session->cstream->rbufsize += len;
+ if (session->client) {
+ http2_client_stream_t *cstream =
+ find_http2_client_stream(stream_id, session);
+ if (cstream) {
+ size_t new_rbufsize = cstream->rbufsize + len;
+ if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_rbufsize <=
+ cstream->response_status.content_length)
+ {
+ memmove(cstream->rbuf + cstream->rbufsize, data,
+ len);
+ cstream->rbufsize = new_rbufsize;
+ } else {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+ } else {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
} else {
isc_nmsocket_h2_t *sock_h2 = ISC_LIST_HEAD(session->sstreams);
while (sock_h2 != NULL) {
if (stream_id == sock_h2->stream_id) {
- memmove(sock_h2->buf + sock_h2->bufsize,
- data, len);
- sock_h2->bufsize += len;
+ size_t new_bufsize = sock_h2->bufsize + len;
+ if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_bufsize <= sock_h2->content_length)
+ {
+ memmove(sock_h2->buf + sock_h2->bufsize,
+ data, len);
+ sock_h2->bufsize = new_bufsize;
+ } else {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
break;
}
sock_h2 = ISC_LIST_NEXT(sock_h2, link);
}
+ if (sock_h2 == NULL) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
}
return (0);
on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
uint32_t error_code, void *user_data) {
isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ int rv = 0;
REQUIRE(VALID_HTTP2_SESSION(session));
UNUSED(error_code);
- if (session->cstream != NULL) {
- if (session->cstream->stream_id == stream_id) {
- int rv;
-
- session->cstream->cb(
- NULL, ISC_R_SUCCESS,
- &(isc_region_t){ session->cstream->rbuf,
- session->cstream->rbufsize },
- session->cstream->cbarg);
- rv = nghttp2_session_terminate_session(
- ngsession, NGHTTP2_NO_ERROR);
- if (rv != 0) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ /* NOTE: calling isc_nm_cancelread() or
+ * isc__nmsocket_prep_destroy() on a socket will lead to an
+ * indirect call to the delete_http2_session() which will, in
+ * turn, perform required stream session cleanup.*/
+ if (session->client) {
+ http2_client_stream_t *cstream =
+ find_http2_client_stream(stream_id, session);
+ if (cstream) {
+ isc_result_t result =
+ cstream->response_status.code >= 200 &&
+ cstream->response_status.code <
+ 300
+ ? ISC_R_SUCCESS
+ : ISC_R_FAILURE;
+ cstream->read_cb(session->handle, result,
+ &(isc_region_t){ cstream->rbuf,
+ cstream->rbufsize },
+ cstream->read_cbarg);
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ put_http2_client_stream(session->mctx, cstream);
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ rv = nghttp2_session_terminate_session(
+ ngsession, NGHTTP2_NO_ERROR);
+ if (rv != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+ if (session->handle->sock->h2.session->reading)
+ {
+ isc_nm_cancelread(
+ session->handle->sock->h2
+ .session->handle);
+ }
}
+ } else {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
}
} else {
- /* XXX */
+ isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
+ ngsession, stream_id);
+ if (ISC_LIST_EMPTY(session->sstreams)) {
+ rv = nghttp2_session_terminate_session(
+ ngsession, NGHTTP2_NO_ERROR);
+ }
+ isc__nmsocket_prep_destroy(sock);
+ if (rv != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
}
- /* XXXWPK TODO we need to close the session */
-
return (0);
}
#ifndef OPENSSL_NO_NEXTPROTONEG
/*
* NPN TLS extension client callback. We check that server advertised
- * the HTTP/2 protocol the nghttp2 library supports. If not, exit the
- * program.
+ * the HTTP/2 protocol the nghttp2 library supports.
*/
static int
select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
UNUSED(arg);
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
- /* TODO */
+ return (SSL_TLSEXT_ERR_NOACK);
}
return (SSL_TLSEXT_ERR_OK);
}
/* Create SSL_CTX. */
static SSL_CTX *
-create_ssl_ctx(void) {
+create_client_ssl_ctx(void) {
SSL_CTX *ssl_ctx = NULL;
- ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ RUNTIME_CHECK(isc_tlsctx_createclient(&ssl_ctx) == ISC_R_SUCCESS);
RUNTIME_CHECK(ssl_ctx != NULL);
- SSL_CTX_set_options(
- ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
- SSL_OP_NO_COMPRESSION |
- SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
#ifndef OPENSSL_NO_NEXTPROTONEG
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
#endif /* !OPENSSL_NO_NEXTPROTONEG */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
+ SSL_CTX_set_alpn_protos(ssl_ctx,
+ (const unsigned char *)NGHTTP2_PROTO_ALPN,
+ NGHTTP2_PROTO_ALPN_LEN);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
return (ssl_ctx);
}
+static void
+client_handle_status_header(http2_client_stream_t *cstream,
+ const uint8_t *value, const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+ strncpy(tmp, (const char *)value,
+ valuelen > tmplen ? tmplen : valuelen);
+ cstream->response_status.code = strtoul(tmp, NULL, 10);
+}
+
+static void
+client_handle_content_length_header(http2_client_stream_t *cstream,
+ const uint8_t *value,
+ const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+ strncpy(tmp, (const char *)value,
+ valuelen > tmplen ? tmplen : valuelen);
+ cstream->response_status.content_length = strtoul(tmp, NULL, 10);
+}
+
+static void
+client_handle_content_type_header(http2_client_stream_t *cstream,
+ const uint8_t *value, const size_t valuelen) {
+ const char type_dns_message[] = "application/dns-message";
+ UNUSED(valuelen);
+ if (strncasecmp((const char *)value, type_dns_message,
+ sizeof(type_dns_message) - 1) == 0)
+ {
+ cstream->response_status.content_type_valid = true;
+ }
+}
+
+static int
+client_on_header_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, const uint8_t *name,
+ size_t namelen, const uint8_t *value, size_t valuelen,
+ uint8_t flags, void *user_data) {
+ isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ const char status[] = ":status";
+ const char content_length[] = "Content-Length";
+ const char content_type[] = "Content-Type";
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(session->client);
+ REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
+
+ UNUSED(flags);
+ UNUSED(ngsession);
+
+ http2_client_stream_t *cstream =
+ find_http2_client_stream(frame->hd.stream_id, session);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
+ break;
+ }
+
+ if (HEADER_MATCH(status, name, namelen)) {
+ client_handle_status_header(cstream, value, valuelen);
+ } else if (HEADER_MATCH(content_length, name, namelen)) {
+ client_handle_content_length_header(cstream, value,
+ valuelen);
+ } else if (HEADER_MATCH(content_type, name, namelen)) {
+ client_handle_content_type_header(cstream, value,
+ valuelen);
+ }
+ break;
+ }
+
+ return (0);
+}
+
static void
initialize_nghttp2_client_session(isc_nm_http2_session_t *session) {
nghttp2_session_callbacks *callbacks = NULL;
- nghttp2_session_callbacks_new(&callbacks);
+ RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
+ nghttp2_session_callbacks_set_on_header_callback(
+ callbacks, client_on_header_callback);
+
nghttp2_session_client_new(&session->ngsession, callbacks, session);
nghttp2_session_callbacks_del(callbacks);
}
-static void
+static bool
send_client_connection_header(isc_nm_http2_session_t *session) {
- nghttp2_settings_entry iv[1] = {
- { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
- };
+ nghttp2_settings_entry iv[] = { { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } };
int rv;
rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
- 1);
+ sizeof(iv) / sizeof(iv[0]));
if (rv != 0) {
- /* TODO */
+ return false;
}
- http2_do_bio(session);
+ return true;
}
#define MAKE_NV(NAME, VALUE, VALUELEN) \
}
static ssize_t
-client_post_read_callback(nghttp2_session *ngsession, int32_t stream_id,
- uint8_t *buf, size_t length, uint32_t *data_flags,
- nghttp2_data_source *source, void *user_data) {
+client_read_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint8_t *buf, size_t length, uint32_t *data_flags,
+ nghttp2_data_source *source, void *user_data) {
isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data;
+ http2_client_stream_t *cstream;
- REQUIRE(session->cstream != NULL);
+ REQUIRE(session->client);
+ REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
UNUSED(ngsession);
UNUSED(source);
- if (session->cstream->stream_id == stream_id) {
- size_t len = session->cstream->postdata->length -
- session->cstream->postdata_pos;
+ cstream = find_http2_client_stream(stream_id, session);
+ if (!cstream || cstream->stream_id != stream_id) {
+ /* We haven't found the stream, so we are not reading anything
+ */
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ if (cstream->post) {
+ size_t len = cstream->postdata.length - cstream->postdata_pos;
if (len > length) {
len = length;
}
- memmove(buf,
- session->cstream->postdata->base +
- session->cstream->postdata_pos,
+ memmove(buf, cstream->postdata.base + cstream->postdata_pos,
len);
- session->cstream->postdata_pos += len;
+ cstream->postdata_pos += len;
- if (session->cstream->postdata_pos ==
- session->cstream->postdata->length) {
+ if (cstream->postdata_pos == cstream->postdata.length) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
return (len);
+ } else {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ return (0);
}
return (0);
/* Send HTTP request to the remote peer */
static isc_result_t
-client_submit_request(isc_nm_http2_session_t *session) {
+client_submit_request(isc_nm_http2_session_t *session,
+ http2_client_stream_t *stream) {
int32_t stream_id;
- http2_client_stream_t *stream = session->cstream;
char *uri = stream->uri;
isc_url_parser_t *up = &stream->up;
nghttp2_data_provider dp;
- char p[64];
-
- snprintf(p, 64, "%u", stream->postdata->length);
-
- nghttp2_nv hdrs[] = {
- MAKE_NV2(":method", "POST"),
- MAKE_NV(":scheme", &uri[up->field_data[ISC_UF_SCHEMA].off],
- up->field_data[ISC_UF_SCHEMA].len),
- MAKE_NV(":authority", stream->authority, stream->authoritylen),
- MAKE_NV(":path", stream->path, stream->pathlen),
- MAKE_NV2("content-type", "application/dns-message"),
- MAKE_NV2("accept", "application/dns-message"),
- MAKE_NV("content-length", p, strlen(p)),
- };
-
- dp = (nghttp2_data_provider){ .read_callback =
- client_post_read_callback };
- stream_id = nghttp2_submit_request(session->ngsession, NULL, hdrs, 7,
- &dp, stream);
+ if (stream->post) {
+ char p[64];
+ snprintf(p, sizeof(p), "%u", stream->postdata.length);
+ nghttp2_nv hdrs[] = {
+ MAKE_NV2(":method", "POST"),
+ MAKE_NV(":scheme",
+ &uri[up->field_data[ISC_UF_SCHEMA].off],
+ up->field_data[ISC_UF_SCHEMA].len),
+ MAKE_NV(":authority", stream->authority,
+ stream->authoritylen),
+ MAKE_NV(":path", stream->path, stream->pathlen),
+ MAKE_NV2("content-type", "application/dns-message"),
+ MAKE_NV2("accept", "application/dns-message"),
+ MAKE_NV("content-length", p, strlen(p)),
+ };
+
+ dp = (nghttp2_data_provider){ .read_callback =
+ client_read_callback };
+ stream_id = nghttp2_submit_request(
+ session->ngsession, NULL, hdrs,
+ sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
+ } else {
+ INSIST(stream->GET_path != NULL);
+ INSIST(stream->GET_path_len != 0);
+ nghttp2_nv hdrs[] = {
+ MAKE_NV2(":method", "GET"),
+ MAKE_NV(":scheme",
+ &uri[up->field_data[ISC_UF_SCHEMA].off],
+ up->field_data[ISC_UF_SCHEMA].len),
+ MAKE_NV(":authority", stream->authority,
+ stream->authoritylen),
+ MAKE_NV(":path", stream->GET_path,
+ stream->GET_path_len),
+ MAKE_NV2("accept", "application/dns-message")
+ };
+
+ dp = (nghttp2_data_provider){ .read_callback =
+ client_read_callback };
+ stream_id = nghttp2_submit_request(
+ session->ngsession, NULL, hdrs,
+ sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
+ }
if (stream_id < 0) {
return (ISC_R_FAILURE);
}
REQUIRE(VALID_HTTP2_SESSION(session));
UNUSED(handle);
- UNUSED(result);
if (result != ISC_R_SUCCESS) {
session->reading = false;
- delete_http2_session(session);
- /* TODO callback! */
+ failed_read_cb(result, session);
return;
}
readlen = nghttp2_session_mem_recv(session->ngsession, region->base,
region->length);
if (readlen < 0) {
- delete_http2_session(session);
- /* TODO callback! */
+ failed_read_cb(ISC_R_CANCELED, session);
return;
}
if (readlen < region->length) {
INSIST(session->bufsize == 0);
- INSIST(region->length - readlen < 65535);
+ INSIST(region->length - readlen < MAX_DNS_MESSAGE_SIZE);
memmove(session->buf, region->base, region->length - readlen);
session->bufsize = region->length - readlen;
isc_nm_pauseread(session->handle);
REQUIRE(VALID_HTTP2_SESSION(session));
UNUSED(handle);
- UNUSED(result);
session->sending = false;
isc_mem_put(session->mctx, session->r.base, session->r.length);
session->r.base = NULL;
- http2_do_bio(session);
+ if (result == ISC_R_SUCCESS) {
+ http2_do_bio(session);
+ }
}
-static bool
+static void
http2_do_bio(isc_nm_http2_session_t *session) {
REQUIRE(VALID_HTTP2_SESSION(session));
(nghttp2_session_want_read(session->ngsession) == 0 &&
nghttp2_session_want_write(session->ngsession) == 0))
{
- delete_http2_session(session);
- return (false);
+ finish_http2_session(session);
+ return;
}
if (nghttp2_session_want_read(session->ngsession) != 0) {
}
http2_do_bio(session);
- return (false);
+ return;
} else {
/* Resume reading, it's idempotent, wait for more */
isc_nm_resumeread(session->handle);
session->sending = true;
isc_nm_send(session->handle, &session->r, https_writecb,
session);
- return (true);
+ return;
}
- return (false);
+ return;
}
+typedef struct http_connect_data {
+ char *uri;
+ isc_nm_cb_t connect_cb;
+ void *connect_cbarg;
+ bool post;
+ bool ssl_ctx_created;
+ SSL_CTX *ssl_ctx;
+} http_connect_data_t;
+
static void
-https_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
- isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)arg;
+transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ char *uri = NULL;
+ http_connect_data_t *pconn_data = (http_connect_data_t *)cbarg;
+ http_connect_data_t conn_data;
+ isc_nm_http2_session_t *session = NULL;
+ isc_mem_t *mctx;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(cbarg != NULL);
+
+ handle->sock->h2.session = NULL;
+ mctx = handle->sock->mgr->mctx;
+
+ conn_data = *pconn_data;
+ uri = conn_data.uri;
+ isc_mem_put(mctx, pconn_data, sizeof(*pconn_data));
+
+ INSIST(conn_data.connect_cb != NULL);
+ INSIST(conn_data.uri != NULL);
if (result != ISC_R_SUCCESS) {
- delete_http2_session(session);
- return;
+ goto error;
}
- isc_nmhandle_attach(handle, &session->handle);
-
-#if 0
-/* TODO H2 */
+ session = isc_mem_get(mctx, sizeof(isc_nm_http2_session_t));
+ *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
+#ifdef NETMGR_TRACE
+ .sstreams_count = 0,
+#endif /* NETMGR_TRACE */
+ .ssl_ctx_created =
+ conn_data.ssl_ctx_created,
+ .ssl_ctx = conn_data.ssl_ctx,
+ .handle = NULL,
+ .client = true };
+ isc_mem_attach(mctx, &session->mctx);
+
+ handle->sock->h2.connect.uri = uri;
+ handle->sock->h2.connect.post = conn_data.post;
+
+ session->ssl_ctx = conn_data.ssl_ctx;
+ conn_data.ssl_ctx = NULL;
+ session->ssl_ctx_created = conn_data.ssl_ctx_created;
+ conn_data.ssl_ctx_created = false;
+
+ if (session->ssl_ctx != NULL) {
+ const unsigned char *alpn = NULL;
+ unsigned int alpnlen = 0;
+ SSL *ssl = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ ssl = handle->sock->tlsstream.ssl;
+ INSIST(ssl != NULL);
#ifndef OPENSSL_NO_NEXTPROTONEG
- SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
+ SSL_get0_next_proto_negotiated(handle->sock->tlsstream.ssl,
+ &alpn, &alpnlen);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- if (alpn == NULL) {
- SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
- }
+ if (alpn == NULL) {
+ SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
+ }
#endif
- if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
- delete_http2_session(session);
- return;
+ if (alpn == NULL || alpnlen != 2 ||
+ memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
+ NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
+ {
+ result = ISC_R_CANCELED;
+ goto error;
+ }
}
-#endif
+
+ isc_nmhandle_attach(handle, &session->handle);
+ handle->sock->h2.session = session;
initialize_nghttp2_client_session(session);
- send_client_connection_header(session);
- client_submit_request(session);
- http2_do_bio(session);
+ if (!send_client_connection_header(session)) {
+ handle->sock->h2.session = NULL;
+ goto error;
+ }
+ conn_data.connect_cb(handle, ISC_R_SUCCESS, conn_data.connect_cbarg);
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ delete_http2_session(session);
+ isc__nmsocket_prep_destroy(handle->sock);
+ } else {
+ http2_do_bio(session);
+ }
+
+ return;
+error:
+ conn_data.connect_cb(handle, result, conn_data.connect_cbarg);
+ if (conn_data.ssl_ctx_created && conn_data.ssl_ctx) {
+ SSL_CTX_free(conn_data.ssl_ctx);
+ }
+
+ if (session != NULL) {
+ delete_http2_session(session);
+ }
+
+ if (uri != NULL) {
+ isc_mem_free(mctx, uri);
+ }
}
isc_result_t
-isc_nm_httpsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
- const char *uri, isc_nm_cb_t cb, void *cbarg,
- unsigned int timeout, size_t extrahandlesize) {
+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,
+ SSL_CTX *ctx, unsigned int timeout, size_t extrahandlesize) {
+ isc_nmiface_t resolved_peer, resolved_local;
+ http_connect_data_t *pconn_data;
+ isc_result_t result;
+ uint16_t port = 80;
+ isc_url_parser_t url_parser;
+ bool create_ssl_ctx;
+ const char http[] = "http";
+ const char http_secured[] = "https";
+ size_t schema_len;
+ const char *schema;
+
REQUIRE(VALID_NM(mgr));
+ REQUIRE(cb != NULL);
+ REQUIRE(uri != NULL);
+ REQUIRE(*uri != '\0');
+
+ result = isc_url_parse(uri, strlen(uri), 0, &url_parser);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ schema_len = url_parser.field_data[ISC_UF_SCHEMA].len;
+ INSIST(schema_len > 0);
+ schema = &uri[url_parser.field_data[ISC_UF_SCHEMA].off];
+
+ if (schema_len == sizeof(http_secured) - 1 &&
+ strncasecmp(http_secured, schema, sizeof(http_secured) - 1) == 0)
+ {
+ create_ssl_ctx = true;
+ } else if (schema_len == sizeof(http) - 1 &&
+ strncasecmp(http, schema, sizeof(http) - 1) == 0)
+ {
+ create_ssl_ctx = false;
+ } else {
+ INSIST(0);
+ ISC_UNREACHABLE();
+ }
+
+ if (ctx == NULL && create_ssl_ctx) {
+ port = 443;
+ ctx = create_client_ssl_ctx();
+ INSIST(ctx != NULL);
+ }
- UNUSED(local);
- UNUSED(peer);
- UNUSED(uri);
- UNUSED(cb);
- UNUSED(cbarg);
- UNUSED(timeout);
- UNUSED(extrahandlesize);
+ if (peer == NULL || local == NULL) {
+ size_t hostlen = 0;
+ char *host = NULL;
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
- return (ISC_R_NOTIMPLEMENTED);
+#ifndef WIN32 /* FIXME */
+ /* extract host name */
+ hostlen = url_parser.field_data[ISC_UF_HOST].len + 1;
+ host = isc_mem_get(mgr->mctx, hostlen);
+ memmove(host, &uri[url_parser.field_data[ISC_UF_HOST].off],
+ hostlen - 1);
+ host[hostlen - 1] = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_CANONNAME;
+
+ result = getaddrinfo(host, NULL, &hints, &res);
+ isc_mem_put(mgr->mctx, host, hostlen);
+ if (result != 0) {
+ return (ISC_R_FAILURE);
+ }
+#endif /* WIN32 */
+
+ if ((url_parser.field_set & (1 << ISC_UF_PORT)) != 0) {
+ port = url_parser.port;
+ }
+
+ if (peer == NULL) {
+ isc_sockaddr_fromsockaddr(&resolved_peer.addr,
+ res->ai_addr);
+ isc_sockaddr_setport(&resolved_peer.addr, port);
+ peer = &resolved_peer;
+ }
+ if (local == NULL) {
+ isc_sockaddr_anyofpf(&resolved_local.addr,
+ res->ai_family);
+ local = &resolved_local;
+ }
+
+ freeaddrinfo(res);
+ }
+
+ pconn_data = isc_mem_get(mgr->mctx, sizeof(*pconn_data));
+ *pconn_data =
+ (http_connect_data_t){ .uri = isc_mem_strdup(mgr->mctx, uri),
+ .post = post,
+ .connect_cb = cb,
+ .connect_cbarg = cbarg,
+ .ssl_ctx_created = create_ssl_ctx,
+ .ssl_ctx = ctx };
+
+ if (ctx || create_ssl_ctx) {
+ result = isc_nm_tlsconnect(mgr, local, peer,
+ transport_connect_cb, pconn_data,
+ ctx, timeout, extrahandlesize);
+ } else {
+ result = isc_nm_tcpconnect(mgr, local, peer,
+ transport_connect_cb, pconn_data,
+ timeout, extrahandlesize);
+ }
+
+ return (result);
}
isc_result_t
-isc_nm_doh_request(isc_nm_t *mgr, const char *uri, isc_region_t *region,
- isc_nm_recv_cb_t cb, void *cbarg, SSL_CTX *ctx) {
- uint16_t port;
- char *host = NULL;
- isc_nm_http2_session_t *session = NULL;
+isc_nm_httprequest(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_recv_cb_t reply_cb, void *cbarg) {
http2_client_stream_t *cstream = NULL;
- struct addrinfo hints;
- struct addrinfo *res = NULL;
- isc_sockaddr_t local, peer;
- isc_result_t result;
- int s;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nm_http2_session_t *session = NULL;
+ isc_mem_t *mctx;
- if (ctx == NULL) {
- ctx = create_ssl_ctx();
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->tid == isc_nm_tid());
+ REQUIRE(VALID_HTTP2_SESSION(handle->sock->h2.session));
+ REQUIRE(region != NULL);
+ REQUIRE(region->base != NULL);
+ REQUIRE(region->length != 0);
+ REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
+ REQUIRE(reply_cb != NULL);
+
+ session = handle->sock->h2.session;
+ mctx = handle->sock->mgr->mctx;
+
+ result = get_http2_client_stream(mctx, &cstream,
+ handle->sock->h2.connect.uri);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
}
- session = isc_mem_get(mgr->mctx, sizeof(isc_nm_http2_session_t));
- *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
- .ctx = ctx };
- isc_mem_attach(mgr->mctx, &session->mctx);
+ cstream->read_cb = reply_cb;
+ cstream->read_cbarg = cbarg;
+ cstream->post = handle->sock->h2.connect.post;
+
+ if (cstream->post) { /* POST */
+ cstream->postdata = (isc_region_t){
+ .base = isc_mem_get(mctx, region->length),
+ .length = region->length
+ };
+ memmove(cstream->postdata.base, region->base, region->length);
+ cstream->postdata_pos = 0;
+ } else { /* GET */
+ size_t path_size = 0;
+ char *base64url_data = NULL;
+ size_t base64url_data_len = 0;
+ isc_buffer_t *buf = NULL;
+ isc_region_t data = *region;
+ isc_region_t base64_region;
+ size_t base64_len = ((4 * data.length / 3) + 3) & ~3;
+
+ isc_buffer_allocate(mctx, &buf, base64_len);
+
+ if ((result = isc_base64_totext(&data, -1, "", buf)) !=
+ ISC_R_SUCCESS) {
+ isc_buffer_free(&buf);
+ goto error;
+ }
- result = get_http2_client_stream(mgr->mctx, &cstream, uri, &port);
- if (result != ISC_R_SUCCESS) {
- delete_http2_session(session);
- return (result);
- }
+ isc__buffer_usedregion(buf, &base64_region);
+ INSIST(base64_region.length == base64_len);
- cstream->postdata = region;
- cstream->postdata_pos = 0;
- cstream->cb = cb;
- cstream->cbarg = cbarg;
+ base64url_data = isc__nm_base64_to_base64url(
+ mctx, (const char *)base64_region.base,
+ base64_region.length, &base64url_data_len);
+ isc_buffer_free(&buf);
+ if (base64url_data == NULL) {
+ goto error;
+ }
- session->cstream = cstream;
+ /* len("?dns=") + len(path) + len(base64url) + len("\0") */
+ path_size = cstream->pathlen + base64url_data_len + 5 + 1;
+ cstream->GET_path = isc_mem_allocate(mctx, path_size);
+ cstream->GET_path_len = (size_t)snprintf(
+ cstream->GET_path, path_size, "%.*s?dns=%s",
+ (int)cstream->pathlen, cstream->path, base64url_data);
-#ifndef WIN32 /* FIXME */
- hints = (struct addrinfo){ .ai_family = PF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_flags = AI_CANONNAME };
- host = isc_mem_strndup(mgr->mctx, cstream->authority,
- cstream->authoritylen + 1);
-
- s = getaddrinfo(host, NULL, &hints, &res);
- isc_mem_free(mgr->mctx, host);
- if (s != 0) {
- delete_http2_session(session);
- return (ISC_R_FAILURE);
+ INSIST(cstream->GET_path_len == (path_size - 1));
+ isc_mem_free(mctx, base64url_data);
}
-#endif /* WIN32 */
- isc_sockaddr_fromsockaddr(&peer, res->ai_addr);
- isc_sockaddr_setport(&peer, port);
- isc_sockaddr_anyofpf(&local, res->ai_family);
+ ISC_LINK_INIT(cstream, link);
+ ISC_LIST_APPEND(session->cstreams, cstream, link);
- freeaddrinfo(res);
+ INSIST(cstream != NULL);
- result = isc_nm_tlsconnect(mgr, (isc_nmiface_t *)&local,
- (isc_nmiface_t *)&peer, https_connect_cb,
- session, ctx, 30000, 0);
- /* XXX: timeout is hard-coded to 30 seconds - make it a parameter */
+ result = client_submit_request(session, cstream);
if (result != ISC_R_SUCCESS) {
- return (result);
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ goto error;
}
+ http2_do_bio(session);
return (ISC_R_SUCCESS);
+error:
+ reply_cb(handle, result, NULL, cbarg);
+ if (cstream) {
+ put_http2_client_stream(mctx, cstream);
+ }
+ return result;
+}
+
+typedef struct isc_nm_connect_send_data {
+ isc_nm_recv_cb_t reply_cb;
+ void *cb_arg;
+ isc_region_t region;
+} isc_nm_connect_send_data_t;
+
+static void
+https_connect_send_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_nm_connect_send_data_t data;
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ memmove(&data, arg, sizeof(data));
+ isc_mem_put(handle->sock->mgr->mctx, arg, sizeof(data));
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ result = isc_nm_httprequest(handle, &data.region, data.reply_cb,
+ data.cb_arg);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ isc_mem_put(handle->sock->mgr->mctx, data.region.base,
+ data.region.length);
+ return;
+error:
+ data.reply_cb(handle, result, NULL, data.cb_arg);
+ isc_mem_put(handle->sock->mgr->mctx, data.region.base,
+ data.region.length);
+}
+
+isc_result_t
+isc_nm_http_connect_send_request(isc_nm_t *mgr, const char *uri, bool post,
+ isc_region_t *region, isc_nm_recv_cb_t cb,
+ void *cbarg, SSL_CTX *ctx,
+ unsigned int timeout) {
+ isc_region_t copy;
+ isc_result_t result;
+ isc_nm_connect_send_data_t *data;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(uri != NULL);
+ REQUIRE(*uri != '\0');
+ REQUIRE(region != NULL);
+ REQUIRE(region->base != NULL);
+ REQUIRE(region->length != 0);
+ REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
+ REQUIRE(cb != NULL);
+
+ copy = (isc_region_t){ .base = isc_mem_get(mgr->mctx, region->length),
+ .length = region->length };
+ memmove(copy.base, region->base, region->length);
+ data = isc_mem_get(mgr->mctx, sizeof(*data));
+ *data = (isc_nm_connect_send_data_t){ .reply_cb = cb,
+ .cb_arg = cbarg,
+ .region = copy };
+ result = isc_nm_httpconnect(mgr, NULL, NULL, uri, post,
+ https_connect_send_cb, data, ctx, timeout,
+ 0);
+
+ return (result);
}
static int
iface = isc_nmhandle_localaddr(session->handle);
isc__nmsocket_init(socket, session->serversocket->mgr,
isc_nm_httpstream, (isc_nmiface_t *)&iface);
- socket->h2 = (isc_nmsocket_h2_t){ .bufpos = 0,
- .bufsize = 0,
- .psock = socket,
- .handler = NULL,
- .request_path = NULL,
- .query_data = NULL,
- .stream_id = frame->hd.stream_id,
- .session = session };
-
+ socket->h2 = (isc_nmsocket_h2_t){
+ .bufpos = 0,
+ .bufsize = 0,
+ .buf = isc_mem_allocate(session->mctx, MAX_DNS_MESSAGE_SIZE),
+ .psock = socket,
+ .handler = NULL,
+ .request_path = NULL,
+ .query_data = NULL,
+ .stream_id = frame->hd.stream_id,
+ .session = session
+ };
+ socket->tid = session->handle->sock->tid;
ISC_LINK_INIT(&socket->h2, link);
ISC_LIST_APPEND(session->sstreams, &socket->h2, link);
+#ifdef NETMGR_TRACE
+ session->sstreams_count++;
+ if (session->sstreams_count > 1) {
+ fprintf(stderr, "HTTP/2 session %p (active streams: %lu)\n",
+ session, session->sstreams_count);
+ }
+#endif /* NETMGR_TRACE */
nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
socket);
return (0);
}
-static int
-server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
- const uint8_t *name, size_t namelen,
- const uint8_t *value, size_t valuelen, uint8_t flags,
- void *user_data) {
- isc_nmsocket_t *socket = NULL;
- const char path[] = ":path";
+static isc_nm_http2_server_handler_t *
+find_server_request_handler(const char *request_path,
+ isc_nmsocket_t *serversocket) {
+ isc_nm_http2_server_handler_t *handler = NULL;
+ REQUIRE(VALID_NMSOCK(serversocket));
- UNUSED(flags);
- UNUSED(user_data);
+ if (request_path == NULL || *request_path == '\0') {
+ return (NULL);
+ }
- switch (frame->hd.type) {
- case NGHTTP2_HEADERS:
- if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
- break;
+ isc_rwlock_lock(&serversocket->h2.handlers_lock, isc_rwlocktype_read);
+ if (atomic_load(&serversocket->listening)) {
+ for (handler = ISC_LIST_HEAD(serversocket->h2.handlers);
+ handler != NULL; handler = ISC_LIST_NEXT(handler, link))
+ {
+ if (!strcmp(request_path, handler->path)) {
+ break;
+ }
}
+ }
+ isc_rwlock_unlock(&serversocket->h2.handlers_lock, isc_rwlocktype_read);
- socket = nghttp2_session_get_stream_user_data(
- session, frame->hd.stream_id);
- if (socket == NULL || socket->h2.request_path != NULL) {
- break;
+ return (handler);
+}
+
+static isc_http2_error_responses_t
+server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ isc_nm_http2_server_handler_t *handler = NULL;
+ size_t j;
+ for (j = 0; j < valuelen && value[j] != '?'; ++j)
+ ;
+ if (socket->h2.request_path != NULL) {
+ isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
+ }
+ socket->h2.request_path = isc_mem_strndup(socket->mgr->mctx,
+ (const char *)value, j + 1);
+ handler = find_server_request_handler(socket->h2.request_path,
+ socket->h2.session->serversocket);
+ if (handler != NULL) {
+ socket->h2.handler_cb = handler->cb;
+ socket->h2.handler_cbarg = handler->cbarg;
+ socket->extrahandlesize = handler->extrahandlesize;
+ } else {
+ isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
+ socket->h2.request_path = NULL;
+ return (ISC_HTTP_ERROR_NOT_FOUND);
+ }
+ if (j < valuelen) {
+ const char *dns_value = NULL;
+ size_t dns_value_len = 0;
+
+ if (socket->h2.request_type != ISC_HTTP_REQ_GET) {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
}
- if (namelen == sizeof(path) - 1 &&
- memcmp(path, name, namelen) == 0) {
- size_t j;
- for (j = 0; j < valuelen && value[j] != '?'; ++j)
- ;
- socket->h2.request_path = isc_mem_strndup(
- socket->mgr->mctx, (const char *)value, j + 1);
- if (j < valuelen) {
- socket->h2.query_data = isc_mem_strndup(
- socket->mgr->mctx, (char *)value + j,
- valuelen - j);
+ if (isc__nm_parse_doh_query_string((const char *)value + j,
+ &dns_value, &dns_value_len))
+ {
+ const size_t decoded_size = dns_value_len / 4 * 3;
+ if (decoded_size <= MAX_DNS_MESSAGE_SIZE) {
+ if (socket->h2.query_data != NULL) {
+ isc_mem_free(socket->mgr->mctx,
+ socket->h2.query_data);
+ }
+ socket->h2.query_data =
+ isc__nm_base64url_to_base64(
+ socket->mgr->mctx, dns_value,
+ dns_value_len,
+ &socket->h2.query_data_len);
+ } else {
+ socket->h2.query_too_large = true;
+ return (ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE);
}
+ } else {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http2_error_responses_t
+server_handle_method_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char get[] = "GET";
+ const char post[] = "POST";
+ if (HEADER_MATCH(get, value, valuelen)) {
+ socket->h2.request_type = ISC_HTTP_REQ_GET;
+ } else if (HEADER_MATCH(post, value, valuelen)) {
+ socket->h2.request_type = ISC_HTTP_REQ_POST;
+ } else {
+ return (ISC_HTTP_ERROR_NOT_IMPLEMENTED);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http2_error_responses_t
+server_handle_scheme_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char http[] = "http";
+ const char http_secure[] = "https";
+ if (HEADER_MATCH(http_secure, value, valuelen)) {
+ socket->h2.request_scheme = ISC_HTTP_SCHEME_HTTP_SECURE;
+ } else if (HEADER_MATCH(http, value, valuelen)) {
+ socket->h2.request_scheme = ISC_HTTP_SCHEME_HTTP;
+ } else {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http2_error_responses_t
+server_handle_content_length_header(isc_nmsocket_t *socket,
+ const uint8_t *value,
+ const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+ if (socket->h2.request_type != ISC_HTTP_REQ_POST) {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ strncpy(tmp, (const char *)value,
+ valuelen > tmplen ? tmplen : valuelen);
+ socket->h2.content_length = strtoul(tmp, NULL, 10);
+ if (socket->h2.content_length > MAX_DNS_MESSAGE_SIZE) {
+ return (ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http2_error_responses_t
+server_handle_content_type_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char type_dns_message[] = "application/dns-message";
+ if (HEADER_MATCH(type_dns_message, value, valuelen)) {
+ socket->h2.content_type_verified = true;
+ } else {
+ return (ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http2_error_responses_t
+server_handle_accept_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char type_accept_all[] = "*/*";
+ const char type_dns_message[] = "application/dns-message";
+ if (HEADER_MATCH(type_dns_message, value, valuelen) ||
+ HEADER_MATCH(type_accept_all, value, valuelen))
+ {
+ socket->h2.accept_type_verified = true;
+ } else {
+ return (ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static int
+server_send_error_response(const isc_http2_error_responses_t error,
+ nghttp2_session *ngsession, isc_nmsocket_t *socket);
+
+static int
+server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen, uint8_t flags,
+ void *user_data) {
+ isc_nmsocket_t *socket = NULL;
+ isc_http2_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ const char path[] = ":path";
+ const char method[] = ":method";
+ const char scheme[] = ":scheme";
+ const char accept[] = "accept";
+ const char content_length[] = "Content-Length";
+ const char content_type[] = "Content-Type";
+
+ UNUSED(flags);
+ UNUSED(user_data);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
+ break;
+ }
+
+ socket = nghttp2_session_get_stream_user_data(
+ session, frame->hd.stream_id);
+ if (socket == NULL) {
+ break;
+ }
+
+ if (HEADER_MATCH(path, name, namelen)) {
+ code = server_handle_path_header(socket, value,
+ valuelen);
+ } else if (HEADER_MATCH(method, name, namelen)) {
+ code = server_handle_method_header(socket, value,
+ valuelen);
+ } else if (HEADER_MATCH(scheme, name, namelen)) {
+ code = server_handle_scheme_header(socket, value,
+ valuelen);
+ } else if (HEADER_MATCH(content_length, name, namelen)) {
+ code = server_handle_content_length_header(
+ socket, value, valuelen);
+ } else if (HEADER_MATCH(content_type, name, namelen)) {
+ code = server_handle_content_type_header(socket, value,
+ valuelen);
+ } else if (HEADER_MATCH(accept, (const char *)name, namelen)) {
+ code = server_handle_accept_header(socket, value,
+ valuelen);
}
break;
}
+ if (code == ISC_HTTP_ERROR_SUCCESS) {
+ return (0);
+ }
+
+ INSIST(socket != NULL);
+ if (server_send_error_response(code, session, socket) != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ };
+ failed_httpstream_read_cb(socket, ISC_R_CANCELED, socket->h2.session);
return (0);
}
return (0);
}
-static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
- "<body><h1>404 Not Found</h1></body></html>";
+#define MAKE_ERROR_REPLY(tag, code) \
+ { \
+ tag, MAKE_NV2(":status", #code) \
+ }
-static int
-error_reply(nghttp2_session *ngsession, isc_nmsocket_t *socket) {
- const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "404") };
+static struct http2_error_responses {
+ const isc_http2_error_responses_t type;
+ const nghttp2_nv header;
+} error_responses[] = {
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_SUCCESS, 200),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_FOUND, 404),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, 413),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_URI_TOO_LONG, 414),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, 415),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_BAD_REQUEST, 400),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_IMPLEMENTED, 501),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_GENERIC, 500),
+};
- memmove(socket->h2.buf, ERROR_HTML, sizeof(ERROR_HTML));
- socket->h2.bufsize = sizeof(ERROR_HTML);
+static int
+server_send_error_response(const isc_http2_error_responses_t error,
+ nghttp2_session *ngsession, isc_nmsocket_t *socket) {
+ socket->h2.bufsize = 0;
socket->h2.bufpos = 0;
- server_send_response(ngsession, socket->h2.stream_id, hdrs,
- sizeof(hdrs) / sizeof(nghttp2_nv), socket);
- return (0);
+ for (size_t i = 0;
+ i < sizeof(error_responses) / sizeof(error_responses[0]); i++)
+ {
+ if (error_responses[i].type == error) {
+ return server_send_response(
+ ngsession, socket->h2.stream_id,
+ &error_responses[i].header,
+ sizeof(error_responses[i].header), socket);
+ }
+ }
+
+ return server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
+ socket);
}
static int
server_on_request_recv(nghttp2_session *ngsession,
isc_nm_http2_session_t *session,
isc_nmsocket_t *socket) {
- isc_nm_http2_server_handler_t *handler = NULL;
isc_nmhandle_t *handle = NULL;
isc_sockaddr_t addr;
-
- if (!socket->h2.request_path) {
- if (error_reply(ngsession, socket) != 0) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
- }
- return (0);
+ isc_http2_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ isc_region_t data;
+
+ /* Sanity checks. Here we use the same error codes that
+ * Unbound uses
+ * (https://blog.nlnetlabs.nl/dns-over-https-in-unbound/) */
+ if (!socket->h2.request_path || !socket->h2.handler_cb) {
+ code = ISC_HTTP_ERROR_NOT_FOUND;
+ } else if ((socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ !socket->h2.content_type_verified) ||
+ !socket->h2.accept_type_verified)
+ {
+ code = ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_UNSUPPORTED) {
+ code = ISC_HTTP_ERROR_NOT_IMPLEMENTED;
+ } else if (socket->h2.request_scheme == ISC_HTTP_SCHEME_UNSUPPORTED) {
+ /* TODO: additional checks if we have enabled encryption on the
+ * socket or not */
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.content_length > MAX_DNS_MESSAGE_SIZE ||
+ socket->h2.query_too_large)
+ {
+ code = ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_GET &&
+ (socket->h2.content_length > 0 ||
+ socket->h2.query_data_len == 0))
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ socket->h2.content_length == 0)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ socket->h2.bufsize > socket->h2.content_length)
+ {
+ code = ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ socket->h2.bufsize != socket->h2.content_length)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
}
- for (handler = ISC_LIST_HEAD(session->serversocket->handlers);
- handler != NULL; handler = ISC_LIST_NEXT(handler, link))
- {
- if (!strcmp(socket->h2.request_path, handler->path)) {
- break;
- }
+ if (code != ISC_HTTP_ERROR_SUCCESS) {
+ goto error;
}
- if (handler == NULL) {
- if (error_reply(ngsession, socket) != 0) {
- return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ if (socket->h2.request_type == ISC_HTTP_REQ_GET) {
+ isc_buffer_t decoded_buf;
+ isc__buffer_init(&decoded_buf, socket->h2.buf,
+ MAX_DNS_MESSAGE_SIZE);
+ if (isc_base64_decodestring(socket->h2.query_data,
+ &decoded_buf) != ISC_R_SUCCESS)
+ {
+ code = ISC_HTTP_ERROR_GENERIC;
+ goto error;
}
- return (0);
+ isc__buffer_usedregion(&decoded_buf, &data);
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST) {
+ INSIST(socket->h2.content_length > 0);
+ data = (isc_region_t){ socket->h2.buf, socket->h2.bufsize };
+ } else {
+ INSIST(0);
+ ISC_UNREACHABLE();
}
- socket->extrahandlesize = handler->extrahandlesize;
addr = isc_nmhandle_peeraddr(session->handle);
handle = isc__nmhandle_get(socket, &addr, NULL);
- handler->cb(handle, ISC_R_SUCCESS,
- &(isc_region_t){ socket->h2.buf, socket->h2.bufsize },
- &(isc_region_t){ (unsigned char *)socket->h2.query_data,
- strlen(socket->h2.query_data) + 1 },
- handler->cbarg);
+ socket->h2.handler_cb(handle, ISC_R_SUCCESS, &data,
+ socket->h2.handler_cbarg);
+ isc_nmhandle_detach(&handle);
+ return (0);
+error:
+ if (server_send_error_response(code, ngsession, socket) != 0) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
return (0);
}
void
isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
isc_nm_cb_t cb, void *cbarg) {
- const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "200") };
isc_nmsocket_t *sock = handle->sock;
+ isc__nm_uvreq_t *uvreq = NULL;
+ isc__netievent_httpsend_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(handle->httpsession != NULL);
+ REQUIRE(VALID_NMSOCK(sock));
- /* TODO FIXME do it asynchronously!!! */
- memcpy(sock->h2.buf, region->base, region->length);
- sock->h2.bufsize = region->length;
+ /* TODO: should be called from within the context of an NM thread? */
+ if (inactive(sock) || handle->httpsession->closed) {
+ cb(handle, ISC_R_CANCELED, cbarg);
+ return;
+ }
+
+ INSIST(VALID_NMHANDLE(handle->httpsession->handle));
+ INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
+ INSIST(sock->tid == handle->httpsession->handle->sock->tid);
+
+ 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;
+
+ ievent = isc__nm_get_netievent_httpsend(sock->mgr, sock, uvreq);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_httpsend_t *ievent = (isc__netievent_httpsend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_nmhandle_t *handle = NULL;
+ isc_nm_cb_t cb = NULL;
+ void *cbarg = NULL;
+ isc_result_t res;
+ size_t content_length_str_len;
+
+ ievent->req = NULL;
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ handle = req->handle;
+ cb = req->cb.send;
+ cbarg = req->cbarg;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMHANDLE(handle->httpsession->handle));
+ REQUIRE(VALID_NMSOCK(handle->httpsession->handle->sock));
+ REQUIRE(handle->httpsession->handle->sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ memmove(sock->h2.buf, req->uvbuf.base, req->uvbuf.len);
+ sock->h2.bufsize = req->uvbuf.len;
+
+ content_length_str_len =
+ snprintf(sock->h2.response_content_length_str,
+ sizeof(sock->h2.response_content_length_str), "%lu",
+ (unsigned long)req->uvbuf.len);
+ const nghttp2_nv hdrs[] = {
+ MAKE_NV2(":status", "200"),
+ MAKE_NV2("Content-Type", "application/dns-message"),
+ MAKE_NV("Content-Length", sock->h2.response_content_length_str,
+ content_length_str_len)
+ };
if (server_send_response(handle->httpsession->ngsession,
sock->h2.stream_id, hdrs,
sizeof(hdrs) / sizeof(nghttp2_nv), sock) != 0)
{
- cb(handle, ISC_R_FAILURE, cbarg);
+ res = ISC_R_FAILURE;
} else {
- cb(handle, ISC_R_SUCCESS, cbarg);
+ res = ISC_R_SUCCESS;
}
+
+ http2_do_bio(handle->httpsession); /*TODO: Should we call it only
+ * on success? */
+ cb(handle, res, cbarg);
+
+ isc__nm_uvreq_put(&req, sock);
}
static int
initialize_nghttp2_server_session(isc_nm_http2_session_t *session) {
nghttp2_session_callbacks *callbacks = NULL;
- nghttp2_session_callbacks_new(&callbacks);
+ RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *httplistensock = (isc_nmsocket_t *)cbarg;
isc_nm_http2_session_t *session = NULL;
+ isc_nmsocket_t *listener, *httpserver;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ if (handle->sock->type == isc_nm_tlssocket) {
+ REQUIRE(VALID_NMSOCK(handle->sock->listener));
+ listener = handle->sock->listener;
+ httpserver = listener->h2.httpserver;
+ } else {
+ REQUIRE(VALID_NMSOCK(handle->sock->server));
+ listener = handle->sock->server;
+ REQUIRE(VALID_NMSOCK(listener->parent));
+ httpserver = listener->parent->h2.httpserver;
+ }
+
+ /* NOTE: HTTP listener socket might be destroyed by the time this
+ * function gets invoked, so we need to do extra sanity checks to
+ * detect this case. */
+ if (inactive(handle->sock) || httpserver == NULL) {
+ return ISC_R_CANCELED;
+ }
if (result != ISC_R_SUCCESS) {
/* XXXWPK do nothing? */
return (result);
}
+ REQUIRE(VALID_NMSOCK(httplistensock));
+ INSIST(httplistensock == httpserver);
+
+ if (inactive(httplistensock) ||
+ !atomic_load(&httplistensock->listening)) {
+ return ISC_R_CANCELED;
+ }
+
session = isc_mem_get(httplistensock->mgr->mctx,
sizeof(isc_nm_http2_session_t));
- *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC };
+ *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC,
+ .ssl_ctx_created = false,
+ .client = false };
initialize_nghttp2_server_session(session);
+ handle->sock->h2.session = session;
isc_mem_attach(httplistensock->mgr->mctx, &session->mctx);
isc_nmhandle_attach(handle, &session->handle);
isc__nmsocket_attach(httplistensock, &session->serversocket);
+ session->serversocket = httplistensock;
server_send_connection_header(session);
/* TODO H2 */
return (ISC_R_SUCCESS);
}
+#ifndef OPENSSL_NO_NEXTPROTONEG
+static int
+next_proto_cb(SSL *ssl, const unsigned char **data, unsigned int *len,
+ void *arg) {
+ (void)ssl;
+ (void)arg;
+
+ *data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
+ *len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int
+alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg) {
+ int ret;
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
+ outlen, in, inlen);
+
+ if (ret != 1) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+
isc_result_t
-isc_nm_listenhttps(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
- isc_quota_t *quota, SSL_CTX *ctx, isc_nmsocket_t **sockp) {
+isc_nm_listenhttp(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog,
+ isc_quota_t *quota, SSL_CTX *ctx, isc_nmsocket_t **sockp) {
isc_nmsocket_t *sock = NULL;
isc_result_t result;
- isc_mem_get(mgr->mctx, sizeof(*sock));
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
isc__nmsocket_init(sock, mgr, isc_nm_httplistener, iface);
if (ctx != NULL) {
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, NULL);
+#endif // OPENSSL_NO_NEXTPROTONEG
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, NULL);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock,
sizeof(isc_nm_http2_session_t),
backlog, quota, ctx, &sock->outer);
return (result);
}
+ sock->outer->h2.httpserver = sock;
+
+ sock->nchildren = sock->outer->nchildren;
+ sock->result = ISC_R_DEFAULT;
+ sock->tid = isc_random_uniform(sock->nchildren);
+ sock->fd = (uv_os_sock_t)-1;
+
atomic_store(&sock->listening, true);
*sockp = sock;
return (ISC_R_SUCCESS);
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_httplistener);
- handler = isc_mem_get(sock->mgr->mctx, sizeof(*handler));
- *handler = (isc_nm_http2_server_handler_t){
- .cb = cb,
- .cbarg = cbarg,
- .extrahandlesize = extrahandlesize,
- .path = isc_mem_strdup(sock->mgr->mctx, uri)
- };
-
- ISC_LINK_INIT(handler, link);
- ISC_LIST_APPEND(sock->handlers, handler, link);
+ if (find_server_request_handler(uri, sock) == NULL) {
+ handler = isc_mem_get(sock->mgr->mctx, sizeof(*handler));
+ *handler = (isc_nm_http2_server_handler_t){
+ .cb = cb,
+ .cbarg = cbarg,
+ .extrahandlesize = extrahandlesize,
+ .path = isc_mem_strdup(sock->mgr->mctx, uri)
+ };
+
+ isc_rwlock_lock(&sock->h2.handlers_lock, isc_rwlocktype_write);
+ ISC_LINK_INIT(handler, link);
+ ISC_LIST_APPEND(sock->h2.handlers, handler, link);
+ isc_rwlock_unlock(&sock->h2.handlers_lock,
+ isc_rwlocktype_write);
+ }
return (ISC_R_SUCCESS);
}
-typedef struct {
- isc_nm_recv_cb_t cb;
- void *cbarg;
-} cbarg_t;
-
-static unsigned char doh_error[] =
- "<html><head><title>No request</title></head>"
- "<body><h1>No request</h1></body></html>";
-
-static const isc_region_t doh_error_r = { doh_error, sizeof(doh_error) };
-
-static void
-https_sendcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
- UNUSED(handle);
- UNUSED(result);
- UNUSED(cbarg);
-}
-
/*
* In DoH we just need to intercept the request - the response can be sent
* to the client code via the nmhandle directly as it's always just the
* http * content.
*/
static void
-doh_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *post,
- isc_region_t *get, void *arg) {
- cbarg_t *dohcbarg = arg;
- isc_region_t *data = NULL;
-
+doh_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *data,
+ void *arg) {
+ isc_nm_http_doh_cbarg_t *dohcbarg = arg;
REQUIRE(VALID_NMHANDLE(handle));
- UNUSED(result);
- UNUSED(get);
-
if (result != ISC_R_SUCCESS) {
/* Shut down the client, then ourselves */
- dohcbarg->cb(NULL, result, NULL, dohcbarg->cbarg);
+ dohcbarg->cb(handle, result, NULL, dohcbarg->cbarg);
/* XXXWPK FREE */
return;
}
-
- if (post != NULL) {
- data = post;
- } else if (get != NULL) {
- /* XXXWPK PARSE */
- data = NULL; /* FIXME */
- } else {
- /* Invalid request, just send the error response */
- isc_nm_send(handle, &doh_error_r, https_sendcb, dohcbarg);
- return;
- }
-
dohcbarg->cb(handle, result, data, dohcbarg->cbarg);
}
isc_nm_recv_cb_t cb, void *cbarg,
size_t extrahandlesize) {
isc_result_t result;
- cbarg_t *dohcbarg = NULL;
+ isc_nm_http_doh_cbarg_t *dohcbarg = NULL;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_httplistener);
- dohcbarg = isc_mem_get(sock->mgr->mctx, sizeof(cbarg_t));
- *dohcbarg = (cbarg_t){ cb, cbarg };
+ dohcbarg = isc_mem_get(sock->mgr->mctx,
+ sizeof(isc_nm_http_doh_cbarg_t));
+ dohcbarg->cb = cb;
+ dohcbarg->cbarg = cbarg;
result = isc_nm_http_add_endpoint(sock, uri, doh_callback, dohcbarg,
extrahandlesize);
if (result != ISC_R_SUCCESS) {
- isc_mem_put(sock->mgr->mctx, dohcbarg, sizeof(cbarg_t));
+ isc_mem_put(sock->mgr->mctx, dohcbarg,
+ sizeof(isc_nm_http_doh_cbarg_t));
}
+ isc_rwlock_lock(&sock->h2.handlers_lock, isc_rwlocktype_write);
+ ISC_LINK_INIT(dohcbarg, link);
+ ISC_LIST_APPEND(sock->h2.handlers_cbargs, dohcbarg, link);
+ isc_rwlock_unlock(&sock->h2.handlers_lock, isc_rwlocktype_write);
+
return (result);
}
+
+void
+isc__nm_http_stoplistening(isc_nmsocket_t *sock) {
+ isc__netievent_httpstop_t *ievent;
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httplistener);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true)) {
+ INSIST(0);
+ ISC_UNREACHABLE();
+ }
+
+ ievent = isc__nm_get_netievent_httpstop(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_http_clear_handlers(isc_nmsocket_t *sock) {
+ isc_nm_http2_server_handler_t *handler = NULL;
+ isc_nm_http_doh_cbarg_t *dohcbarg = NULL;
+ /* delete all handlers */
+ isc_rwlock_lock(&sock->h2.handlers_lock, isc_rwlocktype_write);
+ handler = ISC_LIST_HEAD(sock->h2.handlers);
+ while (handler != NULL) {
+ isc_nm_http2_server_handler_t *next;
+
+ next = ISC_LIST_NEXT(handler, link);
+ ISC_LIST_DEQUEUE(sock->h2.handlers, handler, link);
+ isc_mem_free(sock->mgr->mctx, handler->path);
+ isc_mem_put(sock->mgr->mctx, handler, sizeof(*handler));
+ handler = next;
+ }
+
+ dohcbarg = ISC_LIST_HEAD(sock->h2.handlers_cbargs);
+ while (dohcbarg != NULL) {
+ isc_nm_http_doh_cbarg_t *next;
+
+ next = ISC_LIST_NEXT(dohcbarg, link);
+ ISC_LIST_DEQUEUE(sock->h2.handlers_cbargs, dohcbarg, link);
+ isc_mem_put(sock->mgr->mctx, dohcbarg,
+ sizeof(isc_nm_http_doh_cbarg_t));
+ dohcbarg = next;
+ }
+ isc_rwlock_unlock(&sock->h2.handlers_lock, isc_rwlocktype_write);
+}
+
+void
+isc__nm_http_clear_session(isc_nmsocket_t *sock) {
+ if (sock->type != isc_nm_httpstream && sock->h2.session != NULL) {
+ isc_nm_http2_session_t *session = sock->h2.session;
+ INSIST(ISC_LIST_EMPTY(session->sstreams));
+ session->magic = 0;
+ if (session->ssl_ctx_created) {
+ SSL_CTX_free(session->ssl_ctx);
+ }
+ isc_mem_putanddetach(&sock->h2.session->mctx, session,
+ sizeof(isc_nm_http2_session_t));
+ sock->h2.session = NULL;
+ }
+}
+
+void
+isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_httpstop_t *ievent = (isc__netievent_httpstop_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ atomic_store(&sock->listening, false);
+ atomic_store(&sock->closing, false);
+ atomic_store(&sock->closed, true);
+ if (sock->outer != NULL) {
+ sock->outer->h2.httpserver = NULL;
+ isc_nm_stoplistening(sock->outer);
+ isc_nmsocket_close(&sock->outer);
+ }
+}
+
+static void
+http_close_direct(isc_nmsocket_t *sock) {
+ bool sessions_empty;
+ isc_nm_http2_session_t *session;
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
+ atomic_store(&sock->closed, true);
+ session = sock->h2.session;
+
+ if (ISC_LINK_LINKED(&sock->h2, link)) {
+ ISC_LIST_UNLINK(session->sstreams, &sock->h2, link);
+#ifdef NETMGR_TRACE
+ session->sstreams_count--;
+#endif /* NETMGR_TRACE */
+ }
+
+ sessions_empty = ISC_LIST_EMPTY(session->sstreams);
+ if (!sessions_empty) {
+ http2_do_bio(session);
+ } else if (session->reading) {
+ session->reading = false;
+ if (session->handle != NULL) {
+ isc_nm_cancelread(session->handle);
+ }
+ }
+ /* If session is closed then the only reference to the socket is
+ * the one, created when handling the netievent. */
+ if (!session->closed) {
+ INSIST(session->handle != NULL);
+ isc__nmsocket_detach(&sock);
+ } else {
+ INSIST(isc_refcount_current(&sock->references) == 1);
+ }
+}
+
+void
+isc__nm_http_close(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httpstream);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true)) {
+ return;
+ }
+
+ isc__netievent_httpclose_t *ievent =
+ isc__nm_get_netievent_httpclose(sock->mgr, sock);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_httpclose_t *ievent = (isc__netievent_httpclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ http_close_direct(sock);
+}
+
+static void
+failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ isc_nm_http2_session_t *session) {
+ isc_nmhandle_t *handle = NULL;
+ isc_sockaddr_t addr;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ INSIST(sock->type == isc_nm_httpstream);
+
+ if (!sock->h2.request_path) {
+ return;
+ }
+
+ INSIST(sock->h2.handler_cbarg != NULL);
+
+ (void)nghttp2_submit_rst_stream(
+ session->ngsession, NGHTTP2_FLAG_END_STREAM, sock->h2.stream_id,
+ NGHTTP2_REFUSED_STREAM);
+ addr = isc_nmhandle_peeraddr(session->handle);
+ handle = isc__nmhandle_get(sock, &addr, NULL);
+ sock->h2.handler_cb(handle, result,
+ &(isc_region_t){ sock->h2.buf, sock->h2.bufsize },
+ sock->h2.handler_cbarg);
+ isc_nmhandle_detach(&handle);
+}
+
+static void
+failed_read_cb(isc_result_t result, isc_nm_http2_session_t *session) {
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (session->client) {
+ http2_client_stream_t *cstream = NULL;
+ cstream = ISC_LIST_HEAD(session->cstreams);
+ while (cstream != NULL) {
+ http2_client_stream_t *next = ISC_LIST_NEXT(cstream,
+ link);
+ ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
+
+ cstream->read_cb(session->handle, result,
+ &(isc_region_t){ cstream->rbuf,
+ cstream->rbufsize },
+ cstream->read_cbarg);
+
+ put_http2_client_stream(session->mctx, cstream);
+ cstream = next;
+ }
+ } else {
+ isc_nmsocket_h2_t *h2data = NULL; /* stream socket */
+ session->closed = true;
+ for (h2data = ISC_LIST_HEAD(session->sstreams); h2data != NULL;
+ h2data = ISC_LIST_NEXT(h2data, link))
+ {
+ failed_httpstream_read_cb(h2data->psock, result,
+ session);
+ }
+
+ h2data = ISC_LIST_HEAD(session->sstreams);
+ while (h2data != NULL) {
+ isc_nmsocket_h2_t *next = ISC_LIST_NEXT(h2data, link);
+ ISC_LIST_DEQUEUE(session->sstreams, h2data, link);
+ /* Cleanup socket in place */
+ atomic_store(&h2data->psock->active, false);
+ atomic_store(&h2data->psock->closed, true);
+ isc__nmsocket_detach(&h2data->psock);
+
+ h2data = next;
+ }
+ }
+ finish_http2_session(session);
+}
+
+static const bool base64url_validation_table[256] = {
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, true, false, false, true, true,
+ true, true, true, true, true, true, true, true, false, false,
+ false, false, false, false, false, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, false, false, false, false, true, false, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false
+};
+
+char *
+isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
+ const size_t base64url_len, size_t *res_len) {
+ char *res = NULL;
+ size_t i, k, len;
+
+ if (mem == NULL || base64url == NULL || base64url_len == 0) {
+ return NULL;
+ }
+
+ len = base64url_len % 4 ? base64url_len + (4 - base64url_len % 4)
+ : base64url_len;
+ res = isc_mem_allocate(mem, len + 1); /* '\0' */
+
+ for (i = 0; i < base64url_len; i++) {
+ switch (base64url[i]) {
+ case '-':
+ res[i] = '+';
+ break;
+ case '_':
+ res[i] = '/';
+ break;
+ default:
+ if (base64url_validation_table[(size_t)base64url[i]]) {
+ res[i] = base64url[i];
+ } else {
+ isc_mem_free(mem, res);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ if (base64url_len % 4 != 0) {
+ for (k = 0; k < (4 - base64url_len % 4); k++, i++) {
+ res[i] = '=';
+ }
+ }
+
+ INSIST(i == len);
+
+ if (res_len) {
+ *res_len = len;
+ }
+
+ res[len] = '\0';
+
+ return res;
+}
+
+char *
+isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
+ const size_t base64_len, size_t *res_len) {
+ char *res = NULL;
+ size_t i;
+
+ if (mem == NULL || base64 == NULL || base64_len == 0) {
+ return NULL;
+ }
+
+ res = isc_mem_allocate(mem, base64_len + 1); /* '\0' */
+
+ for (i = 0; i < base64_len; i++) {
+ switch (base64[i]) {
+ case '+':
+ res[i] = '-';
+ break;
+ case '/':
+ res[i] = '_';
+ break;
+ case '=':
+ goto end;
+ break;
+ default:
+ /* All other characters from the alphabet are the same
+ * for both base64 and base64url, so we can reuse the
+ * validation table for the rest of the characters. */
+ if (base64[i] != '-' && base64[i] != '_' &&
+ base64url_validation_table[(size_t)base64[i]])
+ {
+ res[i] = base64[i];
+ } else {
+ isc_mem_free(mem, res);
+ return NULL;
+ }
+ break;
+ }
+ }
+end:
+ if (res_len) {
+ *res_len = i;
+ }
+
+ res[i] = '\0';
+
+ return res;
+}
+
+/* DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
+
+It is based on the following grammar (using WSN/EBNF):
+
+S = query-string.
+query-string = ['?'] { key-value-pair } EOF.
+key-value-pair = key '=' value [ '&' ].
+key = ('_' | alpha) { '_' | alnum}.
+value = value-char {value-char}.
+value-char = unreserved-char | percent-charcode.
+unreserved-char = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
+percent-charcode = '%' hexdigit hexdigit.
+...
+
+Should be good enough.
+*/
+
+typedef struct isc_doh_query_parser_state {
+ const char *str;
+
+ const char *last_key;
+ size_t last_key_len;
+
+ const char *last_value;
+ size_t last_value_len;
+
+ bool doh_query_found;
+ const char *doh_query;
+ size_t doh_query_length;
+} isc_doh_query_parser_state_t;
+
+#define MATCH(ch) (st->str[0] == (ch))
+#define MATCH_ALPHA() isalpha(st->str[0])
+#define MATCH_ALNUM() isalnum(st->str[0])
+#define MATCH_XDIGIT() isxdigit(st->str[0])
+#define ADVANCE() st->str++
+#define GETP() (st->str)
+
+static bool
+rule_query_string(isc_doh_query_parser_state_t *st);
+
+bool
+isc__nm_parse_doh_query_string(const char *query_string, const char **start,
+ size_t *len) {
+ isc_doh_query_parser_state_t state;
+ if (query_string == NULL || *query_string == '\0' || start == NULL ||
+ len == 0) {
+ return false;
+ }
+
+ memset(&state, 0, sizeof(state));
+ state.str = query_string;
+ if (!rule_query_string(&state)) {
+ return false;
+ }
+
+ if (!state.doh_query_found) {
+ return false;
+ }
+
+ *start = state.doh_query;
+ *len = state.doh_query_length;
+
+ INSIST(start != NULL);
+ INSIST(len != 0);
+
+ return true;
+}
+
+static bool
+rule_key_value_pair(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_query_string(isc_doh_query_parser_state_t *st) {
+ if (MATCH('?')) {
+ ADVANCE();
+ }
+
+ while (rule_key_value_pair(st))
+ ;
+ /* EOF */
+ if (!MATCH('\0')) {
+ return false;
+ }
+
+ ADVANCE();
+ return true;
+}
+
+static bool
+rule_key(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_value(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_key_value_pair(isc_doh_query_parser_state_t *st) {
+ if (!rule_key(st)) {
+ return false;
+ }
+
+ if (MATCH('=')) {
+ ADVANCE();
+ } else {
+ return false;
+ }
+
+ if (rule_value(st)) {
+ const char dns[] = "dns";
+ if (st->last_key_len == sizeof(dns) - 1 &&
+ memcmp(st->last_key, dns, sizeof(dns) - 1) == 0)
+ {
+ st->doh_query_found = true;
+ st->doh_query = st->last_value;
+ st->doh_query_length = st->last_value_len;
+ }
+ } else {
+ return false;
+ }
+
+ if (MATCH('&')) {
+ ADVANCE();
+ }
+
+ return true;
+}
+
+static bool
+rule_key(isc_doh_query_parser_state_t *st) {
+ if (MATCH('_') || MATCH_ALPHA()) {
+ st->last_key = GETP();
+ ADVANCE();
+ } else {
+ return false;
+ }
+
+ while (MATCH('_') || MATCH_ALNUM()) {
+ ADVANCE();
+ }
+
+ st->last_key_len = GETP() - st->last_key;
+ return true;
+}
+
+static bool
+rule_value_char(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_value(isc_doh_query_parser_state_t *st) {
+ const char *s = GETP();
+ if (!rule_value_char(st)) {
+ return false;
+ }
+
+ st->last_value = s;
+ while (rule_value_char(st))
+ ;
+ st->last_value_len = GETP() - st->last_value;
+ return true;
+}
+
+static bool
+rule_percent_charcode(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_unreserved_char(isc_doh_query_parser_state_t *st);
+
+static bool
+rule_value_char(isc_doh_query_parser_state_t *st) {
+ if (rule_unreserved_char(st)) {
+ return true;
+ }
+
+ return rule_percent_charcode(st);
+}
+
+static bool
+rule_unreserved_char(isc_doh_query_parser_state_t *st) {
+ if (MATCH_ALNUM() || MATCH('_') || MATCH('.') || MATCH('-') ||
+ MATCH('~')) {
+ ADVANCE();
+ return true;
+ }
+ return false;
+}
+
+static bool
+rule_percent_charcode(isc_doh_query_parser_state_t *st) {
+ if (MATCH('%')) {
+ ADVANCE();
+ } else {
+ return false;
+ }
+
+ if (!MATCH_XDIGIT()) {
+ return false;
+ }
+ ADVANCE();
+
+ if (!MATCH_XDIGIT()) {
+ return false;
+ }
+ ADVANCE();
+
+ return true;
+}
#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>
netievent_tlsdnscycle,
netievent_tlsdnsshutdown,
+ netievent_httpstop,
+ netievent_httpsend,
+ netievent_httpclose,
+
netievent_close,
netievent_shutdown,
netievent_stop,
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[65535];
+ 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 */
atomic_int_fast32_t active_child_connections;
- ISC_LIST(isc_nm_http2_server_handler_t) handlers;
-
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
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
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_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_CASE(tlsdnsstop);
NETIEVENT_CASE(tlsdnsshutdown);
+ NETIEVENT_CASE(httpstop);
+ NETIEVENT_CASE(httpsend);
+ NETIEVENT_CASE(httpclose);
+
NETIEVENT_CASE(connectcb);
NETIEVENT_CASE(readcb);
NETIEVENT_CASE(sendcb);
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);
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);
case isc_nm_tlsdnssocket:
isc__nm_tlsdns_close(sock);
return;
+ case isc_nm_httpstream:
+ isc__nm_http_close(sock);
+ return;
default:
break;
}
(*sockp)->type == isc_nm_tcplistener ||
(*sockp)->type == isc_nm_tcpdnslistener ||
(*sockp)->type == isc_nm_tlsdnslistener ||
- (*sockp)->type == isc_nm_tlslistener);
+ (*sockp)->type == isc_nm_tlslistener ||
+ (*sockp)->type == isc_nm_httplistener);
isc__nmsocket_detach(sockp);
}
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 {
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;
}
sock->statichandle = handle;
}
+ if (sock->type == isc_nm_httpstream) {
+ handle->httpsession = sock->h2.session;
+ }
+
return (handle);
}
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();
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();
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);
done:
result = isc__nm_uverr2result(r);
-
+error:
LOCK(&sock->lock);
sock->result = result;
SIGNAL(&sock->cond);
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);
}
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;
tcp_quota_test \
tcpdns_test \
tlsdns_test \
+ doh_test \
time_test \
timer_test \
udp_test
--- /dev/null
+/*
+ * 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.
+ */
+
+#if HAVE_CMOCKA
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <uv.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/mutex.h>
+#include <isc/netmgr.h>
+#include <isc/nonce.h>
+#include <isc/os.h>
+#include <isc/refcount.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+
+#include "uv_wrap.h"
+#define KEEP_BEFORE
+
+#include "../netmgr/http.c"
+#include "../netmgr/netmgr-int.h"
+#include "../netmgr/uv-compat.c"
+#include "../netmgr/uv-compat.h"
+#include "isctest.h"
+#include "tls_test_cert_key.h"
+
+#define MAX_NM 2
+
+static isc_sockaddr_t tcp_listen_addr;
+
+static uint64_t send_magic = 0;
+static uint64_t stop_magic = 0;
+
+static uv_buf_t send_msg = { .base = (char *)&send_magic,
+ .len = sizeof(send_magic) };
+
+static atomic_uint_fast64_t nsends;
+
+static atomic_uint_fast64_t ssends;
+static atomic_uint_fast64_t sreads;
+
+static atomic_uint_fast64_t csends;
+static atomic_uint_fast64_t creads;
+
+static atomic_bool was_error;
+
+static unsigned int workers = 1;
+
+static bool reuse_supported = true;
+
+static atomic_bool POST = true;
+
+static atomic_bool use_TLS = false;
+static SSL_CTX *server_ssl_ctx = NULL;
+
+static SSL_CTX *
+create_server_ssl_ctx(const char *key, const size_t key_size,
+ const char *key_pass, const char *cert,
+ const size_t cert_size);
+
+#define NSENDS 100
+#define NWRITES 10
+
+#define DOH_PATH "/dns-query"
+
+#define CHECK_RANGE_FULL(v) \
+ { \
+ int __v = atomic_load(&v); \
+ assert_true(__v > NSENDS * NWRITES * 10 / 100); \
+ assert_true(__v <= NSENDS * NWRITES * 110 / 100); \
+ }
+
+#define CHECK_RANGE_HALF(v) \
+ { \
+ int __v = atomic_load(&v); \
+ assert_true(__v > NSENDS * NWRITES * 5 / 100); \
+ assert_true(__v <= NSENDS * NWRITES * 110 / 100); \
+ }
+
+/* Enable this to print values while running tests */
+#undef PRINT_DEBUG
+#ifdef PRINT_DEBUG
+#define X(v) fprintf(stderr, #v " = %" PRIu64 "\n", atomic_load(&v))
+#else
+#define X(v)
+#endif
+
+static int
+setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
+ isc_result_t result;
+ socklen_t addrlen = sizeof(*addr);
+ int fd;
+ int r;
+
+ isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
+
+ fd = socket(AF_INET6, family, 0);
+ if (fd < 0) {
+ perror("setup_ephemeral_port: socket()");
+ return (-1);
+ }
+
+ r = bind(fd, (const struct sockaddr *)&addr->type.sa,
+ sizeof(addr->type.sin6));
+ if (r != 0) {
+ perror("setup_ephemeral_port: bind()");
+ isc__nm_closesocket(fd);
+ return (r);
+ }
+
+ r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
+ if (r != 0) {
+ perror("setup_ephemeral_port: getsockname()");
+ isc__nm_closesocket(fd);
+ return (r);
+ }
+
+ result = isc__nm_socket_reuse(fd);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+ fprintf(stderr,
+ "setup_ephemeral_port: isc__nm_socket_reuse(): %s",
+ isc_result_totext(result));
+ close(fd);
+ return (-1);
+ }
+
+ result = isc__nm_socket_reuse_lb(fd);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+ fprintf(stderr,
+ "setup_ephemeral_port: isc__nm_socket_reuse_lb(): %s",
+ isc_result_totext(result));
+ close(fd);
+ return (-1);
+ }
+ if (result == ISC_R_NOTIMPLEMENTED) {
+ reuse_supported = false;
+ }
+
+#if IPV6_RECVERR
+#define setsockopt_on(socket, level, name) \
+ setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
+
+ r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
+ if (r != 0) {
+ perror("setup_ephemeral_port");
+ close(fd);
+ return (r);
+ }
+#endif
+
+ return (fd);
+}
+
+static int
+_setup(void **state) {
+ UNUSED(state);
+
+ /*workers = isc_os_ncpus();*/
+
+ if (isc_test_begin(NULL, false, workers) != ISC_R_SUCCESS) {
+ return (-1);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ server_ssl_ctx = create_server_ssl_ctx(
+ (const char *)TLS_test_key, sizeof(TLS_test_key), NULL,
+ (const char *)TLS_test_cert, sizeof(TLS_test_cert));
+
+ return (0);
+}
+
+static int
+_teardown(void **state) {
+ UNUSED(state);
+
+ if (server_ssl_ctx) {
+ SSL_CTX_free(server_ssl_ctx);
+ server_ssl_ctx = NULL;
+ }
+
+ isc_test_end();
+
+ return (0);
+}
+
+/* Generic */
+
+static void
+noop_read_cb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *cbarg) {
+ UNUSED(handle);
+ UNUSED(result);
+ UNUSED(region);
+ UNUSED(cbarg);
+}
+
+thread_local uint8_t tcp_buffer_storage[4096];
+thread_local size_t tcp_buffer_length = 0;
+
+static int
+nm_setup(void **state) {
+ size_t nworkers = ISC_MAX(ISC_MIN(workers, 32), 1);
+ int tcp_listen_sock = -1;
+ isc_nm_t **nm = NULL;
+
+ tcp_listen_addr = (isc_sockaddr_t){ .length = 0 };
+ tcp_listen_sock = setup_ephemeral_port(&tcp_listen_addr, SOCK_STREAM);
+ if (tcp_listen_sock < 0) {
+ return (-1);
+ }
+ close(tcp_listen_sock);
+ tcp_listen_sock = -1;
+
+ atomic_store(&nsends, NSENDS * NWRITES);
+
+ atomic_store(&csends, 0);
+ atomic_store(&creads, 0);
+ atomic_store(&sreads, 0);
+ atomic_store(&ssends, 0);
+
+ atomic_store(&was_error, false);
+
+ atomic_store(&POST, false);
+ atomic_store(&use_TLS, false);
+
+ isc_nonce_buf(&send_magic, sizeof(send_magic));
+ isc_nonce_buf(&stop_magic, sizeof(stop_magic));
+ if (send_magic == stop_magic) {
+ return (-1);
+ }
+
+ nm = isc_mem_get(test_mctx, MAX_NM * sizeof(nm[0]));
+ for (size_t i = 0; i < MAX_NM; i++) {
+ nm[i] = isc_nm_start(test_mctx, nworkers);
+ assert_non_null(nm[i]);
+ }
+
+ *state = nm;
+
+ return (0);
+}
+
+static int
+nm_teardown(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+
+ for (size_t i = 0; i < MAX_NM; i++) {
+ isc_nm_destroy(&nm[i]);
+ assert_null(nm[i]);
+ }
+ isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0]));
+
+ return (0);
+}
+
+thread_local size_t nwrites = NWRITES;
+
+static void
+sockaddr_to_url(isc_sockaddr_t *sa, const bool https, char *outbuf,
+ size_t outbuf_len, const char *append) {
+ uint16_t port;
+ char saddr[INET6_ADDRSTRLEN] = { 0 };
+ int family;
+ if (sa == NULL || outbuf == NULL || outbuf_len == 0) {
+ return;
+ }
+
+ family = ((struct sockaddr *)&sa->type.sa)->sa_family;
+
+ port = ntohs(family == AF_INET ? sa->type.sin.sin_port
+ : sa->type.sin6.sin6_port);
+ inet_ntop(family,
+ family == AF_INET
+ ? (struct sockaddr *)&sa->type.sin.sin_addr
+ : (struct sockaddr *)&sa->type.sin6.sin6_addr,
+ saddr, sizeof(saddr));
+
+ snprintf(outbuf, outbuf_len, "%s://%s%s%s:%u%s",
+ https ? "https" : "http", family == AF_INET ? "" : "[", saddr,
+ family == AF_INET ? "" : "]", port, append ? append : "");
+}
+
+/* SSL utils */
+static bool
+ssl_ctx_add_privatekey(SSL_CTX *ctx, const void *key,
+ const unsigned int key_size, const char *pass) {
+ BIO *key_bio = BIO_new_mem_buf(key, key_size);
+ bool res = false;
+ if (key_bio) {
+ RSA *rsa = PEM_read_bio_RSAPrivateKey(
+ key_bio, 0, 0, (void *)((uintptr_t)pass));
+ if (rsa) {
+ res = (1 == SSL_CTX_use_RSAPrivateKey(ctx, rsa))
+ ? true
+ : false;
+ }
+ RSA_free(rsa);
+ BIO_free_all(key_bio);
+ } else {
+ return false;
+ }
+
+ res = SSL_CTX_check_private_key(ctx) == 1 ? true : false;
+
+ return res;
+}
+
+static bool
+ssl_ctx_add_cert_chain(SSL_CTX *ctx, const void *cert,
+ const unsigned int size) {
+ BIO *chain_bio = NULL;
+ STACK_OF(X509_INFO) *chain_stack = NULL;
+ size_t count = 0;
+ X509_INFO *ci = NULL;
+ bool res = true;
+
+ chain_bio = BIO_new_mem_buf(cert, size);
+ if (chain_bio == NULL) {
+ res = false;
+ goto exit;
+ }
+
+ /* read info into BIO */
+ chain_stack = PEM_X509_INFO_read_bio(chain_bio, NULL, NULL, NULL);
+ if (chain_stack == NULL) {
+ res = false;
+ goto exit;
+ }
+
+ count = sk_X509_INFO_num(chain_stack);
+ /* add certs */
+ for (size_t i = count; i > 0; i--) {
+ /* get the cert */
+ ci = sk_X509_INFO_value(chain_stack, i - 1);
+ if (ci == NULL) {
+ res = false;
+ goto exit;
+ }
+
+ /* add the cert */
+ if (SSL_CTX_add_extra_chain_cert(ctx, ci->x509) != 1) {
+ res = false;
+ goto exit;
+ }
+
+ /* use the first cert in chain by default */
+ if (i == 1) {
+ if (SSL_CTX_use_certificate(ctx, ci->x509) != 1) {
+ res = false;
+ goto exit;
+ }
+ }
+ }
+exit:
+ if (chain_stack) {
+ while ((ci = sk_X509_INFO_pop(chain_stack)) != NULL) {
+ X509_INFO_free(ci);
+ }
+ sk_X509_INFO_free(chain_stack);
+ }
+ if (chain_bio) {
+ BIO_free_all(chain_bio);
+ }
+ return res;
+}
+
+static SSL_CTX *
+create_server_ssl_ctx(const char *key, const size_t key_size,
+ const char *key_pass, const char *cert,
+ const size_t cert_size) {
+ SSL_CTX *ssl_ctx;
+ EC_KEY *ecdh;
+
+ ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+ if (!ssl_ctx) {
+ fprintf(stderr, "Could not create SSL/TLS context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+ /* >= TLSv1.2 */
+ SSL_CTX_set_options(
+ ssl_ctx, SSL_OP_ALL | 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);
+
+ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ if (!ecdh) {
+ fprintf(stderr, "EC_KEY_new_by_curv_name failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+ SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
+ EC_KEY_free(ecdh);
+
+ if (ssl_ctx_add_cert_chain(ssl_ctx, cert, cert_size) != true) {
+ fprintf(stderr, "Could not read certificate file\n");
+ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+
+ if (ssl_ctx_add_privatekey(ssl_ctx, key, key_size, key_pass) != true) {
+ fprintf(stderr, "Could not read private key\n");
+ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+
+ return ssl_ctx;
+}
+
+static void
+doh_receive_reply_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ uint_fast64_t sends = atomic_load(&nsends);
+ assert_non_null(handle);
+ UNUSED(cbarg);
+ UNUSED(region);
+
+ if (eresult == ISC_R_SUCCESS) {
+ atomic_fetch_add(&csends, 1);
+ atomic_fetch_add(&creads, 1);
+ if (sends > 0) {
+ atomic_fetch_sub(&nsends, 1);
+ }
+ isc_nm_resumeread(handle);
+ } else {
+ /* We failed to connect; try again */
+ while (sends > 0) {
+ /* Continue until we subtract or we are done */
+ if (atomic_compare_exchange_weak(&nsends, &sends,
+ sends - 1)) {
+ sends--;
+ break;
+ }
+ }
+ atomic_store(&was_error, true);
+ /* Send failed, we need to stop reading too */
+ isc_nm_cancelread(handle);
+ }
+}
+
+static void
+doh_reply_sent_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+ UNUSED(eresult);
+ UNUSED(cbarg);
+
+ assert_non_null(handle);
+
+ if (eresult == ISC_R_SUCCESS) {
+ atomic_fetch_add(&ssends, 1);
+ }
+}
+
+static void
+doh_receive_request_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ uint64_t magic = 0;
+
+ UNUSED(cbarg);
+ assert_non_null(handle);
+
+ if (eresult != ISC_R_SUCCESS) {
+ atomic_store(&was_error, true);
+ return;
+ }
+
+ atomic_fetch_add(&sreads, 1);
+
+ memmove(tcp_buffer_storage + tcp_buffer_length, region->base,
+ region->length);
+ tcp_buffer_length += region->length;
+
+ while (tcp_buffer_length >= sizeof(magic)) {
+ magic = *(uint64_t *)tcp_buffer_storage;
+ assert_true(magic == stop_magic || magic == send_magic);
+
+ tcp_buffer_length -= sizeof(magic);
+ memmove(tcp_buffer_storage, tcp_buffer_storage + sizeof(magic),
+ tcp_buffer_length);
+
+ if (magic == send_magic) {
+ isc_nm_send(handle, region, doh_reply_sent_cb, NULL);
+ return;
+ } else if (magic == stop_magic) {
+ /* We are done, so we don't send anything back */
+ /* There should be no more packets in the buffer */
+ assert_int_equal(tcp_buffer_length, 0);
+ }
+ }
+}
+
+static void
+doh_noop(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ isc_sockaddr_t tcp_connect_addr;
+ char req_url[256];
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr,
+ 0, NULL, NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ noop_read_cb, NULL, 0);
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+
+ sockaddr_to_url(&tcp_listen_addr, false, req_url, sizeof(req_url),
+ DOH_PATH);
+ (void)isc_nm_http_connect_send_request(
+ connect_nm, req_url, atomic_load(&POST),
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ noop_read_cb, NULL, NULL, 30000);
+
+ isc_nm_closedown(connect_nm);
+
+ assert_int_equal(0, atomic_load(&csends));
+ assert_int_equal(0, atomic_load(&creads));
+ assert_int_equal(0, atomic_load(&sreads));
+ assert_int_equal(0, atomic_load(&ssends));
+}
+
+static void
+doh_noop_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_noop(state);
+}
+
+static void
+doh_noop_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_noop(state);
+}
+
+static void
+doh_noresponse(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ isc_sockaddr_t tcp_connect_addr;
+ char req_url[256];
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr,
+ 0, NULL, NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ noop_read_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ sockaddr_to_url(&tcp_listen_addr, false, req_url, sizeof(req_url),
+ DOH_PATH);
+ (void)isc_nm_http_connect_send_request(
+ connect_nm, req_url, atomic_load(&POST),
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ noop_read_cb, NULL, NULL, 30000);
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+ isc_nm_closedown(connect_nm);
+}
+
+static void
+doh_noresponse_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_noresponse(state);
+}
+
+static void
+doh_noresponse_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_noresponse(state);
+}
+
+static void
+doh_receive_send_reply_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ uint_fast64_t sends = atomic_load(&nsends);
+ assert_non_null(handle);
+ UNUSED(region);
+
+ if (eresult == ISC_R_SUCCESS) {
+ atomic_fetch_add(&csends, 1);
+ atomic_fetch_add(&creads, 1);
+ if (sends > 0) {
+ size_t i;
+ atomic_fetch_sub(&nsends, 1);
+ for (i = 0; i < NWRITES / 2; i++) {
+ eresult = isc_nm_httprequest(
+ handle,
+ &(isc_region_t){
+ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ doh_receive_send_reply_cb, cbarg);
+ assert_true(eresult == ISC_R_SUCCESS);
+ }
+ }
+ } else {
+ /* We failed to connect; try again */
+ while (sends > 0) {
+ /* Continue until we subtract or we are done */
+ if (atomic_compare_exchange_weak(&nsends, &sends,
+ sends - 1)) {
+ sends--;
+ break;
+ }
+ }
+ atomic_store(&was_error, true);
+ }
+}
+
+static isc_threadresult_t
+doh_connect_thread(isc_threadarg_t arg) {
+ isc_nm_t *connect_nm = (isc_nm_t *)arg;
+ isc_sockaddr_t tcp_connect_addr;
+ char req_url[256];
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+ sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
+ sizeof(req_url), DOH_PATH);
+
+ while (atomic_load(&nsends) > 0) {
+ (void)isc_nm_http_connect_send_request(
+ connect_nm, req_url, atomic_load(&POST),
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ doh_receive_send_reply_cb, NULL, NULL, 5000);
+ }
+
+ return ((isc_threadresult_t)0);
+}
+
+static void
+doh_recv_one(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ isc_sockaddr_t tcp_connect_addr;
+ char req_url[256];
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ atomic_store(&nsends, 1);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
+ sizeof(req_url), DOH_PATH);
+ result = isc_nm_http_connect_send_request(
+ connect_nm, req_url, atomic_load(&POST),
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ doh_receive_reply_cb, NULL, NULL, 5000);
+
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ while (atomic_load(&nsends) > 0) {
+ if (atomic_load(&was_error)) {
+ break;
+ }
+ isc_thread_yield();
+ }
+
+ while (atomic_load(&ssends) != 1 || atomic_load(&sreads) != 1 ||
+ atomic_load(&csends) != 1)
+ {
+ if (atomic_load(&was_error)) {
+ break;
+ }
+ isc_thread_yield();
+ }
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+ isc_nm_closedown(connect_nm);
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ assert_int_equal(atomic_load(&csends), 1);
+ assert_int_equal(atomic_load(&creads), 1);
+ assert_int_equal(atomic_load(&sreads), 1);
+ assert_int_equal(atomic_load(&ssends), 1);
+}
+
+static void
+doh_recv_one_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_recv_one(state);
+}
+
+static void
+doh_recv_one_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_recv_one(state);
+}
+
+static void
+doh_recv_one_POST_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, true);
+ doh_recv_one(state);
+}
+
+static void
+doh_recv_one_GET_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, false);
+ doh_recv_one(state);
+}
+
+static void
+doh_connect_send_two_requests_cb(isc_nmhandle_t *handle, isc_result_t result,
+ void *arg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ result = isc_nm_httprequest(
+ handle,
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ doh_receive_reply_cb, arg);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ result = isc_nm_httprequest(
+ handle,
+ &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+ .length = send_msg.len },
+ doh_receive_reply_cb, arg);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ isc_nm_resumeread(handle);
+ return;
+error:
+ atomic_store(&was_error, true);
+}
+
+static void
+doh_recv_two(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ isc_sockaddr_t tcp_connect_addr;
+ char req_url[256];
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ atomic_store(&nsends, 2);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ sockaddr_to_url(&tcp_listen_addr, atomic_load(&use_TLS), req_url,
+ sizeof(req_url), DOH_PATH);
+ result = isc_nm_httpconnect(
+ connect_nm, NULL, NULL, req_url, atomic_load(&POST),
+ doh_connect_send_two_requests_cb, NULL, NULL, 5000, 0);
+
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ while (atomic_load(&nsends) > 0) {
+ if (atomic_load(&was_error)) {
+ break;
+ }
+ isc_thread_yield();
+ }
+
+ while (atomic_load(&ssends) != 2 || atomic_load(&sreads) != 2 ||
+ atomic_load(&csends) != 2)
+ {
+ if (atomic_load(&was_error)) {
+ break;
+ }
+ isc_thread_yield();
+ }
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+ isc_nm_closedown(connect_nm);
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ assert_int_equal(atomic_load(&csends), 2);
+ assert_int_equal(atomic_load(&creads), 2);
+ assert_int_equal(atomic_load(&sreads), 2);
+ assert_int_equal(atomic_load(&ssends), 2);
+}
+
+static void
+doh_recv_two_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_recv_two(state);
+}
+
+static void
+doh_recv_two_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_recv_two(state);
+}
+
+static void
+doh_recv_two_POST_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, true);
+ doh_recv_two(state);
+}
+
+static void
+doh_recv_two_GET_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, false);
+ doh_recv_two(state);
+}
+
+static void
+doh_recv_send(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+ isc_thread_t threads[32] = { 0 };
+ isc_sockaddr_t tcp_connect_addr;
+
+ if (!reuse_supported) {
+ skip();
+ return;
+ }
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_create(doh_connect_thread, connect_nm, &threads[i]);
+ }
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_join(threads[i], NULL);
+ }
+
+ isc_nm_closedown(connect_nm);
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ CHECK_RANGE_FULL(csends);
+ CHECK_RANGE_FULL(creads);
+ CHECK_RANGE_FULL(sreads);
+ CHECK_RANGE_FULL(ssends);
+}
+
+static void
+doh_recv_send_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_recv_send(state);
+}
+
+static void
+doh_recv_send_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_recv_send(state);
+}
+
+static void
+doh_recv_send_POST_TLS(void **state) {
+ atomic_store(&POST, true);
+ atomic_store(&use_TLS, true);
+ doh_recv_send(state);
+}
+
+static void
+doh_recv_send_GET_TLS(void **state) {
+ atomic_store(&POST, false);
+ atomic_store(&use_TLS, true);
+ doh_recv_send(state);
+}
+
+static void
+doh_recv_half_send(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+ isc_thread_t threads[32] = { 0 };
+ isc_sockaddr_t tcp_connect_addr;
+
+ if (!reuse_supported) {
+ skip();
+ return;
+ }
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_create(doh_connect_thread, connect_nm, &threads[i]);
+ }
+
+ while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+ isc_thread_yield();
+ }
+
+ isc_nm_closedown(connect_nm);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_join(threads[i], NULL);
+ }
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ CHECK_RANGE_HALF(csends);
+ CHECK_RANGE_HALF(creads);
+ CHECK_RANGE_HALF(sreads);
+ CHECK_RANGE_HALF(ssends);
+}
+
+static void
+doh_recv_half_send_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_recv_half_send(state);
+}
+
+static void
+doh_recv_half_send_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_recv_half_send(state);
+}
+
+static void
+doh_recv_half_send_POST_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, true);
+ doh_recv_half_send(state);
+}
+
+static void
+doh_recv_half_send_GET_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, false);
+ doh_recv_half_send(state);
+}
+
+static void
+doh_half_recv_send(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+ isc_thread_t threads[32] = { 0 };
+ isc_sockaddr_t tcp_connect_addr;
+
+ if (!reuse_supported) {
+ skip();
+ return;
+ }
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_create(doh_connect_thread, connect_nm, &threads[i]);
+ }
+
+ while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+ isc_thread_yield();
+ }
+
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_join(threads[i], NULL);
+ }
+
+ isc_nm_closedown(connect_nm);
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ CHECK_RANGE_HALF(csends);
+ CHECK_RANGE_HALF(creads);
+ CHECK_RANGE_HALF(sreads);
+ CHECK_RANGE_HALF(ssends);
+}
+
+static void
+doh_half_recv_send_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_half_recv_send(state);
+}
+
+static void
+doh_half_recv_send_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_half_recv_send(state);
+}
+
+static void
+doh_half_recv_send_POST_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, true);
+ doh_half_recv_send(state);
+}
+
+static void
+doh_half_recv_send_GET_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, false);
+ doh_half_recv_send(state);
+}
+
+static void
+doh_half_recv_half_send(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_nm_t *listen_nm = nm[0];
+ isc_nm_t *connect_nm = nm[1];
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *listen_sock = NULL;
+ size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+ isc_thread_t threads[32] = { 0 };
+ isc_sockaddr_t tcp_connect_addr;
+
+ if (!reuse_supported) {
+ skip();
+ return;
+ }
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = isc_nm_listenhttp(
+ listen_nm, (isc_nmiface_t *)&tcp_listen_addr, 0, NULL,
+ atomic_load(&use_TLS) ? server_ssl_ctx : NULL, &listen_sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_http_add_doh_endpoint(listen_sock, DOH_PATH,
+ doh_receive_request_cb, NULL, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_create(doh_connect_thread, connect_nm, &threads[i]);
+ }
+
+ while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+ isc_thread_yield();
+ }
+
+ isc_nm_closedown(connect_nm);
+ isc_nm_stoplistening(listen_sock);
+ isc_nmsocket_close(&listen_sock);
+ assert_null(listen_sock);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_join(threads[i], NULL);
+ }
+
+ X(csends);
+ X(creads);
+ X(sreads);
+ X(ssends);
+
+ CHECK_RANGE_HALF(csends);
+ CHECK_RANGE_HALF(creads);
+ CHECK_RANGE_HALF(sreads);
+ CHECK_RANGE_HALF(ssends);
+}
+
+static void
+doh_half_recv_half_send_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_half_recv_half_send(state);
+}
+
+static void
+doh_half_recv_half_send_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_half_recv_half_send(state);
+}
+
+static void
+doh_half_recv_half_send_POST_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, true);
+ doh_half_recv_half_send(state);
+}
+
+static void
+doh_half_recv_half_send_GET_TLS(void **state) {
+ atomic_store(&use_TLS, true);
+ atomic_store(&POST, false);
+ doh_half_recv_half_send(state);
+}
+
+static void
+doh_parse_GET_query_string(void **state) {
+ UNUSED(state);
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] =
+ "dns=AAABAAABAAAAAAAAAWE-"
+ "NjJjaGFyYWN0ZXJsYWJlbC1tYWtlcy1iYXNlNjR1cmwtZGlzdGluY3"
+ "QtZnJvbS1zdGFuZGFyZC1iYXNlNjQHZXhhbXBsZQNjb20AAAEAAQ";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == strlen(str) - 4);
+ assert_true(memcmp(queryp, str + 4, len) == 0);
+ }
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] =
+ "?dns=AAABAAABAAAAAAAAAWE-"
+ "NjJjaGFyYWN0ZXJsYWJlbC1tYWtlcy1iYXNlNjR1cmwtZGlzdGluY3"
+ "QtZnJvbS1zdGFuZGFyZC1iYXNlNjQHZXhhbXBsZQNjb20AAAEAAQ&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == strlen(str) - 6);
+ assert_true(memcmp(queryp, str + 5, len) == 0);
+ }
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123&dns=567";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == 3);
+ assert_true(memcmp(queryp, "567", 3) == 0);
+ }
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?name1=123&dns=567&name2=123&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == 3);
+ assert_true(memcmp(queryp, "567", 3) == 0);
+ }
+ /* complex, but still valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] =
+ "?title=%D0%92%D1%96%D0%B4%D1%81%D0%BE%D1%82%D0%BA%D0%"
+ "BE%D0%B2%D0%B5_%D0%BA%D0%BE%D0%B4%D1%83%D0%B2%D0%B0%"
+ "D0%BD%D0%BD%D1%8F&dns=123&veaction=edit§ion=0";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == 3);
+ assert_true(memcmp(queryp, "123", 3) == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] =
+ "?title=%D0%92%D1%96%D0%B4%D1%81%D0%BE%D1%82%D0%BA%D0%"
+ "BE%D0%B2%D0%B5_%D0%BA%D0%BE%D0%B4%D1%83%D0%B2%D0%B0%"
+ "D0%BD%D0%BD%D1%8F&veaction=edit§ion=0";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123&&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123%12&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == 6);
+ assert_true(memcmp(queryp, "123%12", 6) == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123%ZZ&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123%%&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* invalid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123%AZ&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_false(ret);
+ assert_null(queryp);
+ assert_true(len == 0);
+ }
+ /* valid */
+ {
+ bool ret;
+ const char *queryp = NULL;
+ size_t len = 0;
+ char str[] = "?dns=123%0AZ&";
+
+ ret = isc__nm_parse_doh_query_string(str, &queryp, &len);
+ assert_true(ret);
+ assert_non_null(queryp);
+ assert_true(len > 0);
+ assert_true(len == 7);
+ assert_true(memcmp(queryp, "123%0AZ", 7) == 0);
+ }
+}
+
+static void
+doh_base64url_to_base64(void **state) {
+ UNUSED(state);
+ char *res;
+ size_t res_len = 0;
+ /* valid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3VyZS4";
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3VyZS4=";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhcw==";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3Vy";
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3Vy";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3U";
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3U=";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhcw==";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "PDw_Pz8-Pg";
+ char res_test[] = "PDw/Pz8+Pg==";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char test[] = "PDw_Pz8-Pg";
+ char res_test[] = "PDw/Pz8+Pg==";
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ NULL);
+ assert_non_null(res);
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* invalid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ res_len = 0;
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, 0, &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "";
+ res_len = 0;
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "PDw_Pz8-Pg==";
+ res_len = 0;
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "PDw_Pz8-Pg%3D%3D"; /* percent encoded "==" at the
+ end */
+ res_len = 0;
+
+ res = isc__nm_base64url_to_base64(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ res_len = 0;
+
+ res = isc__nm_base64url_to_base64(test_mctx, NULL, 31231,
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+}
+
+static void
+doh_base64_to_base64url(void **state) {
+ char *res;
+ size_t res_len = 0;
+ UNUSED(state);
+ /* valid */
+ {
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3VyZS4";
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3VyZS4=";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw==";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3Vy";
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3Vy";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhc3U";
+ char test[] = "YW55IGNhcm5hbCBwbGVhc3U=";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw==";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "PDw_Pz8-Pg";
+ char test[] = "PDw/Pz8+Pg==";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_non_null(res);
+ assert_true(res_len == strlen(res_test));
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* valid */
+ {
+ char res_test[] = "PDw_Pz8-Pg";
+ char test[] = "PDw/Pz8+Pg==";
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ NULL);
+ assert_non_null(res);
+ assert_true(strcmp(res, res_test) == 0);
+ isc_mem_free(test_mctx, res);
+ }
+ /* invalid */
+ {
+ char test[] = "YW55IGNhcm5hbCBwbGVhcw";
+ res_len = 0;
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, 0, &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "";
+ res_len = 0;
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "PDw_Pz8-Pg==";
+ res_len = 0;
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ char test[] = "PDw_Pz8-Pg%3D%3D"; /* percent encoded "==" at the
+ end */
+ res_len = 0;
+
+ res = isc__nm_base64_to_base64url(test_mctx, test, strlen(test),
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+ /* invalid */
+ {
+ res_len = 0;
+
+ res = isc__nm_base64_to_base64url(test_mctx, NULL, 31231,
+ &res_len);
+ assert_null(res);
+ assert_true(res_len == 0);
+ }
+}
+/*
+static char wikipedia_org_A[] = { 0xae, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x77,
+ 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69,
+ 0x61, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00,
+ 0x01, 0x00, 0x01 };
+
+static void
+doh_print_reply_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ assert_non_null(handle);
+ UNUSED(cbarg);
+ UNUSED(region);
+
+ puts("Cloudflare DNS query result:");
+ if (eresult == ISC_R_SUCCESS) {
+ puts("success!");
+ printf("Response size: %lu\n", region->length);
+ atomic_fetch_add(&creads, 1);
+ isc_nm_resumeread(handle);
+ } else {
+ puts("failure!");
+ atomic_store(&was_error, true);
+ isc_nm_cancelread(handle);
+ }
+}
+
+static void
+doh_cloudflare(void **state) {
+ isc_nm_t **nm = (isc_nm_t **)*state;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ result = isc_nm_http_connect_send_request(
+ nm[0], "https://cloudflare-dns.com/dns-query",
+ atomic_load(&POST),
+ &(isc_region_t){ .base = (uint8_t *)wikipedia_org_A,
+ .length = sizeof(wikipedia_org_A) },
+ doh_print_reply_cb, NULL, NULL, 5000);
+
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ while (atomic_load(&creads) != 1 && atomic_load(&was_error) == false) {
+ isc_thread_yield();
+ }
+
+ isc_nm_closedown(nm[0]);
+}
+
+static void
+doh_cloudflare_POST(void **state) {
+ atomic_store(&POST, true);
+ doh_cloudflare(state);
+}
+
+static void
+doh_cloudflare_GET(void **state) {
+ atomic_store(&POST, false);
+ doh_cloudflare(state);
+}
+*/
+int
+main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(doh_parse_GET_query_string,
+ NULL, NULL),
+ cmocka_unit_test_setup_teardown(doh_base64url_to_base64, NULL,
+ NULL),
+ cmocka_unit_test_setup_teardown(doh_base64_to_base64url, NULL,
+ NULL),
+ cmocka_unit_test_setup_teardown(doh_noop_POST, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_noop_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_noresponse_POST, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_noresponse_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_one_POST, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_one_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_one_POST_TLS, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_one_GET_TLS, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_two_POST, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_two_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_two_POST_TLS, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_two_GET_TLS, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_send_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_send_POST, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_send_GET_TLS, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_send_POST_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_half_send_GET,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_half_send_POST,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_half_send_GET_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_recv_half_send_POST_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_send_GET,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_send_POST,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_send_GET_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_send_POST_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_half_send_GET,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_half_send_POST,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_half_recv_half_send_GET_TLS,
+ nm_setup, nm_teardown),
+ cmocka_unit_test_setup_teardown(
+ doh_half_recv_half_send_POST_TLS, nm_setup,
+ nm_teardown),
+ /*cmocka_unit_test_setup_teardown(doh_cloudflare_GET, nm_setup,
+ nm_teardown),
+ cmocka_unit_test_setup_teardown(doh_cloudflare_POST, nm_setup,
+ nm_teardown)*/
+ };
+
+ return (cmocka_run_group_tests(tests, _setup, _teardown));
+}
+
+#else /* HAVE_CMOCKA */
+
+#include <stdio.h>
+
+int
+main(void) {
+ printf("1..0 # Skipped: cmocka not available\n");
+ return (0);
+}
+
+#endif /* if HAVE_CMOCKA */
--- /dev/null
+/*
+ * 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.
+ */
+
+/* THIS FILE IS AUTOMATICALLY GENERATED!!! DO NOT EDIT!!! */
+/* Generated on: [ Tue Dec 29 17:58:04 2020 ], from file: test_cert.pem */
+
+#ifndef _TLS_TEST_CERT_H
+#define _TLS_TEST_CERT_H
+
+static const unsigned char TLS_test_cert[] = {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43,
+ 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x46, 0x69, 0x54, 0x43, 0x43,
+ 0x41, 0x33, 0x47, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55,
+ 0x66, 0x67, 0x2f, 0x48, 0x6f, 0x43, 0x6c, 0x42, 0x43, 0x67, 0x6e, 0x64,
+ 0x50, 0x32, 0x30, 0x56, 0x47, 0x2b, 0x30, 0x4c, 0x63, 0x4b, 0x58, 0x56,
+ 0x69, 0x66, 0x55, 0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49,
+ 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x4c, 0x0a, 0x42, 0x51, 0x41,
+ 0x77, 0x55, 0x7a, 0x45, 0x4c, 0x4d, 0x41, 0x6b, 0x47, 0x41, 0x31, 0x55,
+ 0x45, 0x42, 0x68, 0x4d, 0x43, 0x56, 0x55, 0x45, 0x78, 0x45, 0x44, 0x41,
+ 0x4f, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x67, 0x4d, 0x42, 0x31, 0x56,
+ 0x72, 0x63, 0x6d, 0x46, 0x70, 0x62, 0x6d, 0x55, 0x78, 0x45, 0x44, 0x41,
+ 0x4f, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x63, 0x4d, 0x42, 0x30, 0x74,
+ 0x6f, 0x0a, 0x59, 0x58, 0x4a, 0x72, 0x61, 0x58, 0x59, 0x78, 0x44, 0x44,
+ 0x41, 0x4b, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x6f, 0x4d, 0x41, 0x30,
+ 0x6c, 0x54, 0x51, 0x7a, 0x45, 0x53, 0x4d, 0x42, 0x41, 0x47, 0x41, 0x31,
+ 0x55, 0x45, 0x41, 0x77, 0x77, 0x4a, 0x62, 0x47, 0x39, 0x6a, 0x59, 0x57,
+ 0x78, 0x6f, 0x62, 0x33, 0x4e, 0x30, 0x4d, 0x43, 0x41, 0x58, 0x44, 0x54,
+ 0x49, 0x77, 0x4d, 0x54, 0x49, 0x79, 0x0a, 0x4f, 0x54, 0x45, 0x31, 0x4e,
+ 0x54, 0x51, 0x79, 0x4d, 0x31, 0x6f, 0x59, 0x44, 0x7a, 0x49, 0x78, 0x4d,
+ 0x6a, 0x41, 0x78, 0x4d, 0x6a, 0x41, 0x31, 0x4d, 0x54, 0x55, 0x31, 0x4e,
+ 0x44, 0x49, 0x7a, 0x57, 0x6a, 0x42, 0x54, 0x4d, 0x51, 0x73, 0x77, 0x43,
+ 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x51,
+ 0x54, 0x45, 0x51, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x45, 0x0a,
+ 0x43, 0x41, 0x77, 0x48, 0x56, 0x57, 0x74, 0x79, 0x59, 0x57, 0x6c, 0x75,
+ 0x5a, 0x54, 0x45, 0x51, 0x4d, 0x41, 0x34, 0x47, 0x41, 0x31, 0x55, 0x45,
+ 0x42, 0x77, 0x77, 0x48, 0x53, 0x32, 0x68, 0x68, 0x63, 0x6d, 0x74, 0x70,
+ 0x64, 0x6a, 0x45, 0x4d, 0x4d, 0x41, 0x6f, 0x47, 0x41, 0x31, 0x55, 0x45,
+ 0x43, 0x67, 0x77, 0x44, 0x53, 0x56, 0x4e, 0x44, 0x4d, 0x52, 0x49, 0x77,
+ 0x45, 0x41, 0x59, 0x44, 0x0a, 0x56, 0x51, 0x51, 0x44, 0x44, 0x41, 0x6c,
+ 0x73, 0x62, 0x32, 0x4e, 0x68, 0x62, 0x47, 0x68, 0x76, 0x63, 0x33, 0x51,
+ 0x77, 0x67, 0x67, 0x49, 0x69, 0x4d, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71,
+ 0x47, 0x53, 0x49, 0x62, 0x33, 0x44, 0x51, 0x45, 0x42, 0x41, 0x51, 0x55,
+ 0x41, 0x41, 0x34, 0x49, 0x43, 0x44, 0x77, 0x41, 0x77, 0x67, 0x67, 0x49,
+ 0x4b, 0x41, 0x6f, 0x49, 0x43, 0x41, 0x51, 0x43, 0x71, 0x0a, 0x74, 0x49,
+ 0x42, 0x33, 0x43, 0x4c, 0x75, 0x71, 0x52, 0x54, 0x6d, 0x67, 0x39, 0x4d,
+ 0x45, 0x56, 0x69, 0x70, 0x30, 0x63, 0x43, 0x45, 0x2f, 0x33, 0x68, 0x47,
+ 0x64, 0x63, 0x53, 0x61, 0x51, 0x4f, 0x64, 0x6b, 0x39, 0x42, 0x5a, 0x41,
+ 0x53, 0x79, 0x4f, 0x4a, 0x35, 0x4c, 0x42, 0x38, 0x2f, 0x63, 0x2f, 0x48,
+ 0x30, 0x38, 0x4c, 0x30, 0x63, 0x4c, 0x79, 0x43, 0x4c, 0x6b, 0x31, 0x70,
+ 0x33, 0x78, 0x0a, 0x67, 0x56, 0x2b, 0x53, 0x57, 0x2f, 0x6d, 0x79, 0x38,
+ 0x65, 0x6b, 0x6f, 0x4d, 0x4a, 0x6a, 0x70, 0x4e, 0x54, 0x37, 0x70, 0x68,
+ 0x74, 0x78, 0x4a, 0x4e, 0x4e, 0x6c, 0x4e, 0x36, 0x39, 0x56, 0x63, 0x6e,
+ 0x75, 0x71, 0x33, 0x41, 0x45, 0x44, 0x4a, 0x63, 0x41, 0x41, 0x4c, 0x6f,
+ 0x38, 0x2b, 0x63, 0x65, 0x33, 0x6d, 0x4b, 0x35, 0x77, 0x41, 0x4d, 0x73,
+ 0x7a, 0x61, 0x52, 0x7a, 0x33, 0x44, 0x34, 0x0a, 0x5a, 0x4e, 0x66, 0x65,
+ 0x54, 0x33, 0x61, 0x6b, 0x39, 0x45, 0x78, 0x74, 0x47, 0x62, 0x57, 0x42,
+ 0x4f, 0x6d, 0x32, 0x4f, 0x66, 0x68, 0x49, 0x2b, 0x45, 0x53, 0x6d, 0x32,
+ 0x55, 0x41, 0x32, 0x34, 0x61, 0x49, 0x6a, 0x46, 0x74, 0x4a, 0x52, 0x72,
+ 0x6d, 0x56, 0x4d, 0x65, 0x37, 0x68, 0x44, 0x4b, 0x54, 0x78, 0x68, 0x49,
+ 0x69, 0x54, 0x6a, 0x4c, 0x54, 0x76, 0x56, 0x4e, 0x50, 0x2b, 0x77, 0x53,
+ 0x0a, 0x43, 0x62, 0x32, 0x47, 0x59, 0x34, 0x74, 0x7a, 0x7a, 0x30, 0x66,
+ 0x66, 0x79, 0x62, 0x50, 0x6d, 0x65, 0x5a, 0x50, 0x58, 0x61, 0x32, 0x54,
+ 0x39, 0x77, 0x65, 0x30, 0x68, 0x6a, 0x41, 0x56, 0x53, 0x4b, 0x47, 0x76,
+ 0x76, 0x30, 0x48, 0x41, 0x48, 0x65, 0x4a, 0x72, 0x4f, 0x75, 0x31, 0x75,
+ 0x38, 0x79, 0x4a, 0x4c, 0x6e, 0x58, 0x72, 0x64, 0x47, 0x2f, 0x4f, 0x7a,
+ 0x4b, 0x71, 0x65, 0x4a, 0x38, 0x0a, 0x7a, 0x74, 0x4a, 0x34, 0x47, 0x59,
+ 0x58, 0x7a, 0x70, 0x4b, 0x39, 0x42, 0x68, 0x74, 0x57, 0x6b, 0x6d, 0x7a,
+ 0x38, 0x74, 0x41, 0x78, 0x7a, 0x77, 0x6c, 0x5a, 0x47, 0x6d, 0x54, 0x41,
+ 0x79, 0x58, 0x45, 0x55, 0x50, 0x79, 0x33, 0x42, 0x71, 0x61, 0x4a, 0x66,
+ 0x2b, 0x6c, 0x46, 0x4e, 0x67, 0x61, 0x68, 0x74, 0x57, 0x78, 0x6c, 0x77,
+ 0x58, 0x2f, 0x31, 0x59, 0x4c, 0x35, 0x75, 0x69, 0x69, 0x49, 0x0a, 0x77,
+ 0x4c, 0x69, 0x41, 0x36, 0x67, 0x2f, 0x39, 0x39, 0x79, 0x4e, 0x4c, 0x41,
+ 0x69, 0x34, 0x38, 0x54, 0x6b, 0x41, 0x63, 0x44, 0x58, 0x42, 0x35, 0x38,
+ 0x61, 0x5a, 0x78, 0x36, 0x32, 0x43, 0x63, 0x6b, 0x58, 0x75, 0x4d, 0x6d,
+ 0x32, 0x68, 0x61, 0x37, 0x6e, 0x43, 0x64, 0x77, 0x66, 0x76, 0x31, 0x71,
+ 0x71, 0x6a, 0x72, 0x79, 0x44, 0x56, 0x34, 0x32, 0x2b, 0x56, 0x37, 0x48,
+ 0x75, 0x64, 0x4f, 0x0a, 0x57, 0x36, 0x33, 0x39, 0x6f, 0x6b, 0x6a, 0x69,
+ 0x56, 0x32, 0x33, 0x69, 0x48, 0x79, 0x2f, 0x33, 0x2f, 0x2f, 0x72, 0x56,
+ 0x31, 0x44, 0x59, 0x6a, 0x76, 0x35, 0x49, 0x4c, 0x48, 0x51, 0x79, 0x6d,
+ 0x7a, 0x4a, 0x2b, 0x76, 0x6e, 0x4a, 0x38, 0x4a, 0x69, 0x33, 0x59, 0x74,
+ 0x32, 0x66, 0x44, 0x44, 0x55, 0x58, 0x66, 0x33, 0x41, 0x45, 0x6e, 0x49,
+ 0x32, 0x74, 0x54, 0x75, 0x4c, 0x34, 0x54, 0x42, 0x0a, 0x4b, 0x54, 0x69,
+ 0x74, 0x33, 0x79, 0x79, 0x69, 0x54, 0x56, 0x52, 0x4e, 0x45, 0x59, 0x39,
+ 0x63, 0x4b, 0x47, 0x54, 0x6a, 0x73, 0x41, 0x7a, 0x48, 0x44, 0x7a, 0x35,
+ 0x33, 0x6b, 0x74, 0x6b, 0x38, 0x2b, 0x77, 0x44, 0x73, 0x49, 0x71, 0x76,
+ 0x4d, 0x54, 0x6d, 0x79, 0x33, 0x72, 0x65, 0x6d, 0x36, 0x68, 0x4b, 0x30,
+ 0x71, 0x51, 0x30, 0x48, 0x38, 0x50, 0x4f, 0x77, 0x57, 0x66, 0x31, 0x75,
+ 0x6c, 0x0a, 0x55, 0x71, 0x46, 0x67, 0x37, 0x34, 0x77, 0x67, 0x62, 0x61,
+ 0x32, 0x73, 0x71, 0x37, 0x56, 0x78, 0x61, 0x62, 0x63, 0x32, 0x71, 0x65,
+ 0x4b, 0x71, 0x74, 0x59, 0x4c, 0x59, 0x30, 0x4e, 0x6b, 0x51, 0x71, 0x44,
+ 0x67, 0x73, 0x42, 0x30, 0x55, 0x56, 0x7a, 0x50, 0x70, 0x68, 0x53, 0x56,
+ 0x5a, 0x51, 0x65, 0x49, 0x49, 0x41, 0x4b, 0x6c, 0x6f, 0x7a, 0x6a, 0x49,
+ 0x43, 0x7a, 0x39, 0x75, 0x48, 0x47, 0x0a, 0x39, 0x48, 0x31, 0x36, 0x51,
+ 0x52, 0x45, 0x74, 0x43, 0x4d, 0x78, 0x37, 0x42, 0x46, 0x77, 0x48, 0x74,
+ 0x36, 0x49, 0x31, 0x2b, 0x70, 0x4a, 0x69, 0x50, 0x50, 0x47, 0x69, 0x78,
+ 0x66, 0x42, 0x47, 0x74, 0x72, 0x64, 0x4a, 0x51, 0x78, 0x39, 0x34, 0x62,
+ 0x77, 0x76, 0x43, 0x6f, 0x38, 0x59, 0x4a, 0x71, 0x57, 0x77, 0x67, 0x66,
+ 0x41, 0x77, 0x50, 0x30, 0x57, 0x45, 0x6c, 0x6f, 0x6b, 0x69, 0x6b, 0x0a,
+ 0x53, 0x6a, 0x6a, 0x6b, 0x79, 0x62, 0x33, 0x69, 0x53, 0x48, 0x6c, 0x65,
+ 0x54, 0x52, 0x36, 0x62, 0x7a, 0x48, 0x47, 0x73, 0x69, 0x78, 0x49, 0x38,
+ 0x72, 0x76, 0x44, 0x65, 0x33, 0x74, 0x76, 0x63, 0x79, 0x44, 0x4e, 0x46,
+ 0x2f, 0x78, 0x34, 0x68, 0x70, 0x77, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42,
+ 0x6f, 0x31, 0x4d, 0x77, 0x55, 0x54, 0x41, 0x64, 0x42, 0x67, 0x4e, 0x56,
+ 0x48, 0x51, 0x34, 0x45, 0x0a, 0x46, 0x67, 0x51, 0x55, 0x68, 0x6c, 0x51,
+ 0x77, 0x37, 0x49, 0x54, 0x2b, 0x38, 0x59, 0x6e, 0x5a, 0x68, 0x6e, 0x62,
+ 0x6d, 0x38, 0x62, 0x32, 0x64, 0x6e, 0x4d, 0x48, 0x6e, 0x69, 0x76, 0x6b,
+ 0x77, 0x48, 0x77, 0x59, 0x44, 0x56, 0x52, 0x30, 0x6a, 0x42, 0x42, 0x67,
+ 0x77, 0x46, 0x6f, 0x41, 0x55, 0x68, 0x6c, 0x51, 0x77, 0x37, 0x49, 0x54,
+ 0x2b, 0x38, 0x59, 0x6e, 0x5a, 0x68, 0x6e, 0x62, 0x6d, 0x0a, 0x38, 0x62,
+ 0x32, 0x64, 0x6e, 0x4d, 0x48, 0x6e, 0x69, 0x76, 0x6b, 0x77, 0x44, 0x77,
+ 0x59, 0x44, 0x56, 0x52, 0x30, 0x54, 0x41, 0x51, 0x48, 0x2f, 0x42, 0x41,
+ 0x55, 0x77, 0x41, 0x77, 0x45, 0x42, 0x2f, 0x7a, 0x41, 0x4e, 0x42, 0x67,
+ 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51,
+ 0x73, 0x46, 0x41, 0x41, 0x4f, 0x43, 0x41, 0x67, 0x45, 0x41, 0x49, 0x59,
+ 0x36, 0x6e, 0x0a, 0x74, 0x54, 0x36, 0x54, 0x5a, 0x59, 0x2b, 0x4b, 0x6b,
+ 0x56, 0x7a, 0x34, 0x67, 0x31, 0x56, 0x4c, 0x50, 0x6d, 0x44, 0x72, 0x4e,
+ 0x6e, 0x6a, 0x65, 0x58, 0x59, 0x61, 0x35, 0x59, 0x42, 0x4d, 0x58, 0x4a,
+ 0x4f, 0x77, 0x61, 0x56, 0x56, 0x44, 0x2b, 0x6e, 0x6d, 0x42, 0x64, 0x51,
+ 0x51, 0x53, 0x67, 0x41, 0x2f, 0x30, 0x37, 0x41, 0x61, 0x63, 0x68, 0x71,
+ 0x61, 0x2f, 0x52, 0x77, 0x54, 0x31, 0x61, 0x0a, 0x2b, 0x7a, 0x37, 0x45,
+ 0x2f, 0x4b, 0x56, 0x34, 0x78, 0x76, 0x43, 0x55, 0x4d, 0x6a, 0x64, 0x64,
+ 0x4f, 0x64, 0x36, 0x48, 0x77, 0x51, 0x35, 0x58, 0x32, 0x2b, 0x7a, 0x6e,
+ 0x46, 0x41, 0x71, 0x73, 0x48, 0x75, 0x5a, 0x41, 0x46, 0x63, 0x36, 0x37,
+ 0x36, 0x47, 0x4d, 0x77, 0x75, 0x73, 0x37, 0x45, 0x69, 0x30, 0x53, 0x4f,
+ 0x41, 0x41, 0x36, 0x44, 0x30, 0x6f, 0x63, 0x52, 0x72, 0x2f, 0x68, 0x4c,
+ 0x0a, 0x62, 0x6c, 0x4f, 0x4b, 0x70, 0x48, 0x79, 0x66, 0x72, 0x46, 0x76,
+ 0x42, 0x70, 0x5a, 0x6a, 0x6e, 0x4f, 0x49, 0x33, 0x47, 0x68, 0x37, 0x45,
+ 0x32, 0x49, 0x57, 0x71, 0x6f, 0x62, 0x7a, 0x73, 0x35, 0x62, 0x58, 0x5a,
+ 0x45, 0x6a, 0x34, 0x47, 0x47, 0x58, 0x33, 0x30, 0x39, 0x45, 0x2f, 0x39,
+ 0x38, 0x6e, 0x33, 0x47, 0x35, 0x46, 0x68, 0x2b, 0x78, 0x31, 0x7a, 0x61,
+ 0x59, 0x38, 0x79, 0x78, 0x4f, 0x0a, 0x56, 0x62, 0x4a, 0x31, 0x50, 0x61,
+ 0x4b, 0x64, 0x30, 0x4f, 0x36, 0x4a, 0x45, 0x6b, 0x72, 0x36, 0x6f, 0x36,
+ 0x6e, 0x61, 0x48, 0x33, 0x44, 0x45, 0x33, 0x53, 0x68, 0x53, 0x68, 0x54,
+ 0x30, 0x6b, 0x36, 0x63, 0x53, 0x68, 0x5a, 0x38, 0x2b, 0x74, 0x6f, 0x35,
+ 0x64, 0x47, 0x38, 0x6b, 0x2b, 0x6f, 0x72, 0x69, 0x39, 0x41, 0x44, 0x52,
+ 0x52, 0x48, 0x4f, 0x62, 0x71, 0x51, 0x54, 0x42, 0x4a, 0x46, 0x0a, 0x6e,
+ 0x67, 0x35, 0x4f, 0x72, 0x6f, 0x46, 0x2f, 0x31, 0x34, 0x75, 0x73, 0x65,
+ 0x2b, 0x49, 0x61, 0x42, 0x32, 0x79, 0x41, 0x57, 0x30, 0x68, 0x44, 0x37,
+ 0x43, 0x2f, 0x79, 0x31, 0x2f, 0x56, 0x38, 0x41, 0x78, 0x35, 0x77, 0x31,
+ 0x38, 0x59, 0x51, 0x36, 0x6e, 0x4d, 0x44, 0x7a, 0x51, 0x56, 0x64, 0x77,
+ 0x63, 0x59, 0x69, 0x4c, 0x77, 0x54, 0x42, 0x31, 0x66, 0x4b, 0x30, 0x33,
+ 0x4f, 0x55, 0x36, 0x0a, 0x31, 0x72, 0x32, 0x59, 0x62, 0x47, 0x4b, 0x49,
+ 0x62, 0x43, 0x59, 0x63, 0x4e, 0x76, 0x59, 0x72, 0x4a, 0x2b, 0x61, 0x38,
+ 0x74, 0x59, 0x56, 0x48, 0x58, 0x51, 0x68, 0x2f, 0x78, 0x52, 0x61, 0x74,
+ 0x79, 0x61, 0x4e, 0x69, 0x41, 0x59, 0x34, 0x77, 0x6b, 0x58, 0x36, 0x49,
+ 0x32, 0x79, 0x43, 0x66, 0x69, 0x35, 0x61, 0x6d, 0x32, 0x69, 0x4f, 0x42,
+ 0x50, 0x51, 0x72, 0x36, 0x6f, 0x38, 0x58, 0x58, 0x0a, 0x2b, 0x44, 0x69,
+ 0x35, 0x4a, 0x4e, 0x58, 0x59, 0x55, 0x2f, 0x76, 0x76, 0x59, 0x53, 0x4d,
+ 0x35, 0x51, 0x61, 0x79, 0x6f, 0x35, 0x78, 0x7a, 0x4c, 0x63, 0x4d, 0x7a,
+ 0x76, 0x57, 0x77, 0x4b, 0x70, 0x4d, 0x6c, 0x4c, 0x33, 0x6c, 0x6f, 0x30,
+ 0x48, 0x34, 0x53, 0x70, 0x54, 0x6e, 0x75, 0x66, 0x55, 0x63, 0x78, 0x39,
+ 0x6f, 0x72, 0x2f, 0x75, 0x5a, 0x6e, 0x44, 0x51, 0x45, 0x30, 0x43, 0x2f,
+ 0x2f, 0x0a, 0x6d, 0x6c, 0x44, 0x52, 0x67, 0x2f, 0x73, 0x48, 0x6d, 0x65,
+ 0x62, 0x37, 0x6f, 0x56, 0x79, 0x65, 0x72, 0x39, 0x30, 0x33, 0x64, 0x45,
+ 0x74, 0x6b, 0x73, 0x79, 0x32, 0x7a, 0x74, 0x31, 0x6b, 0x45, 0x4b, 0x45,
+ 0x78, 0x77, 0x41, 0x51, 0x75, 0x73, 0x57, 0x74, 0x63, 0x77, 0x4c, 0x7a,
+ 0x66, 0x71, 0x45, 0x30, 0x32, 0x6f, 0x51, 0x78, 0x56, 0x74, 0x51, 0x71,
+ 0x73, 0x66, 0x47, 0x78, 0x30, 0x4c, 0x0a, 0x43, 0x75, 0x6a, 0x51, 0x65,
+ 0x5a, 0x56, 0x76, 0x42, 0x30, 0x77, 0x6c, 0x63, 0x33, 0x33, 0x33, 0x4d,
+ 0x31, 0x32, 0x4d, 0x41, 0x69, 0x44, 0x75, 0x6c, 0x71, 0x2b, 0x73, 0x58,
+ 0x4d, 0x70, 0x4c, 0x53, 0x2b, 0x47, 0x79, 0x39, 0x57, 0x34, 0x34, 0x42,
+ 0x55, 0x45, 0x74, 0x46, 0x4f, 0x77, 0x51, 0x2f, 0x58, 0x35, 0x57, 0x32,
+ 0x58, 0x77, 0x4d, 0x53, 0x45, 0x42, 0x57, 0x39, 0x49, 0x66, 0x6f, 0x0a,
+ 0x58, 0x4d, 0x6d, 0x4f, 0x37, 0x51, 0x69, 0x35, 0x59, 0x65, 0x79, 0x7a,
+ 0x46, 0x47, 0x78, 0x31, 0x36, 0x6e, 0x59, 0x6f, 0x35, 0x61, 0x45, 0x6e,
+ 0x4c, 0x2b, 0x7a, 0x6d, 0x72, 0x64, 0x39, 0x43, 0x36, 0x36, 0x61, 0x51,
+ 0x76, 0x6b, 0x42, 0x66, 0x5a, 0x2f, 0x6e, 0x50, 0x70, 0x76, 0x55, 0x63,
+ 0x53, 0x57, 0x69, 0x34, 0x73, 0x45, 0x47, 0x44, 0x69, 0x6a, 0x43, 0x4c,
+ 0x65, 0x4b, 0x43, 0x59, 0x0a, 0x36, 0x4c, 0x34, 0x6e, 0x74, 0x4e, 0x72,
+ 0x6e, 0x7a, 0x62, 0x79, 0x38, 0x70, 0x70, 0x71, 0x50, 0x64, 0x66, 0x79,
+ 0x58, 0x4e, 0x67, 0x4e, 0x35, 0x34, 0x6e, 0x4d, 0x4e, 0x77, 0x55, 0x78,
+ 0x66, 0x47, 0x33, 0x4a, 0x41, 0x69, 0x55, 0x55, 0x3d, 0x0a, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49,
+ 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a
+};
+
+/* THIS FILE IS AUTOMATICALLY GENERATED!!! DO NOT EDIT!!! */
+/* Generated on: [ Tue Dec 29 17:58:52 2020 ], from file: test_key.pem */
+
+static const unsigned char TLS_test_key[] = {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x4a, 0x51, 0x67, 0x49, 0x42,
+ 0x41, 0x44, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
+ 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
+ 0x43, 0x53, 0x77, 0x77, 0x67, 0x67, 0x6b, 0x6f, 0x41, 0x67, 0x45, 0x41,
+ 0x41, 0x6f, 0x49, 0x43, 0x41, 0x51, 0x43, 0x71, 0x74, 0x49, 0x42, 0x33,
+ 0x43, 0x4c, 0x75, 0x71, 0x52, 0x54, 0x6d, 0x67, 0x0a, 0x39, 0x4d, 0x45,
+ 0x56, 0x69, 0x70, 0x30, 0x63, 0x43, 0x45, 0x2f, 0x33, 0x68, 0x47, 0x64,
+ 0x63, 0x53, 0x61, 0x51, 0x4f, 0x64, 0x6b, 0x39, 0x42, 0x5a, 0x41, 0x53,
+ 0x79, 0x4f, 0x4a, 0x35, 0x4c, 0x42, 0x38, 0x2f, 0x63, 0x2f, 0x48, 0x30,
+ 0x38, 0x4c, 0x30, 0x63, 0x4c, 0x79, 0x43, 0x4c, 0x6b, 0x31, 0x70, 0x33,
+ 0x78, 0x67, 0x56, 0x2b, 0x53, 0x57, 0x2f, 0x6d, 0x79, 0x38, 0x65, 0x6b,
+ 0x6f, 0x0a, 0x4d, 0x4a, 0x6a, 0x70, 0x4e, 0x54, 0x37, 0x70, 0x68, 0x74,
+ 0x78, 0x4a, 0x4e, 0x4e, 0x6c, 0x4e, 0x36, 0x39, 0x56, 0x63, 0x6e, 0x75,
+ 0x71, 0x33, 0x41, 0x45, 0x44, 0x4a, 0x63, 0x41, 0x41, 0x4c, 0x6f, 0x38,
+ 0x2b, 0x63, 0x65, 0x33, 0x6d, 0x4b, 0x35, 0x77, 0x41, 0x4d, 0x73, 0x7a,
+ 0x61, 0x52, 0x7a, 0x33, 0x44, 0x34, 0x5a, 0x4e, 0x66, 0x65, 0x54, 0x33,
+ 0x61, 0x6b, 0x39, 0x45, 0x78, 0x74, 0x0a, 0x47, 0x62, 0x57, 0x42, 0x4f,
+ 0x6d, 0x32, 0x4f, 0x66, 0x68, 0x49, 0x2b, 0x45, 0x53, 0x6d, 0x32, 0x55,
+ 0x41, 0x32, 0x34, 0x61, 0x49, 0x6a, 0x46, 0x74, 0x4a, 0x52, 0x72, 0x6d,
+ 0x56, 0x4d, 0x65, 0x37, 0x68, 0x44, 0x4b, 0x54, 0x78, 0x68, 0x49, 0x69,
+ 0x54, 0x6a, 0x4c, 0x54, 0x76, 0x56, 0x4e, 0x50, 0x2b, 0x77, 0x53, 0x43,
+ 0x62, 0x32, 0x47, 0x59, 0x34, 0x74, 0x7a, 0x7a, 0x30, 0x66, 0x66, 0x0a,
+ 0x79, 0x62, 0x50, 0x6d, 0x65, 0x5a, 0x50, 0x58, 0x61, 0x32, 0x54, 0x39,
+ 0x77, 0x65, 0x30, 0x68, 0x6a, 0x41, 0x56, 0x53, 0x4b, 0x47, 0x76, 0x76,
+ 0x30, 0x48, 0x41, 0x48, 0x65, 0x4a, 0x72, 0x4f, 0x75, 0x31, 0x75, 0x38,
+ 0x79, 0x4a, 0x4c, 0x6e, 0x58, 0x72, 0x64, 0x47, 0x2f, 0x4f, 0x7a, 0x4b,
+ 0x71, 0x65, 0x4a, 0x38, 0x7a, 0x74, 0x4a, 0x34, 0x47, 0x59, 0x58, 0x7a,
+ 0x70, 0x4b, 0x39, 0x42, 0x0a, 0x68, 0x74, 0x57, 0x6b, 0x6d, 0x7a, 0x38,
+ 0x74, 0x41, 0x78, 0x7a, 0x77, 0x6c, 0x5a, 0x47, 0x6d, 0x54, 0x41, 0x79,
+ 0x58, 0x45, 0x55, 0x50, 0x79, 0x33, 0x42, 0x71, 0x61, 0x4a, 0x66, 0x2b,
+ 0x6c, 0x46, 0x4e, 0x67, 0x61, 0x68, 0x74, 0x57, 0x78, 0x6c, 0x77, 0x58,
+ 0x2f, 0x31, 0x59, 0x4c, 0x35, 0x75, 0x69, 0x69, 0x49, 0x77, 0x4c, 0x69,
+ 0x41, 0x36, 0x67, 0x2f, 0x39, 0x39, 0x79, 0x4e, 0x4c, 0x0a, 0x41, 0x69,
+ 0x34, 0x38, 0x54, 0x6b, 0x41, 0x63, 0x44, 0x58, 0x42, 0x35, 0x38, 0x61,
+ 0x5a, 0x78, 0x36, 0x32, 0x43, 0x63, 0x6b, 0x58, 0x75, 0x4d, 0x6d, 0x32,
+ 0x68, 0x61, 0x37, 0x6e, 0x43, 0x64, 0x77, 0x66, 0x76, 0x31, 0x71, 0x71,
+ 0x6a, 0x72, 0x79, 0x44, 0x56, 0x34, 0x32, 0x2b, 0x56, 0x37, 0x48, 0x75,
+ 0x64, 0x4f, 0x57, 0x36, 0x33, 0x39, 0x6f, 0x6b, 0x6a, 0x69, 0x56, 0x32,
+ 0x33, 0x69, 0x0a, 0x48, 0x79, 0x2f, 0x33, 0x2f, 0x2f, 0x72, 0x56, 0x31,
+ 0x44, 0x59, 0x6a, 0x76, 0x35, 0x49, 0x4c, 0x48, 0x51, 0x79, 0x6d, 0x7a,
+ 0x4a, 0x2b, 0x76, 0x6e, 0x4a, 0x38, 0x4a, 0x69, 0x33, 0x59, 0x74, 0x32,
+ 0x66, 0x44, 0x44, 0x55, 0x58, 0x66, 0x33, 0x41, 0x45, 0x6e, 0x49, 0x32,
+ 0x74, 0x54, 0x75, 0x4c, 0x34, 0x54, 0x42, 0x4b, 0x54, 0x69, 0x74, 0x33,
+ 0x79, 0x79, 0x69, 0x54, 0x56, 0x52, 0x4e, 0x0a, 0x45, 0x59, 0x39, 0x63,
+ 0x4b, 0x47, 0x54, 0x6a, 0x73, 0x41, 0x7a, 0x48, 0x44, 0x7a, 0x35, 0x33,
+ 0x6b, 0x74, 0x6b, 0x38, 0x2b, 0x77, 0x44, 0x73, 0x49, 0x71, 0x76, 0x4d,
+ 0x54, 0x6d, 0x79, 0x33, 0x72, 0x65, 0x6d, 0x36, 0x68, 0x4b, 0x30, 0x71,
+ 0x51, 0x30, 0x48, 0x38, 0x50, 0x4f, 0x77, 0x57, 0x66, 0x31, 0x75, 0x6c,
+ 0x55, 0x71, 0x46, 0x67, 0x37, 0x34, 0x77, 0x67, 0x62, 0x61, 0x32, 0x73,
+ 0x0a, 0x71, 0x37, 0x56, 0x78, 0x61, 0x62, 0x63, 0x32, 0x71, 0x65, 0x4b,
+ 0x71, 0x74, 0x59, 0x4c, 0x59, 0x30, 0x4e, 0x6b, 0x51, 0x71, 0x44, 0x67,
+ 0x73, 0x42, 0x30, 0x55, 0x56, 0x7a, 0x50, 0x70, 0x68, 0x53, 0x56, 0x5a,
+ 0x51, 0x65, 0x49, 0x49, 0x41, 0x4b, 0x6c, 0x6f, 0x7a, 0x6a, 0x49, 0x43,
+ 0x7a, 0x39, 0x75, 0x48, 0x47, 0x39, 0x48, 0x31, 0x36, 0x51, 0x52, 0x45,
+ 0x74, 0x43, 0x4d, 0x78, 0x37, 0x0a, 0x42, 0x46, 0x77, 0x48, 0x74, 0x36,
+ 0x49, 0x31, 0x2b, 0x70, 0x4a, 0x69, 0x50, 0x50, 0x47, 0x69, 0x78, 0x66,
+ 0x42, 0x47, 0x74, 0x72, 0x64, 0x4a, 0x51, 0x78, 0x39, 0x34, 0x62, 0x77,
+ 0x76, 0x43, 0x6f, 0x38, 0x59, 0x4a, 0x71, 0x57, 0x77, 0x67, 0x66, 0x41,
+ 0x77, 0x50, 0x30, 0x57, 0x45, 0x6c, 0x6f, 0x6b, 0x69, 0x6b, 0x53, 0x6a,
+ 0x6a, 0x6b, 0x79, 0x62, 0x33, 0x69, 0x53, 0x48, 0x6c, 0x65, 0x0a, 0x54,
+ 0x52, 0x36, 0x62, 0x7a, 0x48, 0x47, 0x73, 0x69, 0x78, 0x49, 0x38, 0x72,
+ 0x76, 0x44, 0x65, 0x33, 0x74, 0x76, 0x63, 0x79, 0x44, 0x4e, 0x46, 0x2f,
+ 0x78, 0x34, 0x68, 0x70, 0x77, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42, 0x41,
+ 0x6f, 0x49, 0x43, 0x41, 0x47, 0x2b, 0x31, 0x56, 0x55, 0x67, 0x51, 0x4c,
+ 0x2f, 0x62, 0x5a, 0x2f, 0x44, 0x39, 0x6e, 0x53, 0x35, 0x2b, 0x55, 0x4b,
+ 0x51, 0x48, 0x36, 0x0a, 0x4d, 0x70, 0x4a, 0x77, 0x55, 0x38, 0x39, 0x68,
+ 0x35, 0x58, 0x6b, 0x4e, 0x56, 0x51, 0x6f, 0x65, 0x73, 0x4c, 0x41, 0x4d,
+ 0x4f, 0x78, 0x49, 0x77, 0x6c, 0x34, 0x63, 0x75, 0x74, 0x36, 0x6d, 0x56,
+ 0x36, 0x72, 0x45, 0x38, 0x46, 0x42, 0x47, 0x61, 0x6e, 0x4a, 0x73, 0x35,
+ 0x4a, 0x56, 0x69, 0x36, 0x31, 0x61, 0x6d, 0x54, 0x67, 0x78, 0x65, 0x6f,
+ 0x7a, 0x62, 0x66, 0x32, 0x2f, 0x79, 0x65, 0x45, 0x0a, 0x2b, 0x45, 0x7a,
+ 0x7a, 0x78, 0x36, 0x79, 0x6c, 0x51, 0x75, 0x65, 0x73, 0x6d, 0x7a, 0x36,
+ 0x4d, 0x62, 0x4e, 0x6b, 0x6c, 0x63, 0x50, 0x49, 0x74, 0x44, 0x61, 0x53,
+ 0x43, 0x62, 0x4f, 0x52, 0x49, 0x44, 0x4a, 0x46, 0x44, 0x43, 0x64, 0x66,
+ 0x62, 0x58, 0x7a, 0x73, 0x39, 0x69, 0x73, 0x4a, 0x52, 0x54, 0x2f, 0x76,
+ 0x63, 0x58, 0x74, 0x4d, 0x61, 0x65, 0x74, 0x75, 0x4a, 0x5a, 0x37, 0x35,
+ 0x53, 0x0a, 0x70, 0x41, 0x39, 0x33, 0x33, 0x63, 0x73, 0x50, 0x6b, 0x68,
+ 0x72, 0x32, 0x56, 0x57, 0x4c, 0x44, 0x72, 0x45, 0x6a, 0x4a, 0x65, 0x6b,
+ 0x71, 0x49, 0x55, 0x66, 0x61, 0x43, 0x55, 0x67, 0x72, 0x4e, 0x75, 0x5a,
+ 0x76, 0x61, 0x48, 0x4d, 0x36, 0x6a, 0x6f, 0x52, 0x47, 0x67, 0x7a, 0x43,
+ 0x54, 0x51, 0x71, 0x61, 0x5a, 0x73, 0x6f, 0x38, 0x55, 0x2f, 0x30, 0x6e,
+ 0x6c, 0x6c, 0x59, 0x43, 0x6b, 0x64, 0x0a, 0x66, 0x32, 0x5a, 0x4c, 0x37,
+ 0x4b, 0x6b, 0x43, 0x58, 0x30, 0x30, 0x48, 0x5a, 0x4c, 0x4c, 0x33, 0x76,
+ 0x51, 0x67, 0x32, 0x6c, 0x56, 0x2f, 0x70, 0x33, 0x62, 0x75, 0x70, 0x71,
+ 0x66, 0x43, 0x38, 0x32, 0x38, 0x55, 0x5a, 0x71, 0x4c, 0x39, 0x71, 0x38,
+ 0x75, 0x72, 0x6e, 0x30, 0x58, 0x57, 0x45, 0x68, 0x6c, 0x4d, 0x4e, 0x6c,
+ 0x78, 0x36, 0x54, 0x5a, 0x4f, 0x57, 0x6d, 0x4c, 0x6d, 0x35, 0x2f, 0x0a,
+ 0x56, 0x67, 0x58, 0x65, 0x61, 0x77, 0x54, 0x66, 0x53, 0x58, 0x48, 0x6d,
+ 0x65, 0x34, 0x66, 0x48, 0x48, 0x36, 0x56, 0x32, 0x62, 0x6c, 0x67, 0x53,
+ 0x54, 0x75, 0x54, 0x31, 0x44, 0x59, 0x78, 0x38, 0x6e, 0x78, 0x46, 0x76,
+ 0x49, 0x4a, 0x6e, 0x5a, 0x68, 0x38, 0x4d, 0x4f, 0x45, 0x75, 0x39, 0x52,
+ 0x76, 0x49, 0x74, 0x65, 0x6d, 0x66, 0x72, 0x77, 0x2f, 0x31, 0x36, 0x35,
+ 0x65, 0x6c, 0x75, 0x50, 0x0a, 0x56, 0x74, 0x62, 0x6b, 0x2b, 0x79, 0x54,
+ 0x74, 0x44, 0x69, 0x6c, 0x38, 0x32, 0x6f, 0x4e, 0x69, 0x75, 0x68, 0x4b,
+ 0x44, 0x39, 0x38, 0x4a, 0x65, 0x51, 0x69, 0x6c, 0x43, 0x75, 0x42, 0x53,
+ 0x73, 0x4a, 0x4f, 0x78, 0x52, 0x75, 0x64, 0x69, 0x6c, 0x49, 0x4e, 0x53,
+ 0x76, 0x63, 0x74, 0x72, 0x33, 0x65, 0x53, 0x37, 0x63, 0x63, 0x43, 0x6e,
+ 0x6e, 0x75, 0x37, 0x5a, 0x61, 0x32, 0x49, 0x56, 0x45, 0x0a, 0x35, 0x31,
+ 0x41, 0x4c, 0x6b, 0x42, 0x42, 0x61, 0x33, 0x4e, 0x4c, 0x76, 0x49, 0x37,
+ 0x6c, 0x70, 0x6d, 0x78, 0x47, 0x41, 0x65, 0x6d, 0x33, 0x67, 0x52, 0x6b,
+ 0x51, 0x30, 0x74, 0x4e, 0x63, 0x76, 0x65, 0x39, 0x65, 0x51, 0x4b, 0x6e,
+ 0x42, 0x44, 0x71, 0x37, 0x75, 0x45, 0x48, 0x78, 0x35, 0x56, 0x45, 0x79,
+ 0x54, 0x4d, 0x4c, 0x43, 0x49, 0x6d, 0x6d, 0x6d, 0x7a, 0x32, 0x4d, 0x58,
+ 0x62, 0x61, 0x0a, 0x73, 0x71, 0x64, 0x52, 0x44, 0x33, 0x35, 0x4d, 0x57,
+ 0x54, 0x72, 0x77, 0x66, 0x64, 0x36, 0x32, 0x36, 0x72, 0x6b, 0x72, 0x44,
+ 0x6f, 0x6c, 0x39, 0x61, 0x56, 0x46, 0x50, 0x6d, 0x37, 0x6e, 0x2b, 0x6e,
+ 0x43, 0x71, 0x34, 0x6c, 0x73, 0x50, 0x63, 0x72, 0x76, 0x7a, 0x4c, 0x52,
+ 0x4e, 0x63, 0x32, 0x78, 0x31, 0x2b, 0x6a, 0x52, 0x4f, 0x48, 0x64, 0x70,
+ 0x31, 0x58, 0x48, 0x65, 0x56, 0x75, 0x6e, 0x0a, 0x61, 0x67, 0x78, 0x6d,
+ 0x44, 0x31, 0x44, 0x75, 0x50, 0x2f, 0x6c, 0x30, 0x42, 0x68, 0x4f, 0x36,
+ 0x78, 0x55, 0x33, 0x73, 0x74, 0x50, 0x6e, 0x71, 0x33, 0x35, 0x44, 0x4d,
+ 0x4a, 0x76, 0x79, 0x65, 0x30, 0x46, 0x34, 0x75, 0x2f, 0x6a, 0x6c, 0x46,
+ 0x4f, 0x4a, 0x36, 0x45, 0x36, 0x2b, 0x34, 0x7a, 0x53, 0x69, 0x71, 0x45,
+ 0x46, 0x6b, 0x41, 0x4e, 0x57, 0x59, 0x74, 0x67, 0x6c, 0x36, 0x42, 0x47,
+ 0x0a, 0x32, 0x63, 0x36, 0x58, 0x44, 0x72, 0x42, 0x66, 0x2b, 0x36, 0x53,
+ 0x34, 0x30, 0x4f, 0x79, 0x76, 0x50, 0x4c, 0x46, 0x48, 0x39, 0x4b, 0x61,
+ 0x30, 0x63, 0x68, 0x47, 0x58, 0x37, 0x68, 0x59, 0x7a, 0x44, 0x46, 0x2b,
+ 0x4b, 0x2f, 0x47, 0x64, 0x50, 0x73, 0x56, 0x75, 0x55, 0x74, 0x75, 0x56,
+ 0x48, 0x4d, 0x4e, 0x63, 0x6e, 0x33, 0x64, 0x5a, 0x2f, 0x6e, 0x4c, 0x33,
+ 0x5a, 0x4f, 0x4a, 0x6c, 0x47, 0x0a, 0x46, 0x43, 0x33, 0x44, 0x34, 0x41,
+ 0x2f, 0x36, 0x36, 0x62, 0x41, 0x51, 0x72, 0x4e, 0x69, 0x4d, 0x62, 0x48,
+ 0x37, 0x35, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x51, 0x44, 0x61, 0x5a, 0x48,
+ 0x68, 0x4a, 0x49, 0x79, 0x49, 0x4c, 0x34, 0x4d, 0x49, 0x6b, 0x54, 0x4f,
+ 0x6c, 0x31, 0x63, 0x39, 0x4e, 0x6a, 0x62, 0x48, 0x62, 0x75, 0x32, 0x34,
+ 0x41, 0x31, 0x4c, 0x76, 0x37, 0x4e, 0x38, 0x47, 0x52, 0x4f, 0x0a, 0x63,
+ 0x55, 0x6b, 0x34, 0x75, 0x70, 0x6e, 0x68, 0x5a, 0x69, 0x6a, 0x7a, 0x6a,
+ 0x72, 0x38, 0x53, 0x34, 0x49, 0x6b, 0x71, 0x59, 0x49, 0x49, 0x35, 0x77,
+ 0x71, 0x45, 0x30, 0x6f, 0x4a, 0x55, 0x41, 0x75, 0x6c, 0x42, 0x7a, 0x66,
+ 0x6e, 0x39, 0x76, 0x4b, 0x76, 0x32, 0x6e, 0x6d, 0x66, 0x62, 0x39, 0x68,
+ 0x4e, 0x74, 0x35, 0x32, 0x66, 0x73, 0x64, 0x35, 0x49, 0x4c, 0x54, 0x6e,
+ 0x6e, 0x58, 0x73, 0x0a, 0x58, 0x39, 0x47, 0x33, 0x7a, 0x56, 0x2f, 0x36,
+ 0x6c, 0x54, 0x33, 0x4b, 0x6f, 0x53, 0x4d, 0x53, 0x41, 0x46, 0x55, 0x31,
+ 0x68, 0x37, 0x79, 0x69, 0x64, 0x57, 0x6b, 0x75, 0x6c, 0x38, 0x4e, 0x42,
+ 0x56, 0x49, 0x57, 0x6a, 0x48, 0x56, 0x65, 0x55, 0x31, 0x4f, 0x4c, 0x31,
+ 0x34, 0x46, 0x45, 0x54, 0x75, 0x62, 0x62, 0x47, 0x42, 0x6f, 0x2f, 0x2b,
+ 0x73, 0x48, 0x4a, 0x71, 0x31, 0x65, 0x36, 0x56, 0x0a, 0x39, 0x65, 0x34,
+ 0x48, 0x4d, 0x35, 0x67, 0x53, 0x70, 0x38, 0x6c, 0x30, 0x68, 0x55, 0x78,
+ 0x47, 0x45, 0x52, 0x63, 0x65, 0x31, 0x49, 0x74, 0x74, 0x6f, 0x6a, 0x48,
+ 0x6a, 0x39, 0x61, 0x67, 0x4f, 0x37, 0x58, 0x61, 0x62, 0x75, 0x7a, 0x6b,
+ 0x4f, 0x4c, 0x63, 0x4d, 0x65, 0x30, 0x62, 0x43, 0x52, 0x66, 0x64, 0x70,
+ 0x37, 0x52, 0x61, 0x44, 0x62, 0x68, 0x70, 0x4a, 0x6d, 0x49, 0x41, 0x4b,
+ 0x53, 0x0a, 0x50, 0x58, 0x4c, 0x4c, 0x77, 0x38, 0x57, 0x34, 0x65, 0x34,
+ 0x43, 0x6c, 0x35, 0x35, 0x6a, 0x37, 0x59, 0x37, 0x5a, 0x71, 0x48, 0x31,
+ 0x47, 0x32, 0x47, 0x4a, 0x77, 0x4b, 0x58, 0x58, 0x6d, 0x7a, 0x4b, 0x56,
+ 0x48, 0x5a, 0x77, 0x37, 0x72, 0x4f, 0x49, 0x32, 0x7a, 0x49, 0x2f, 0x46,
+ 0x4e, 0x70, 0x47, 0x4b, 0x5a, 0x31, 0x4c, 0x6c, 0x54, 0x74, 0x61, 0x6c,
+ 0x35, 0x62, 0x4b, 0x5a, 0x75, 0x43, 0x0a, 0x59, 0x37, 0x45, 0x47, 0x6f,
+ 0x5a, 0x37, 0x62, 0x71, 0x64, 0x53, 0x63, 0x2f, 0x51, 0x33, 0x71, 0x64,
+ 0x6f, 0x58, 0x55, 0x4d, 0x6e, 0x4a, 0x69, 0x52, 0x4f, 0x4d, 0x30, 0x69,
+ 0x6a, 0x37, 0x51, 0x43, 0x56, 0x56, 0x64, 0x4b, 0x63, 0x36, 0x46, 0x57,
+ 0x39, 0x48, 0x51, 0x54, 0x72, 0x4a, 0x56, 0x41, 0x6f, 0x49, 0x42, 0x41,
+ 0x51, 0x44, 0x49, 0x47, 0x63, 0x7a, 0x43, 0x73, 0x41, 0x4a, 0x2b, 0x0a,
+ 0x55, 0x68, 0x75, 0x7a, 0x42, 0x77, 0x6c, 0x50, 0x4b, 0x32, 0x75, 0x70,
+ 0x4b, 0x4b, 0x35, 0x57, 0x4f, 0x59, 0x43, 0x63, 0x4b, 0x70, 0x72, 0x65,
+ 0x79, 0x6b, 0x6b, 0x65, 0x68, 0x41, 0x70, 0x31, 0x2f, 0x38, 0x49, 0x61,
+ 0x68, 0x70, 0x79, 0x79, 0x46, 0x38, 0x4d, 0x6b, 0x64, 0x37, 0x6d, 0x66,
+ 0x4b, 0x54, 0x5a, 0x49, 0x5a, 0x75, 0x74, 0x74, 0x52, 0x44, 0x31, 0x6c,
+ 0x39, 0x34, 0x30, 0x69, 0x0a, 0x79, 0x44, 0x32, 0x7a, 0x73, 0x63, 0x33,
+ 0x6b, 0x45, 0x49, 0x49, 0x2f, 0x46, 0x41, 0x33, 0x6d, 0x46, 0x41, 0x56,
+ 0x37, 0x4c, 0x70, 0x4e, 0x4e, 0x51, 0x73, 0x46, 0x7a, 0x59, 0x51, 0x6a,
+ 0x48, 0x32, 0x4c, 0x64, 0x69, 0x52, 0x36, 0x6f, 0x2b, 0x4a, 0x43, 0x6a,
+ 0x4f, 0x34, 0x75, 0x77, 0x4b, 0x78, 0x75, 0x50, 0x5a, 0x51, 0x43, 0x74,
+ 0x6f, 0x50, 0x59, 0x48, 0x6d, 0x48, 0x72, 0x64, 0x6c, 0x0a, 0x30, 0x43,
+ 0x4c, 0x47, 0x6e, 0x48, 0x4f, 0x37, 0x59, 0x38, 0x31, 0x67, 0x33, 0x69,
+ 0x35, 0x6f, 0x76, 0x54, 0x63, 0x37, 0x63, 0x61, 0x4f, 0x45, 0x73, 0x6f,
+ 0x32, 0x6a, 0x67, 0x37, 0x4a, 0x4c, 0x67, 0x49, 0x41, 0x4a, 0x48, 0x55,
+ 0x31, 0x71, 0x31, 0x62, 0x4d, 0x75, 0x34, 0x6f, 0x33, 0x56, 0x76, 0x6a,
+ 0x63, 0x43, 0x37, 0x41, 0x57, 0x47, 0x30, 0x4d, 0x56, 0x30, 0x50, 0x30,
+ 0x64, 0x41, 0x0a, 0x31, 0x7a, 0x46, 0x63, 0x4e, 0x76, 0x6f, 0x6c, 0x47,
+ 0x4f, 0x32, 0x75, 0x67, 0x63, 0x34, 0x36, 0x48, 0x63, 0x57, 0x37, 0x44,
+ 0x6c, 0x37, 0x4e, 0x6b, 0x43, 0x76, 0x63, 0x48, 0x7a, 0x59, 0x4c, 0x6b,
+ 0x68, 0x35, 0x6e, 0x44, 0x48, 0x72, 0x6b, 0x49, 0x69, 0x58, 0x61, 0x6d,
+ 0x32, 0x52, 0x48, 0x77, 0x65, 0x75, 0x79, 0x71, 0x72, 0x48, 0x35, 0x4b,
+ 0x2f, 0x2f, 0x74, 0x48, 0x71, 0x7a, 0x77, 0x0a, 0x65, 0x4d, 0x47, 0x35,
+ 0x54, 0x63, 0x65, 0x33, 0x49, 0x4d, 0x69, 0x67, 0x35, 0x46, 0x55, 0x61,
+ 0x66, 0x57, 0x47, 0x33, 0x45, 0x6f, 0x42, 0x47, 0x6c, 0x52, 0x30, 0x36,
+ 0x5a, 0x41, 0x69, 0x7a, 0x34, 0x68, 0x37, 0x52, 0x66, 0x72, 0x7a, 0x50,
+ 0x44, 0x65, 0x7a, 0x6a, 0x37, 0x66, 0x6d, 0x6c, 0x64, 0x6f, 0x72, 0x62,
+ 0x46, 0x45, 0x65, 0x79, 0x42, 0x36, 0x33, 0x69, 0x4b, 0x69, 0x58, 0x6b,
+ 0x0a, 0x70, 0x34, 0x7a, 0x53, 0x78, 0x75, 0x42, 0x49, 0x63, 0x70, 0x67,
+ 0x4c, 0x41, 0x6f, 0x49, 0x42, 0x41, 0x44, 0x69, 0x75, 0x37, 0x78, 0x46,
+ 0x38, 0x6a, 0x75, 0x2b, 0x71, 0x54, 0x48, 0x6d, 0x44, 0x68, 0x4f, 0x79,
+ 0x35, 0x50, 0x56, 0x71, 0x47, 0x34, 0x6d, 0x2b, 0x6f, 0x68, 0x53, 0x52,
+ 0x49, 0x71, 0x46, 0x58, 0x6e, 0x57, 0x51, 0x47, 0x4c, 0x49, 0x63, 0x67,
+ 0x5a, 0x6c, 0x71, 0x73, 0x4d, 0x0a, 0x43, 0x77, 0x44, 0x38, 0x51, 0x64,
+ 0x65, 0x79, 0x63, 0x36, 0x65, 0x4f, 0x47, 0x50, 0x37, 0x49, 0x35, 0x33,
+ 0x4a, 0x7a, 0x59, 0x33, 0x6b, 0x6d, 0x34, 0x6f, 0x36, 0x33, 0x66, 0x48,
+ 0x66, 0x73, 0x48, 0x70, 0x34, 0x4c, 0x74, 0x6a, 0x47, 0x69, 0x39, 0x42,
+ 0x77, 0x79, 0x57, 0x5a, 0x30, 0x75, 0x6e, 0x45, 0x34, 0x30, 0x79, 0x4d,
+ 0x4b, 0x72, 0x4e, 0x42, 0x47, 0x53, 0x75, 0x71, 0x43, 0x64, 0x0a, 0x62,
+ 0x38, 0x5a, 0x53, 0x41, 0x48, 0x70, 0x42, 0x6e, 0x39, 0x77, 0x65, 0x2b,
+ 0x50, 0x54, 0x70, 0x71, 0x48, 0x30, 0x78, 0x59, 0x72, 0x70, 0x6f, 0x4a,
+ 0x36, 0x39, 0x6f, 0x68, 0x44, 0x7a, 0x61, 0x37, 0x48, 0x57, 0x49, 0x33,
+ 0x55, 0x4a, 0x54, 0x5a, 0x33, 0x38, 0x4b, 0x59, 0x51, 0x46, 0x6e, 0x35,
+ 0x71, 0x71, 0x59, 0x45, 0x43, 0x37, 0x59, 0x41, 0x41, 0x6e, 0x61, 0x65,
+ 0x46, 0x51, 0x50, 0x0a, 0x50, 0x4a, 0x69, 0x44, 0x71, 0x49, 0x4a, 0x66,
+ 0x47, 0x54, 0x4d, 0x6c, 0x55, 0x33, 0x4d, 0x48, 0x4d, 0x41, 0x2f, 0x4d,
+ 0x79, 0x4f, 0x76, 0x6d, 0x38, 0x6d, 0x77, 0x46, 0x4d, 0x67, 0x2f, 0x65,
+ 0x44, 0x4e, 0x44, 0x49, 0x2b, 0x42, 0x30, 0x72, 0x48, 0x7a, 0x50, 0x7a,
+ 0x70, 0x49, 0x61, 0x56, 0x37, 0x52, 0x57, 0x59, 0x70, 0x56, 0x49, 0x7a,
+ 0x70, 0x4d, 0x49, 0x43, 0x64, 0x43, 0x55, 0x6e, 0x0a, 0x32, 0x51, 0x49,
+ 0x32, 0x6c, 0x46, 0x78, 0x62, 0x53, 0x78, 0x4e, 0x4d, 0x51, 0x62, 0x63,
+ 0x54, 0x75, 0x42, 0x78, 0x77, 0x6d, 0x6f, 0x2f, 0x48, 0x33, 0x37, 0x69,
+ 0x33, 0x74, 0x70, 0x71, 0x65, 0x55, 0x7a, 0x50, 0x76, 0x57, 0x65, 0x37,
+ 0x6a, 0x4f, 0x51, 0x45, 0x64, 0x48, 0x32, 0x6e, 0x6e, 0x75, 0x38, 0x6e,
+ 0x4a, 0x69, 0x4e, 0x56, 0x55, 0x37, 0x72, 0x4f, 0x62, 0x36, 0x31, 0x46,
+ 0x30, 0x0a, 0x53, 0x50, 0x7a, 0x65, 0x4b, 0x57, 0x68, 0x37, 0x6a, 0x73,
+ 0x79, 0x2b, 0x73, 0x7a, 0x57, 0x53, 0x54, 0x36, 0x35, 0x70, 0x57, 0x31,
+ 0x67, 0x2f, 0x73, 0x2b, 0x70, 0x55, 0x57, 0x59, 0x66, 0x2f, 0x68, 0x76,
+ 0x75, 0x63, 0x45, 0x57, 0x6b, 0x43, 0x67, 0x67, 0x45, 0x41, 0x65, 0x46,
+ 0x6a, 0x4d, 0x42, 0x65, 0x76, 0x47, 0x46, 0x43, 0x4e, 0x64, 0x39, 0x58,
+ 0x61, 0x74, 0x36, 0x71, 0x65, 0x36, 0x0a, 0x77, 0x4b, 0x70, 0x75, 0x37,
+ 0x2f, 0x7a, 0x31, 0x6c, 0x50, 0x63, 0x71, 0x33, 0x67, 0x50, 0x62, 0x70,
+ 0x6a, 0x62, 0x54, 0x38, 0x39, 0x51, 0x32, 0x38, 0x61, 0x30, 0x30, 0x59,
+ 0x51, 0x68, 0x5a, 0x6e, 0x58, 0x31, 0x45, 0x62, 0x71, 0x31, 0x69, 0x73,
+ 0x48, 0x6a, 0x31, 0x37, 0x32, 0x6d, 0x7a, 0x59, 0x37, 0x68, 0x58, 0x63,
+ 0x69, 0x76, 0x73, 0x73, 0x44, 0x36, 0x6f, 0x44, 0x68, 0x71, 0x2f, 0x0a,
+ 0x75, 0x79, 0x42, 0x63, 0x6d, 0x35, 0x77, 0x2f, 0x44, 0x36, 0x38, 0x62,
+ 0x65, 0x4d, 0x52, 0x46, 0x68, 0x52, 0x63, 0x2f, 0x4b, 0x4c, 0x4c, 0x32,
+ 0x32, 0x47, 0x30, 0x78, 0x76, 0x74, 0x34, 0x51, 0x6a, 0x52, 0x31, 0x39,
+ 0x79, 0x5a, 0x32, 0x46, 0x50, 0x41, 0x79, 0x55, 0x44, 0x73, 0x57, 0x76,
+ 0x63, 0x71, 0x2f, 0x57, 0x72, 0x61, 0x31, 0x59, 0x76, 0x51, 0x73, 0x72,
+ 0x72, 0x2f, 0x42, 0x2b, 0x0a, 0x66, 0x56, 0x77, 0x6a, 0x6e, 0x57, 0x72,
+ 0x76, 0x35, 0x52, 0x69, 0x62, 0x75, 0x42, 0x75, 0x4c, 0x68, 0x47, 0x53,
+ 0x59, 0x76, 0x30, 0x41, 0x78, 0x77, 0x55, 0x6d, 0x57, 0x58, 0x6b, 0x4c,
+ 0x59, 0x32, 0x63, 0x48, 0x34, 0x66, 0x43, 0x31, 0x43, 0x2b, 0x4d, 0x62,
+ 0x72, 0x4c, 0x41, 0x49, 0x30, 0x50, 0x34, 0x34, 0x76, 0x56, 0x4a, 0x67,
+ 0x59, 0x58, 0x39, 0x58, 0x51, 0x37, 0x4b, 0x37, 0x70, 0x0a, 0x68, 0x4b,
+ 0x4d, 0x64, 0x58, 0x57, 0x61, 0x36, 0x6e, 0x5a, 0x34, 0x75, 0x39, 0x6f,
+ 0x4e, 0x58, 0x58, 0x62, 0x53, 0x48, 0x31, 0x4c, 0x32, 0x30, 0x2b, 0x31,
+ 0x56, 0x4f, 0x4e, 0x42, 0x63, 0x31, 0x6e, 0x52, 0x30, 0x49, 0x57, 0x77,
+ 0x41, 0x4f, 0x75, 0x67, 0x35, 0x66, 0x71, 0x2f, 0x55, 0x43, 0x6e, 0x36,
+ 0x4a, 0x72, 0x63, 0x4e, 0x57, 0x76, 0x37, 0x62, 0x73, 0x52, 0x2b, 0x74,
+ 0x6f, 0x45, 0x0a, 0x30, 0x48, 0x2b, 0x44, 0x6a, 0x38, 0x4d, 0x4a, 0x47,
+ 0x67, 0x70, 0x4f, 0x6a, 0x43, 0x54, 0x79, 0x78, 0x30, 0x4f, 0x53, 0x32,
+ 0x46, 0x32, 0x50, 0x47, 0x34, 0x43, 0x57, 0x48, 0x68, 0x48, 0x4a, 0x2f,
+ 0x2b, 0x77, 0x49, 0x30, 0x62, 0x4e, 0x53, 0x49, 0x43, 0x45, 0x36, 0x58,
+ 0x61, 0x43, 0x77, 0x75, 0x2b, 0x32, 0x4e, 0x6a, 0x4e, 0x41, 0x59, 0x71,
+ 0x6b, 0x78, 0x69, 0x61, 0x44, 0x42, 0x44, 0x0a, 0x73, 0x51, 0x4b, 0x43,
+ 0x41, 0x51, 0x45, 0x41, 0x72, 0x54, 0x66, 0x56, 0x52, 0x74, 0x43, 0x71,
+ 0x71, 0x72, 0x72, 0x32, 0x49, 0x45, 0x4b, 0x41, 0x59, 0x63, 0x7a, 0x2b,
+ 0x4c, 0x4a, 0x4f, 0x71, 0x66, 0x4b, 0x37, 0x77, 0x4f, 0x6f, 0x66, 0x49,
+ 0x54, 0x7a, 0x73, 0x45, 0x47, 0x76, 0x57, 0x6c, 0x75, 0x47, 0x73, 0x72,
+ 0x6e, 0x54, 0x33, 0x56, 0x55, 0x71, 0x6c, 0x41, 0x30, 0x2b, 0x45, 0x37,
+ 0x0a, 0x33, 0x4a, 0x45, 0x41, 0x56, 0x41, 0x33, 0x6c, 0x65, 0x76, 0x32,
+ 0x51, 0x74, 0x72, 0x61, 0x33, 0x47, 0x30, 0x67, 0x56, 0x77, 0x6f, 0x4f,
+ 0x6e, 0x63, 0x55, 0x33, 0x50, 0x4f, 0x2b, 0x4c, 0x73, 0x61, 0x56, 0x67,
+ 0x6a, 0x33, 0x52, 0x43, 0x2b, 0x31, 0x4c, 0x75, 0x68, 0x67, 0x35, 0x68,
+ 0x68, 0x68, 0x52, 0x4b, 0x34, 0x4b, 0x6a, 0x4d, 0x39, 0x41, 0x38, 0x76,
+ 0x78, 0x62, 0x2b, 0x6b, 0x76, 0x0a, 0x4c, 0x72, 0x53, 0x50, 0x46, 0x56,
+ 0x62, 0x4f, 0x35, 0x49, 0x6d, 0x64, 0x4f, 0x6c, 0x33, 0x6f, 0x4c, 0x6f,
+ 0x67, 0x34, 0x43, 0x2b, 0x69, 0x63, 0x79, 0x44, 0x72, 0x39, 0x79, 0x76,
+ 0x67, 0x4c, 0x4d, 0x78, 0x67, 0x66, 0x6a, 0x44, 0x55, 0x38, 0x2f, 0x6d,
+ 0x47, 0x5a, 0x2f, 0x62, 0x42, 0x6d, 0x47, 0x6f, 0x38, 0x79, 0x55, 0x41,
+ 0x75, 0x61, 0x68, 0x39, 0x4e, 0x79, 0x4f, 0x2f, 0x63, 0x76, 0x0a, 0x65,
+ 0x6d, 0x4e, 0x49, 0x65, 0x6f, 0x42, 0x6b, 0x33, 0x44, 0x6d, 0x45, 0x34,
+ 0x7a, 0x55, 0x35, 0x6b, 0x76, 0x38, 0x43, 0x41, 0x41, 0x4e, 0x55, 0x65,
+ 0x31, 0x75, 0x56, 0x6b, 0x4a, 0x2f, 0x74, 0x78, 0x6d, 0x58, 0x31, 0x70,
+ 0x56, 0x64, 0x6d, 0x56, 0x61, 0x36, 0x70, 0x38, 0x42, 0x61, 0x41, 0x79,
+ 0x51, 0x31, 0x4b, 0x61, 0x71, 0x6e, 0x64, 0x46, 0x43, 0x61, 0x53, 0x4d,
+ 0x79, 0x53, 0x56, 0x0a, 0x66, 0x46, 0x77, 0x33, 0x44, 0x42, 0x61, 0x2b,
+ 0x39, 0x52, 0x36, 0x69, 0x2f, 0x58, 0x46, 0x4b, 0x36, 0x64, 0x41, 0x67,
+ 0x70, 0x70, 0x63, 0x47, 0x53, 0x4d, 0x33, 0x36, 0x41, 0x62, 0x42, 0x73,
+ 0x78, 0x36, 0x6c, 0x6f, 0x70, 0x4f, 0x61, 0x62, 0x61, 0x41, 0x41, 0x6f,
+ 0x73, 0x71, 0x4f, 0x63, 0x66, 0x73, 0x58, 0x6b, 0x62, 0x6b, 0x68, 0x4c,
+ 0x76, 0x67, 0x75, 0x69, 0x41, 0x45, 0x77, 0x6f, 0x0a, 0x47, 0x38, 0x33,
+ 0x66, 0x39, 0x50, 0x62, 0x41, 0x56, 0x38, 0x66, 0x72, 0x6e, 0x6f, 0x54,
+ 0x62, 0x51, 0x36, 0x51, 0x6c, 0x47, 0x45, 0x6f, 0x4f, 0x67, 0x48, 0x53,
+ 0x56, 0x73, 0x77, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45,
+ 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a
+};
+
+#endif /* _TLS_TEST_CERT_H */
#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;
INSIST(off + len <= buflen);
v = 0;
- for (pp = buf + off; pp < end; p++) {
+ for (pp = buf + off; pp < end; pp++) {
v *= 10;
v += *pp - '0';
isc_nm_closedown
isc_nm_destroy
isc_nm_detach
-isc_nm_listenhttps
+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
include/isccfg/aclconf.h \
include/isccfg/cfg.h \
include/isccfg/grammar.h \
+ include/isccfg/httpconf.h \
include/isccfg/kaspconf.h \
include/isccfg/log.h \
- include/isccfg/namedconf.h
+ include/isccfg/namedconf.h \
+ include/isccfg/tlsconf.h
libisccfg_la_SOURCES = \
$(libisccfg_la_HEADERS) \
aclconf.c \
+ httpconf.c \
dnsconf.c \
kaspconf.c \
log.c \
namedconf.c \
+ tlsconf.c \
parser.c
libisccfg_la_CPPFLAGS = \
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/util.h>
+
+#include <isccfg/grammar.h>
+#include <isccfg/httpconf.h>
+
+void
+cfg_http_storage_init(isc_mem_t *mctx, isc_cfg_http_storage_t *storage) {
+ REQUIRE(mctx != NULL);
+ REQUIRE(storage != NULL);
+
+ memset(storage, 0, sizeof(*storage));
+ isc_mem_attach(mctx, &storage->mctx);
+ ISC_LIST_INIT(storage->list);
+}
+
+void
+cfg_http_storage_uninit(isc_cfg_http_storage_t *storage) {
+ REQUIRE(storage != NULL);
+
+ cfg_http_storage_clear(storage);
+ isc_mem_detach(&storage->mctx);
+}
+
+void
+cfg_http_storage_clear(isc_cfg_http_storage_t *storage) {
+ REQUIRE(storage != NULL);
+
+ isc_mem_t *mctx = storage->mctx;
+
+ if (!ISC_LIST_EMPTY(storage->list)) {
+ isc_cfg_http_obj_t *http = ISC_LIST_HEAD(storage->list);
+ while (http != NULL) {
+ isc_cfg_http_obj_t *next = ISC_LIST_NEXT(http, link);
+ ISC_LIST_DEQUEUE(storage->list, http, link);
+ storage->nservers--;
+
+ isc_mem_free(mctx, http->name);
+
+ if (!ISC_LIST_EMPTY(http->endpoints)) {
+ isc_cfg_http_endpoint_t *ep =
+ ISC_LIST_HEAD(http->endpoints);
+ while (ep != NULL) {
+ isc_cfg_http_endpoint_t *epnext =
+ ISC_LIST_NEXT(ep, link);
+ isc_mem_free(mctx, ep->path);
+ isc_mem_put(mctx, ep, sizeof(*ep));
+ ep = epnext;
+ http->nendponts--;
+ }
+ }
+
+ isc_mem_put(mctx, http, sizeof(*http));
+ http = next;
+ }
+ }
+
+ INSIST(storage->nservers == 0);
+}
+
+isc_cfg_http_obj_t *
+cfg_http_find(const char *name, isc_cfg_http_storage_t *storage) {
+ isc_cfg_http_obj_t *http = NULL;
+ REQUIRE(name != NULL && *name != '\0');
+ REQUIRE(storage != NULL);
+
+ for (http = ISC_LIST_HEAD(storage->list); http != NULL;
+ http = ISC_LIST_NEXT(http, link))
+ {
+ if (strcasecmp(name, http->name) == 0) {
+ break;
+ }
+ }
+
+ return (http);
+}
+
+static isc_result_t
+push_http_obj(const cfg_obj_t *map, isc_cfg_http_storage_t *storage) {
+ isc_mem_t *mctx = storage->mctx;
+ isc_cfg_http_obj_t *new;
+ const cfg_obj_t *endpoints = NULL;
+ const cfg_listelt_t *elt;
+
+ if (!cfg_obj_ismap(map) || map->value.map.id == NULL ||
+ !cfg_obj_isstring(map->value.map.id))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (cfg_http_find(cfg_obj_asstring(map->value.map.id), storage) != NULL)
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (cfg_map_get(map, "endpoints", &endpoints) != ISC_R_SUCCESS ||
+ !cfg_obj_islist(endpoints))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ INSIST(endpoints != NULL);
+
+ new = isc_mem_get(mctx, sizeof(*new));
+ memset(new, 0, sizeof(*new));
+ ISC_LIST_INIT(new->endpoints);
+ new->name = isc_mem_strdup(mctx, cfg_obj_asstring(map->value.map.id));
+
+ for (elt = cfg_list_first(endpoints); elt != NULL;
+ elt = cfg_list_next(elt)) {
+ isc_cfg_http_endpoint_t *newep = NULL;
+ const cfg_obj_t *endp = cfg_listelt_value(elt);
+ newep = isc_mem_get(mctx, sizeof(*newep));
+ ISC_LINK_INIT(newep, link);
+ newep->path = isc_mem_strdup(mctx, cfg_obj_asstring(endp));
+
+ ISC_LIST_PREPEND(new->endpoints, newep, link);
+ new->nendponts++;
+ }
+
+ ISC_LINK_INIT(new, link);
+ ISC_LIST_PREPEND(storage->list, new, link);
+ storage->nservers++;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+cfg_http_storage_load(const cfg_obj_t *cfg_ctx,
+ isc_cfg_http_storage_t *storage) {
+ bool found = false;
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_obj_t *http = NULL;
+ const cfg_listelt_t *elt;
+ const cfg_obj_t *map = NULL;
+
+ REQUIRE(cfg_ctx != NULL);
+ REQUIRE(storage != NULL);
+
+ cfg_http_storage_clear(storage);
+ result = cfg_map_get(cfg_ctx, "http", &http);
+ if (result != ISC_R_SUCCESS) {
+ /* No statements found, but it is fine. */
+ return (ISC_R_SUCCESS);
+ }
+
+ INSIST(http != NULL);
+
+ for (elt = cfg_list_first(http); elt != NULL; elt = cfg_list_next(elt))
+ {
+ map = cfg_listelt_value(elt);
+ INSIST(map != NULL);
+ found = true;
+ result = push_http_obj(map, storage);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ }
+
+ if (found == true && storage->nservers == 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef ISCCFG_HTTPCONF_H
+#define ISCCFG_HTTPCONF_H 1
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/list.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/types.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/tlsconf.h>
+
+typedef struct isc_cfg_http_endpoint {
+ char *path;
+ LINK(struct isc_cfg_http_endpoint) link;
+} isc_cfg_http_endpoint_t;
+
+typedef struct isc_cfg_http_obj {
+ char *name;
+ LINK(struct isc_cfg_http_obj) link;
+ ISC_LIST(isc_cfg_http_endpoint_t) endpoints;
+ size_t nendponts;
+} isc_cfg_http_obj_t;
+
+typedef struct isc_cfg_http_storage {
+ isc_mem_t *mctx;
+ ISC_LIST(isc_cfg_http_obj_t) list;
+ size_t nservers;
+} isc_cfg_http_storage_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+void
+cfg_http_storage_init(isc_mem_t *mctx, isc_cfg_http_storage_t *storage);
+
+void
+cfg_http_storage_uninit(isc_cfg_http_storage_t *storage);
+
+isc_result_t
+cfg_http_storage_load(const cfg_obj_t * cfg_ctx,
+ isc_cfg_http_storage_t *storage);
+
+isc_cfg_http_obj_t *
+cfg_http_find(const char *name, isc_cfg_http_storage_t *storage);
+
+void
+cfg_http_storage_clear(isc_cfg_http_storage_t *storage);
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISCCFG_HTTPCONF_H */
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef ISCCFG_TLSCONF_H
+#define ISCCFG_TLSCONF_H 1
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/list.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/types.h>
+
+#include <isccfg/cfg.h>
+
+typedef struct isc_cfg_tls_obj {
+ char *name;
+ char *key_file;
+ char *cert_file;
+ char *dh_param;
+ char *protocols;
+ char *ciphers;
+ LINK(struct isc_cfg_tls_obj) link;
+} isc_cfg_tls_obj_t;
+
+typedef struct isc_cfg_tls_data_storage {
+ isc_mem_t *mctx;
+ size_t count;
+ ISC_LIST(isc_cfg_tls_obj_t) list;
+} isc_cfg_tls_data_storage_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+void
+cfg_tls_storage_init(isc_mem_t *mctx, isc_cfg_tls_data_storage_t *storage);
+
+void
+cfg_tls_storage_uninit(isc_cfg_tls_data_storage_t *storage);
+
+isc_result_t
+cfg_tls_storage_load(const cfg_obj_t * cfg_ctx,
+ isc_cfg_tls_data_storage_t *storage);
+
+isc_cfg_tls_obj_t *
+cfg_tls_storage_find(const char *name, isc_cfg_tls_data_storage_t *storage);
+/*
+ * Looks for TLS key/certificate pair.
+ */
+
+void
+cfg_tls_storage_clear(isc_cfg_tls_data_storage_t *storage);
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISCCFG_TLSCONF_H */
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;
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_secure_endpoint;
-static cfg_type_t cfg_type_http_secure_server;
+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;
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;
{ "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 }
};
{ "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 },
{ "host-statistics", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "host-statistics-max", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "hostname", &cfg_type_qstringornone, 0 },
- { "https-server", &cfg_type_http_secure_server, 0 },
{ "interface-interval", &cfg_type_duration, 0 },
{ "keep-response-order", &cfg_type_bracketed_aml, 0 },
{ "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
{ "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 },
{ "filter-aaaa-on-v4", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
{ "filter-aaaa-on-v6", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
{ "glue-cache", &cfg_type_boolean, CFG_CLAUSEFLAG_DEPRECATED },
- { "https-endpoint", &cfg_type_http_secure_endpoint, 0 },
{ "ipv4only-enable", &cfg_type_boolean, 0 },
{ "ipv4only-contact", &cfg_type_astring, 0 },
{ "ipv4only-server", &cfg_type_astring, 0 },
"tlsoptional", parse_optional_keyvalue, print_keyvalue,
doc_optional_keyvalue, &cfg_rep_string, &tls_kw
};
-static cfg_type_t cfg_type_tls = { "tls", parse_keyvalue,
- print_keyvalue, doc_keyvalue,
- &cfg_rep_string, &tls_kw };
-static keyword_type_t servername_kw = { "https-server", &cfg_type_astring };
-static cfg_type_t cfg_type_servername = { "servername", parse_keyvalue,
- print_keyvalue, doc_keyvalue,
- &cfg_rep_string, &servername_kw };
+/* http and https */
-/* http-endpoint */
-static cfg_tuplefielddef_t endpoint_fields[] = {
- { "path", &cfg_type_qstring, 0 },
- { "servername", &cfg_type_servername, 0 },
+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,
+ CFG_CLAUSEFLAG_EXPERIMENTAL },
{ NULL, NULL, 0 }
};
-static cfg_type_t cfg_type_http_secure_endpoint = {
- "endpoint", cfg_parse_tuple, cfg_print_tuple,
- cfg_doc_tuple, &cfg_rep_tuple, endpoint_fields
+
+static cfg_clausedef_t *http_description_clausesets[] = {
+ cfg_http_description_clauses, NULL
};
-/* http-server */
-static cfg_tuplefielddef_t server_fields[] = {
- { "name", &cfg_type_astring, 0 },
- { "port", &cfg_type_optional_port, 0 },
- { "tls", &cfg_type_tls, 0 },
- { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
- { NULL, NULL, 0 }
+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 cfg_type_t cfg_type_http_secure_server = {
- "server", cfg_parse_tuple, cfg_print_tuple,
- cfg_doc_tuple, &cfg_rep_tuple, server_fields
+
+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
};
--- /dev/null
+/*
+ * 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 <string.h>
+
+#include <isc/util.h>
+
+#include <isccfg/grammar.h>
+#include <isccfg/tlsconf.h>
+
+void
+cfg_tls_storage_init(isc_mem_t *mctx, isc_cfg_tls_data_storage_t *storage) {
+ REQUIRE(mctx != NULL);
+ REQUIRE(storage != NULL);
+
+ memset(storage, 0, sizeof(*storage));
+ isc_mem_attach(mctx, &storage->mctx);
+ ISC_LIST_INIT(storage->list);
+}
+
+void
+cfg_tls_storage_uninit(isc_cfg_tls_data_storage_t *storage) {
+ REQUIRE(storage != NULL);
+
+ cfg_tls_storage_clear(storage);
+ isc_mem_detach(&storage->mctx);
+}
+
+void
+cfg_tls_storage_clear(isc_cfg_tls_data_storage_t *storage) {
+ REQUIRE(storage != NULL);
+
+ isc_mem_t *mctx = storage->mctx;
+
+ if (!ISC_LIST_EMPTY(storage->list)) {
+ isc_cfg_tls_obj_t *tls_obj = ISC_LIST_HEAD(storage->list);
+ while (tls_obj != NULL) {
+ isc_cfg_tls_obj_t *next = ISC_LIST_NEXT(tls_obj, link);
+ ISC_LIST_DEQUEUE(storage->list, tls_obj, link);
+ storage->count--;
+
+ isc_mem_free(mctx, tls_obj->name);
+ isc_mem_free(mctx, tls_obj->key_file);
+ isc_mem_free(mctx, tls_obj->cert_file);
+
+ if (tls_obj->dh_param != NULL) {
+ isc_mem_free(mctx, tls_obj->dh_param);
+ }
+
+ if (tls_obj->protocols != NULL) {
+ isc_mem_free(mctx, tls_obj->protocols);
+ }
+
+ if (tls_obj->ciphers != NULL) {
+ isc_mem_free(mctx, tls_obj->ciphers);
+ }
+
+ isc_mem_put(mctx, tls_obj, sizeof(*tls_obj));
+ tls_obj = next;
+ }
+ }
+
+ INSIST(storage->count == 0);
+}
+
+static isc_result_t
+push_tls_obj(const cfg_obj_t *map, isc_cfg_tls_data_storage_t *storage) {
+ isc_mem_t *mctx = storage->mctx;
+ isc_cfg_tls_obj_t *new;
+ const cfg_obj_t *key_file = NULL, *cert_file = NULL, *dh_param = NULL,
+ *protocols = NULL, *ciphers = NULL;
+
+ if (!cfg_obj_ismap(map) || map->value.map.id == NULL ||
+ !cfg_obj_isstring(map->value.map.id))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (cfg_tls_storage_find(cfg_obj_asstring(map->value.map.id),
+ storage) != NULL) {
+ return (ISC_R_FAILURE);
+ }
+
+ if (cfg_map_get(map, "key-file", &key_file) != ISC_R_SUCCESS ||
+ !cfg_obj_isstring(key_file))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (cfg_map_get(map, "cert-file", &cert_file) != ISC_R_SUCCESS) {
+ return (ISC_R_FAILURE);
+ }
+
+ (void)cfg_map_get(map, "dh-param", &dh_param);
+ (void)cfg_map_get(map, "protocols", &protocols);
+ (void)cfg_map_get(map, "ciphers", &ciphers);
+
+ INSIST(key_file != NULL);
+ INSIST(cert_file != NULL);
+
+ new = isc_mem_get(mctx, sizeof(*new));
+ memset(new, 0, sizeof(*new));
+ new->name = isc_mem_strdup(mctx, cfg_obj_asstring(map->value.map.id));
+ new->key_file = isc_mem_strdup(mctx, cfg_obj_asstring(key_file));
+ new->cert_file = isc_mem_strdup(mctx, cfg_obj_asstring(cert_file));
+
+ if (dh_param != NULL && cfg_obj_isstring(dh_param)) {
+ new->dh_param = isc_mem_strdup(mctx,
+ cfg_obj_asstring(dh_param));
+ }
+
+ if (protocols != NULL && cfg_obj_isstring(protocols)) {
+ new->protocols = isc_mem_strdup(mctx,
+ cfg_obj_asstring(protocols));
+ }
+
+ if (ciphers != NULL && cfg_obj_isstring(ciphers)) {
+ new->ciphers = isc_mem_strdup(mctx, cfg_obj_asstring(ciphers));
+ }
+
+ ISC_LINK_INIT(new, link);
+ ISC_LIST_PREPEND(storage->list, new, link);
+ storage->count++;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+cfg_tls_storage_load(const cfg_obj_t *cfg_ctx,
+ isc_cfg_tls_data_storage_t *storage) {
+ isc_result_t result = ISC_R_SUCCESS;
+ bool found = false;
+ const cfg_obj_t *tls = NULL;
+ const cfg_listelt_t *elt;
+ const cfg_obj_t *map = NULL;
+
+ REQUIRE(cfg_ctx != NULL);
+ REQUIRE(storage != NULL);
+
+ result = cfg_map_get(cfg_ctx, "tls", &tls);
+ if (result != ISC_R_SUCCESS) {
+ /* No tls statements found, but it is fine. */
+ return (ISC_R_SUCCESS);
+ }
+
+ cfg_tls_storage_clear(storage);
+ INSIST(tls != NULL);
+
+ for (elt = cfg_list_first(tls); elt != NULL; elt = cfg_list_next(elt)) {
+ map = cfg_listelt_value(elt);
+ INSIST(map != NULL);
+ found = true;
+ result = push_tls_obj(map, storage);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ }
+
+ if (found == true && storage->count == 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_cfg_tls_obj_t *
+cfg_tls_storage_find(const char *name, isc_cfg_tls_data_storage_t *storage) {
+ isc_cfg_tls_obj_t *tls_obj = NULL;
+ REQUIRE(storage != NULL);
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ for (tls_obj = ISC_LIST_HEAD(storage->list); tls_obj != NULL;
+ tls_obj = ISC_LIST_NEXT(tls_obj, link))
+ {
+ if (strcasecmp(name, tls_obj->name) == 0) {
+ break;
+ }
+ }
+
+ return tls_obj;
+}
cfg_doc_tuple
cfg_doc_void
cfg_gettoken
+cfg_http_storage_clear
+cfg_http_storage_init
+cfg_http_storage_uninit
+cfg_http_storage_load
+cfg_http_find
cfg_is_enum
cfg_kasp_fromconfig
cfg_list_first
cfg_obj_isvoid
cfg_obj_line
cfg_obj_log
+cfg_tls_storage_clear
+cfg_tls_storage_init
+cfg_tls_storage_uninit
+cfg_tls_storage_load
+cfg_tls_storage_find
cfg_parse_addressed_map
cfg_parse_astring
cfg_parse_boolean
<ClCompile Include="..\dnsconf.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\httpconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\kaspconf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\log.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\tlsconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\namedconf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClInclude Include="..\include\isccfg\grammar.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\include\isccfg\httpconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\include\isccfg\kaspconf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\include\isccfg\log.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\include\isccfg\tlsconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\include\isccfg\namedconf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ItemGroup>
<ClCompile Include="..\aclconf.c" />
<ClCompile Include="..\dnsconf.c" />
+ <ClCompile Include="..\httpconf.c" />
<ClCompile Include="..\kaspconf.c" />
<ClCompile Include="..\log.c" />
<ClCompile Include="..\namedconf.c" />
+ <ClCompile Include="..\tlsconf.c" />
<ClCompile Include="..\parser.c" />
<ClCompile Include="DLLMain.c" />
</ItemGroup>
<ClInclude Include="..\include\isccfg\aclconf.h" />
<ClInclude Include="..\include\isccfg\cfg.h" />
<ClInclude Include="..\include\isccfg\grammar.h" />
+ <ClInclude Include="..\include\isccfg\httpconf.h" />
<ClInclude Include="..\include\isccfg\kaspconf.h" />
<ClInclude Include="..\include\isccfg\log.h" />
<ClInclude Include="..\include\isccfg\namedconf.h" />
+ <ClInclude Include="..\include\isccfg\tlsconf.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\lib\isc\win32\libisc.vcxproj">
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
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;
};
* 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, 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);
/*%<
goto failure;
}
+ ifp->tcplistensocket = NULL;
+ ifp->http_listensocket = NULL;
+ ifp->http_secure_listensocket = NULL;
+
*ifpret = ifp;
return (ISC_R_SUCCESS);
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,
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) {
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);
}
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);
+ if (tls) {
+ 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 = NULL;
- if (tls) {
- result = isc_tlsctx_createserver(key, cert, &elt->sslctx);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
- }
+ 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, 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, key != NULL,
+ 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) {
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));
}
ns_lib_init
ns_lib_shutdown
ns_listenelt_create
+ns_listenelt_create_http
ns_listenelt_destroy
ns_listenlist_attach
ns_listenlist_create
./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
./lib/isccfg/include/isccfg/aclconf.h C 1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2013,2014,2016,2018,2019,2020,2021
./lib/isccfg/include/isccfg/cfg.h C 2000,2001,2002,2004,2005,2006,2007,2010,2013,2014,2015,2016,2018,2019,2020,2021
./lib/isccfg/include/isccfg/grammar.h C 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2013,2014,2015,2016,2017,2018,2019,2020,2021
+./lib/isccfg/include/isccfg/httpconf.h C 2021
./lib/isccfg/include/isccfg/kaspconf.h C 2019,2020,2021
./lib/isccfg/include/isccfg/log.h C 2001,2004,2005,2006,2007,2009,2016,2018,2019,2020,2021
./lib/isccfg/include/isccfg/namedconf.h C 2002,2004,2005,2006,2007,2009,2010,2014,2016,2018,2019,2020,2021
+./lib/isccfg/include/isccfg/tlsconf.h C 2021
+./lib/isccfg/httpconf.c C 2021
./lib/isccfg/kaspconf.c C 2019,2020,2021
./lib/isccfg/log.c C 2001,2004,2005,2006,2007,2016,2018,2019,2020,2021
./lib/isccfg/namedconf.c C 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
+./lib/isccfg/tlsconf.c C 2021
./lib/isccfg/parser.c C 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
./lib/isccfg/tests/duration_test.c C 2019,2020,2021
./lib/isccfg/tests/parser_test.c C 2016,2018,2019,2020,2021