From 6b9a31989cea5bf350f4b26ebef7e88d2cc7c70c Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 25 Jan 2021 17:44:39 +0200 Subject: [PATCH 01/11] Resurrect old TLS code This commit resurrects the old TLS code from 8f73c70d23e26954165fd44ce5617a95f112bcff. It also includes numerous stability fixes and support for isc_nm_cancelread() for the TLS layer. The code was resurrected to be used for DoH. --- lib/isc/Makefile.am | 1 + lib/isc/include/isc/netmgr.h | 11 + lib/isc/netmgr/netmgr-int.h | 106 +++ lib/isc/netmgr/netmgr.c | 49 +- lib/isc/netmgr/tlsstream.c | 936 ++++++++++++++++++++++++ lib/isc/win32/libisc.def.in | 2 + lib/isc/win32/libisc.vcxproj.filters.in | 2 +- lib/isc/win32/libisc.vcxproj.in | 1 + util/copyrights | 1 + 9 files changed, 1106 insertions(+), 3 deletions(-) create mode 100644 lib/isc/netmgr/tlsstream.c diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index c516fd5458..c84832b294 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -129,6 +129,7 @@ libisc_la_SOURCES = \ netmgr/tcp.c \ netmgr/tcpdns.c \ netmgr/tlsdns.c \ + netmgr/tlsstream.c \ netmgr/udp.c \ netmgr/uv-compat.c \ netmgr/uv-compat.h \ diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 455101bafb..0f25e86819 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -473,6 +473,17 @@ isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats); * full range of socket-related stats counter numbers. */ +isc_result_t +isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface, + isc_nm_accept_cb_t accept_cb, void *accept_cbarg, + size_t extrahandlesize, int backlog, isc_quota_t *quota, + isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp); + +isc_result_t +isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, + isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx, + unsigned int timeout, size_t extrahandlesize); + isc_result_t isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, isc_nm_cb_t cb, void *cbarg, unsigned int timeout, diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 0d7bdae422..d3c026cf0c 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -252,6 +252,13 @@ typedef enum isc__netievent_type { netievent_tcpdnsclose, netievent_tcpdnsstop, + netievent_tlsclose, + netievent_tlssend, + netievent_tlsstartread, + netievent_tlsconnect, + netievent_tlsdobio, + netievent_tlscancel, + netievent_tlsdnsaccept, netievent_tlsdnsconnect, netievent_tlsdnssend, @@ -642,6 +649,8 @@ typedef enum isc_nmsocket_type { isc_nm_tcplistener, isc_nm_tcpdnslistener, isc_nm_tcpdnssocket, + isc_nm_tlslistener, + isc_nm_tlssocket, isc_nm_tlsdnslistener, isc_nm_tlsdnssocket } isc_nmsocket_type; @@ -670,6 +679,11 @@ enum { STATID_ACTIVE = 10 }; +typedef struct isc_nmsocket_tls_send_req { + isc_nmsocket_t *tlssock; + isc_region_t data; +} isc_nmsocket_tls_send_req_t; + struct isc_nmsocket { /*% Unlocked, RO */ int magic; @@ -705,6 +719,27 @@ struct isc_nmsocket { isc__nm_uvreq_t *pending_req; } tls; + /*% TLS stuff */ + struct tlsstream { + bool server; + BIO *app_bio; + SSL *ssl; + SSL_CTX *ctx; + BIO *ssl_bio; + isc_nmsocket_t *tlslistener; + enum { + TLS_INIT, + TLS_HANDSHAKE, + TLS_IO, + TLS_ERROR, + TLS_CLOSING, + TLS_CLOSED + } state; + size_t nsending; + /* List of active send requests. */ + ISC_LIST(isc__nm_uvreq_t) sends; + } tlsstream; + /*% * quota is the TCP client, attached when a TCP connection * is established. pquota is a non-attached pointer to the @@ -1220,6 +1255,28 @@ isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0); * stoplisten, send, read, pause, close). */ +void +isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0); + +/*%< + * Callback handlers for asynchronouse TLS events. + */ + void isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0); void @@ -1291,6 +1348,10 @@ isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0); void isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, void *cbarg); + +void +isc__nm_tls_cancelread(isc_nmhandle_t *handle); + /*%< * Back-end implementation of isc_nm_send() for TLSDNS handles. */ @@ -1344,6 +1405,38 @@ isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle); * Stop reading on a connected TLSDNS handle. */ +void +isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg); + +void +isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg); + +void +isc__nm_tls_close(isc_nmsocket_t *sock); +/*%< + * Close a TLS socket. + */ + +void +isc__nm_tls_pauseread(isc_nmhandle_t *handle); +/*%< + * Pause reading on this handle, while still remembering the callback. + */ + +void +isc__nm_tls_resumeread(isc_nmhandle_t *handle); +/*%< + * Resume reading from the handle. + * + */ + +void +isc__nm_tls_cleanup_data(isc_nmsocket_t *sock); + +void +isc__nm_tls_stoplistening(isc_nmsocket_t *sock); + #define isc__nm_uverr2result(x) \ isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__) isc_result_t @@ -1444,6 +1537,12 @@ NETIEVENT_SOCKET_TYPE(tcpclose); NETIEVENT_SOCKET_TYPE(tcplisten); NETIEVENT_SOCKET_TYPE(tcppauseread); NETIEVENT_SOCKET_TYPE(tcpstop); +NETIEVENT_SOCKET_TYPE(tlsclose); +/* NETIEVENT_SOCKET_TYPE(tlsconnect); */ /* unique type, defined independently + */ +NETIEVENT_SOCKET_TYPE(tlsdobio); +NETIEVENT_SOCKET_TYPE(tlsstartread); +NETIEVENT_SOCKET_HANDLE_TYPE(tlscancel); NETIEVENT_SOCKET_TYPE(udpclose); NETIEVENT_SOCKET_TYPE(udplisten); NETIEVENT_SOCKET_TYPE(udpread); @@ -1473,6 +1572,7 @@ NETIEVENT_SOCKET_TYPE(tlsdnscycle); NETIEVENT_SOCKET_REQ_TYPE(tcpconnect); NETIEVENT_SOCKET_REQ_TYPE(tcpsend); NETIEVENT_SOCKET_TYPE(tcpstartread); +NETIEVENT_SOCKET_REQ_TYPE(tlssend); NETIEVENT_SOCKET_REQ_TYPE(udpconnect); NETIEVENT_SOCKET_REQ_RESULT_TYPE(connectcb); @@ -1498,6 +1598,11 @@ NETIEVENT_SOCKET_DECL(tcplisten); NETIEVENT_SOCKET_DECL(tcppauseread); NETIEVENT_SOCKET_DECL(tcpstartread); NETIEVENT_SOCKET_DECL(tcpstop); +NETIEVENT_SOCKET_DECL(tlsclose); +NETIEVENT_SOCKET_DECL(tlsconnect); +NETIEVENT_SOCKET_DECL(tlsdobio); +NETIEVENT_SOCKET_DECL(tlsstartread); +NETIEVENT_SOCKET_HANDLE_DECL(tlscancel); NETIEVENT_SOCKET_DECL(udpclose); NETIEVENT_SOCKET_DECL(udplisten); NETIEVENT_SOCKET_DECL(udpread); @@ -1526,6 +1631,7 @@ NETIEVENT_SOCKET_DECL(tlsdnscycle); NETIEVENT_SOCKET_REQ_DECL(tcpconnect); NETIEVENT_SOCKET_REQ_DECL(tcpsend); +NETIEVENT_SOCKET_REQ_DECL(tlssend); NETIEVENT_SOCKET_REQ_DECL(udpconnect); NETIEVENT_SOCKET_REQ_RESULT_DECL(connectcb); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 584142a52c..3253dab999 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -716,6 +716,13 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) { NETIEVENT_CASE(tcpdnsread); NETIEVENT_CASE(tcpdnsstop); + NETIEVENT_CASE(tlsstartread); + NETIEVENT_CASE(tlssend); + NETIEVENT_CASE(tlsclose); + NETIEVENT_CASE(tlsconnect); + NETIEVENT_CASE(tlsdobio); + NETIEVENT_CASE(tlscancel); + NETIEVENT_CASE(tlsdnscycle); NETIEVENT_CASE(tlsdnsaccept); NETIEVENT_CASE(tlsdnslisten); @@ -776,6 +783,11 @@ NETIEVENT_SOCKET_DEF(tcplisten); NETIEVENT_SOCKET_DEF(tcppauseread); NETIEVENT_SOCKET_DEF(tcpstartread); NETIEVENT_SOCKET_DEF(tcpstop); +NETIEVENT_SOCKET_DEF(tlsclose); +NETIEVENT_SOCKET_DEF(tlsconnect); +NETIEVENT_SOCKET_DEF(tlsdobio); +NETIEVENT_SOCKET_DEF(tlsstartread); +NETIEVENT_SOCKET_HANDLE_DEF(tlscancel); NETIEVENT_SOCKET_DEF(udpclose); NETIEVENT_SOCKET_DEF(udplisten); NETIEVENT_SOCKET_DEF(udpread); @@ -804,6 +816,7 @@ NETIEVENT_SOCKET_DEF(tlsdnsshutdown); NETIEVENT_SOCKET_REQ_DEF(tcpconnect); NETIEVENT_SOCKET_REQ_DEF(tcpsend); +NETIEVENT_SOCKET_REQ_DEF(tlssend); NETIEVENT_SOCKET_REQ_DEF(udpconnect); NETIEVENT_SOCKET_REQ_RESULT_DEF(connectcb); @@ -986,6 +999,8 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { isc_mutex_destroy(&sock->lock); isc_condition_destroy(&sock->cond); isc_condition_destroy(&sock->scond); + isc__nm_tls_cleanup_data(sock); + #ifdef NETMGR_TRACE LOCK(&sock->mgr->lock); ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link); @@ -1094,6 +1109,9 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_tcpdnssocket: isc__nm_tcpdns_close(sock); return; + case isc_nm_tlssocket: + isc__nm_tls_close(sock); + break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_close(sock); return; @@ -1139,7 +1157,8 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) { REQUIRE((*sockp)->type == isc_nm_udplistener || (*sockp)->type == isc_nm_tcplistener || (*sockp)->type == isc_nm_tcpdnslistener || - (*sockp)->type == isc_nm_tlsdnslistener); + (*sockp)->type == isc_nm_tlsdnslistener || + (*sockp)->type == isc_nm_tlslistener); isc__nmsocket_detach(sockp); } @@ -1218,6 +1237,9 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, isc_condition_init(&sock->scond); isc_refcount_init(&sock->references, 1); + memset(&sock->tlsstream, 0, sizeof(sock->tlsstream)); + ISC_LIST_INIT(sock->tlsstream.sends); + NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %lu\n", sock, isc_refcount_current(&sock->references)); @@ -1353,7 +1375,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer, #endif UNLOCK(&sock->lock); - if (sock->type == isc_nm_tcpsocket || + if (sock->type == isc_nm_tcpsocket || sock->type == isc_nm_tlssocket || (sock->type == isc_nm_udpsocket && atomic_load(&sock->client)) || (sock->type == isc_nm_tcpdnssocket && atomic_load(&sock->client)) || (sock->type == isc_nm_tlsdnssocket && atomic_load(&sock->client))) @@ -1390,6 +1412,7 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) { return (handle->sock->type == isc_nm_tcpsocket || handle->sock->type == isc_nm_tcpdnssocket || + handle->sock->type == isc_nm_tlssocket || handle->sock->type == isc_nm_tlsdnssocket); } @@ -1667,6 +1690,9 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_tcpdnssocket: isc__nm_tcpdns_send(handle, region, cb, cbarg); break; + case isc_nm_tlssocket: + isc__nm_tls_send(handle, region, cb, cbarg); + break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_send(handle, region, cb, cbarg); break; @@ -1697,6 +1723,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { case isc_nm_tcpdnssocket: isc__nm_tcpdns_read(handle, cb, cbarg); break; + case isc_nm_tlssocket: + isc__nm_tls_read(handle, cb, cbarg); + break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_read(handle, cb, cbarg); break; @@ -1723,6 +1752,9 @@ isc_nm_cancelread(isc_nmhandle_t *handle) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_cancelread(handle); break; + case isc_nm_tlssocket: + isc__nm_tls_cancelread(handle); + break; default: INSIST(0); ISC_UNREACHABLE(); @@ -1739,6 +1771,9 @@ isc_nm_pauseread(isc_nmhandle_t *handle) { case isc_nm_tcpsocket: isc__nm_tcp_pauseread(handle); break; + case isc_nm_tlssocket: + isc__nm_tls_pauseread(handle); + break; default: INSIST(0); ISC_UNREACHABLE(); @@ -1755,6 +1790,9 @@ isc_nm_resumeread(isc_nmhandle_t *handle) { case isc_nm_tcpsocket: isc__nm_tcp_resumeread(handle); break; + case isc_nm_tlssocket: + isc__nm_tls_resumeread(handle); + break; default: INSIST(0); ISC_UNREACHABLE(); @@ -1775,6 +1813,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { case isc_nm_tcplistener: isc__nm_tcp_stoplistening(sock); break; + case isc_nm_tlslistener: + isc__nm_tls_stoplistening(sock); + break; case isc_nm_tlsdnslistener: isc__nm_tlsdns_stoplistening(sock); break; @@ -2322,6 +2363,10 @@ nmsocket_type_totext(isc_nmsocket_type type) { return ("isc_nm_tcpdnslistener"); case isc_nm_tcpdnssocket: return ("isc_nm_tcpdnssocket"); + case isc_nm_tlssocket: + return ("isc_nm_tlssocket"); + case isc_nm_tlslistener: + return ("isc_nm_tlslistener"); case isc_nm_tlsdnslistener: return ("isc_nm_tlsdnslistener"); case isc_nm_tlsdnssocket: diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c new file mode 100644 index 0000000000..3b4c01f11d --- /dev/null +++ b/lib/isc/netmgr/tlsstream.c @@ -0,0 +1,936 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netmgr-int.h" +#include "uv-compat.h" + +#define TLS_BUF_SIZE 65536 + +static isc_result_t +tls_error_to_result(int tls_err) { + switch (tls_err) { + case SSL_ERROR_ZERO_RETURN: + return (ISC_R_EOF); + default: + return (ISC_R_UNEXPECTED); + } +} + +static void +tls_do_bio(isc_nmsocket_t *sock); + +static void +tls_close_direct(isc_nmsocket_t *sock); + +static void +async_tls_do_bio(isc_nmsocket_t *sock); + +/* + * The socket is closing, outerhandle has been detached, listener is + * inactive, or the netmgr is closing: any operation on it should abort + * with ISC_R_CANCELED. + */ +static bool +inactive(isc_nmsocket_t *sock) { + return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) || + sock->outerhandle == NULL || + (sock->listener != NULL && + !isc__nmsocket_active(sock->listener)) || + atomic_load(&sock->mgr->closing)); +} + +static void +update_result(isc_nmsocket_t *sock, const isc_result_t result) { + LOCK(&sock->lock); + sock->result = result; + SIGNAL(&sock->cond); + if (!atomic_load(&sock->active)) { + WAIT(&sock->scond, &sock->lock); + } + UNLOCK(&sock->lock); + if (sock->parent) { + LOCK(&sock->parent->lock); + sock->parent->result = result; + UNLOCK(&sock->parent->lock); + } +} + +static void +tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { + isc_nmsocket_tls_send_req_t *send_req = + (isc_nmsocket_tls_send_req_t *)cbarg; + isc_nmsocket_t *sock = send_req->tlssock; + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(VALID_NMSOCK(sock)); + + /* XXXWPK TODO */ + UNUSED(eresult); + + isc_mem_put(handle->sock->mgr->mctx, send_req->data.base, + send_req->data.length); + isc_mem_put(handle->sock->mgr->mctx, send_req, sizeof(*send_req)); + + sock->tlsstream.nsending--; + async_tls_do_bio(sock); + isc__nmsocket_detach(&sock); +} + +static void +tls_failed_read_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle, + const isc_result_t result, const bool close) { + REQUIRE(VALID_NMSOCK(sock)); + + if (!sock->tlsstream.server && + (sock->tlsstream.state == TLS_INIT || + sock->tlsstream.state == TLS_HANDSHAKE) && + sock->connect_cb != NULL) + { + INSIST(handle == NULL); + handle = isc__nmhandle_get(sock, NULL, NULL); + sock->connect_cb(handle, result, sock->connect_cbarg); + update_result(sock, result); + isc__nmsocket_clearcb(sock); + isc_nmhandle_detach(&handle); + } else if (sock->recv_cb != NULL) { + isc__nm_uvreq_t *req = NULL; + req = isc__nm_uvreq_get(sock->mgr, sock); + req->cb.recv = sock->recv_cb; + req->cbarg = sock->recv_cbarg; + req->handle = NULL; + if (handle) { + REQUIRE(VALID_NMHANDLE(handle)); + isc_nmhandle_attach(handle, &req->handle); + } else { + req->handle = isc__nmhandle_get(sock, NULL, NULL); + } + isc__nmsocket_clearcb(sock); + isc__nm_readcb(sock, req, result); + } + sock->tlsstream.state = TLS_ERROR; + + if (close) { + isc__nmsocket_prep_destroy(sock); + } +} + +static void +async_tls_do_bio(isc_nmsocket_t *sock) { + isc__netievent_tlsdobio_t *ievent = + isc__nm_get_netievent_tlsdobio(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + +static void +tls_do_bio(isc_nmsocket_t *sock) { + isc_result_t result = ISC_R_SUCCESS; + int pending, tls_err = 0; + int rv; + isc__nm_uvreq_t *req; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); + + /* We will resume read if TLS layer wants us to */ + if (sock->outerhandle != NULL) { + REQUIRE(VALID_NMHANDLE(sock->outerhandle)); + isc_nm_pauseread(sock->outerhandle); + } + + if (sock->tlsstream.state == TLS_INIT) { + (void)SSL_do_handshake(sock->tlsstream.ssl); + sock->tlsstream.state = TLS_HANDSHAKE; + } else if (sock->tlsstream.state == TLS_ERROR) { + result = ISC_R_FAILURE; + goto low_level_error; + } else if (sock->tlsstream.state == TLS_CLOSED) { + return; + } + + /* Data from TLS to client */ + char buf[1]; + if (sock->tlsstream.state == TLS_IO && sock->recv_cb != NULL && + !atomic_load(&sock->readpaused)) + { + (void)SSL_peek(sock->tlsstream.ssl, buf, 1); + while ((pending = SSL_pending(sock->tlsstream.ssl)) > 0) { + if (pending > TLS_BUF_SIZE) { + pending = TLS_BUF_SIZE; + } + isc_region_t region = { + isc_mem_get(sock->mgr->mctx, pending), pending + }; + isc_region_t dregion; + memset(region.base, 0, region.length); + rv = SSL_read(sock->tlsstream.ssl, region.base, + region.length); + /* Pending succeded, so should read */ + RUNTIME_CHECK(rv == pending); + dregion = (isc_region_t){ region.base, rv }; + sock->recv_cb(sock->statichandle, ISC_R_SUCCESS, + &dregion, sock->recv_cbarg); + isc_mem_put(sock->mgr->mctx, region.base, + region.length); + } + } + + /* Peek to move the session forward */ + (void)SSL_peek(sock->tlsstream.ssl, buf, 1); + + /* Data from TLS to network */ + pending = BIO_pending(sock->tlsstream.app_bio); + if (pending > 0) { + /*TODO Should we keep the track of these requests in a list? */ + isc_nmsocket_tls_send_req_t *send_req = NULL; + if (pending > TLS_BUF_SIZE) { + pending = TLS_BUF_SIZE; + } + send_req = isc_mem_get(sock->mgr->mctx, sizeof(*send_req)); + send_req->data.base = isc_mem_get(sock->mgr->mctx, pending); + send_req->data.length = pending; + send_req->tlssock = NULL; + isc__nmsocket_attach(sock, &send_req->tlssock); + rv = BIO_read(sock->tlsstream.app_bio, send_req->data.base, + pending); + /* There's something pending, read must succeed */ + RUNTIME_CHECK(rv == pending); + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nm_send(sock->outerhandle, &send_req->data, tls_senddone, + send_req); + /* We'll continue in tls_senddone */ + return; + } + + /* Get the potential error code */ + rv = SSL_peek(sock->tlsstream.ssl, buf, 1); + + if (rv < 0) { + tls_err = SSL_get_error(sock->tlsstream.ssl, rv); + } + + /* Only after doing the IO we can check if SSL handshake is done */ + if (sock->tlsstream.state == TLS_HANDSHAKE && + SSL_is_init_finished(sock->tlsstream.ssl) == 1) + { + isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL); + if (sock->tlsstream.server) { + sock->listener->accept_cb(sock->statichandle, + ISC_R_SUCCESS, + sock->listener->accept_cbarg); + } else { + sock->connect_cb(tlshandle, ISC_R_SUCCESS, + sock->connect_cbarg); + update_result(tlshandle->sock, ISC_R_SUCCESS); + } + isc_nmhandle_detach(&tlshandle); + sock->tlsstream.state = TLS_IO; + async_tls_do_bio(sock); + return; + } + + switch (tls_err) { + case 0: + return; + case SSL_ERROR_WANT_WRITE: + if (sock->tlsstream.nsending == 0) { + /* + * Launch tls_do_bio asynchronously. If we're sending + * already the send callback will call it. + */ + async_tls_do_bio(sock); + } else { + return; + } + break; + case SSL_ERROR_WANT_READ: + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nm_resumeread(sock->outerhandle); + break; + default: + result = tls_error_to_result(tls_err); + goto error; + } + + while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) != NULL) { + INSIST(VALID_UVREQ(req)); + rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base, + req->uvbuf.len); + if (rv < 0) { + if (sock->tlsstream.nsending == 0) { + async_tls_do_bio(sock); + } + return; + } + if (rv != (int)req->uvbuf.len) { + if (!sock->tlsstream.server && + (sock->tlsstream.state == TLS_HANDSHAKE || + TLS_INIT)) + { + isc_nmhandle_t *tlshandle = + isc__nmhandle_get(sock, NULL, NULL); + sock->connect_cb(tlshandle, result, + sock->connect_cbarg); + update_result(tlshandle->sock, result); + isc_nmhandle_detach(&tlshandle); + } + sock->tlsstream.state = TLS_ERROR; + async_tls_do_bio(sock); + return; + } + ISC_LIST_UNLINK(sock->tlsstream.sends, req, link); + req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg); + isc__nm_uvreq_put(&req, sock); + } + + return; + +error: + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR, + ISC_LOG_ERROR, "SSL error in BIO: %d %s", tls_err, + isc_result_totext(result)); +low_level_error: + if (sock->tlsstream.state == TLS_HANDSHAKE) { + isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL); + if (!sock->tlsstream.server) { + sock->connect_cb(tlshandle, result, + sock->connect_cbarg); + update_result(tlshandle->sock, result); + } + isc_nmhandle_detach(&tlshandle); + } else if (sock->tlsstream.state == TLS_IO) { + if (ISC_LIST_HEAD(sock->tlsstream.sends) != NULL) { + while ((req = ISC_LIST_HEAD(sock->tlsstream.sends)) != + NULL) { + req->cb.send(sock->statichandle, result, + req->cbarg); + ISC_LIST_UNLINK(sock->tlsstream.sends, req, + link); + isc__nm_uvreq_put(&req, sock); + } + } else if (sock->recv_cb != NULL) { + tls_failed_read_cb(sock, sock->statichandle, result, + false); + } else { + tls_close_direct(sock); + } + } + sock->tlsstream.state = TLS_ERROR; +} + +static void +tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, + void *cbarg) { + isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg; + int rv; + + REQUIRE(VALID_NMSOCK(tlssock)); + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(tlssock->tid == isc_nm_tid()); + if (result != ISC_R_SUCCESS) { + tls_failed_read_cb(tlssock, tlssock->statichandle, result, + true); + return; + } + rv = BIO_write(tlssock->tlsstream.app_bio, region->base, + region->length); + + if (rv != (int)region->length) { + /* XXXWPK log it? */ + tlssock->tlsstream.state = TLS_ERROR; + } + tls_do_bio(tlssock); +} + +static isc_result_t +initialize_tls(isc_nmsocket_t *sock, bool server) { + REQUIRE(sock->tid == isc_nm_tid()); + + if (BIO_new_bio_pair(&(sock->tlsstream.ssl_bio), TLS_BUF_SIZE, + &(sock->tlsstream.app_bio), TLS_BUF_SIZE) != 1) + { + SSL_free(sock->tlsstream.ssl); + return (ISC_R_TLSERROR); + } + + SSL_set_bio(sock->tlsstream.ssl, sock->tlsstream.ssl_bio, + sock->tlsstream.ssl_bio); + if (server) { + SSL_set_accept_state(sock->tlsstream.ssl); + } else { + SSL_set_connect_state(sock->tlsstream.ssl); + } + sock->tlsstream.nsending = 0; + isc_nm_read(sock->outerhandle, tls_readcb, sock); + tls_do_bio(sock); + return (ISC_R_SUCCESS); +} + +static isc_result_t +tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { + isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg; + isc_nmsocket_t *tlssock = NULL; + int r; + + /* If accept() was unsuccessful we can't do anything */ + if (result != ISC_R_SUCCESS) { + return (result); + } + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(VALID_NMSOCK(tlslistensock)); + REQUIRE(tlslistensock->type == isc_nm_tlslistener); + + /* + * We need to create a 'wrapper' tlssocket for this connection. + */ + tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock)); + isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket, + handle->sock->iface); + + /* We need to initialize SSL now to reference SSL_CTX properly */ + tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx; + tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx); + ISC_LIST_INIT(tlssock->tlsstream.sends); + if (tlssock->tlsstream.ssl == NULL) { + update_result(tlssock, ISC_R_TLSERROR); + atomic_store(&tlssock->closed, true); + isc__nmsocket_detach(&tlssock); + return (ISC_R_TLSERROR); + } + + tlssock->extrahandlesize = tlslistensock->extrahandlesize; + isc__nmsocket_attach(tlslistensock, &tlssock->listener); + isc_nmhandle_attach(handle, &tlssock->outerhandle); + tlssock->peer = handle->sock->peer; + tlssock->read_timeout = atomic_load(&handle->sock->mgr->init); + tlssock->tid = isc_nm_tid(); + tlssock->tlsstream.server = true; + tlssock->tlsstream.state = TLS_INIT; + + r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop, + &tlssock->timer); + RUNTIME_CHECK(r == 0); + + tlssock->timer.data = tlssock; + tlssock->timer_initialized = true; + tlssock->tlsstream.ctx = tlslistensock->tlsstream.ctx; + + result = initialize_tls(tlssock, true); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* TODO: catch failure code, detach tlssock, and log the error */ + + return (result); +} + +isc_result_t +isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface, + isc_nm_accept_cb_t accept_cb, void *accept_cbarg, + size_t extrahandlesize, int backlog, isc_quota_t *quota, + SSL_CTX *sslctx, isc_nmsocket_t **sockp) { + isc_result_t result; + isc_nmsocket_t *tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock)); + isc_nmsocket_t *tsock = NULL; + + REQUIRE(VALID_NM(mgr)); + + isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface); + tlssock->result = ISC_R_DEFAULT; + tlssock->accept_cb = accept_cb; + tlssock->accept_cbarg = accept_cbarg; + tlssock->extrahandlesize = extrahandlesize; + tlssock->tlsstream.ctx = sslctx; + tlssock->tlsstream.ssl = NULL; + + /* + * tlssock will be a TLS 'wrapper' around an unencrypted stream. + * We set tlssock->outer to a socket listening for a TCP connection. + */ + result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock, + extrahandlesize, backlog, quota, + &tlssock->outer); + if (result != ISC_R_SUCCESS) { + atomic_store(&tlssock->closed, true); + isc__nmsocket_detach(&tlssock); + return (result); + } + + /* wait for listen result */ + isc__nmsocket_attach(tlssock->outer, &tsock); + LOCK(&tlssock->outer->lock); + while (tlssock->outer->rchildren != tlssock->outer->nchildren) { + WAIT(&tlssock->outer->cond, &tlssock->outer->lock); + } + result = tlssock->outer->result; + tlssock->result = result; + atomic_store(&tlssock->active, true); + INSIST(tlssock->outer->tlsstream.tlslistener == NULL); + isc__nmsocket_attach(tlssock, &tlssock->outer->tlsstream.tlslistener); + BROADCAST(&tlssock->outer->scond); + UNLOCK(&tlssock->outer->lock); + isc__nmsocket_detach(&tsock); + INSIST(result != ISC_R_DEFAULT); + + if (result == ISC_R_SUCCESS) { + atomic_store(&tlssock->listening, true); + *sockp = tlssock; + } + + return result; +} + +void +isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) { + int rv; + isc__netievent_tlssend_t *ievent = (isc__netievent_tlssend_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + isc__nm_uvreq_t *req = ievent->req; + ievent->req = NULL; + REQUIRE(VALID_UVREQ(req)); + REQUIRE(sock->tid == isc_nm_tid()); + UNUSED(worker); + + if (inactive(sock)) { + req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg); + isc__nm_uvreq_put(&req, sock); + return; + } + if (!ISC_LIST_EMPTY(sock->tlsstream.sends)) { + /* We're not the first */ + ISC_LIST_APPEND(sock->tlsstream.sends, req, link); + tls_do_bio(sock); + return; + } + + rv = SSL_write(sock->tlsstream.ssl, req->uvbuf.base, req->uvbuf.len); + if (rv < 0) { + /* + * We might need to read, we might need to write, or the + * TLS socket might be dead - in any case, we need to + * enqueue the uvreq and let the TLS BIO layer do the rest. + */ + ISC_LIST_APPEND(sock->tlsstream.sends, req, link); + tls_do_bio(sock); + return; + } + if (rv != (int)req->uvbuf.len) { + sock->tlsstream.state = TLS_ERROR; + async_tls_do_bio(sock); + return; + } + req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg); + isc__nm_uvreq_put(&req, sock); + tls_do_bio(sock); + return; +} + +void +isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg) { + isc__netievent_tlssend_t *ievent = NULL; + isc__nm_uvreq_t *uvreq = NULL; + isc_nmsocket_t *sock = NULL; + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + sock = handle->sock; + + REQUIRE(sock->type == isc_nm_tlssocket); + + if (inactive(sock)) { + cb(handle, ISC_R_CANCELED, cbarg); + return; + } + + uvreq = isc__nm_uvreq_get(sock->mgr, sock); + isc_nmhandle_attach(handle, &uvreq->handle); + uvreq->cb.send = cb; + uvreq->cbarg = cbarg; + + uvreq->uvbuf.base = (char *)region->base; + uvreq->uvbuf.len = region->length; + + /* + * We need to create an event and pass it using async channel + */ + ievent = isc__nm_get_netievent_tlssend(sock->mgr, sock, uvreq); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + +void +isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tlsstartread_t *ievent = + (isc__netievent_tlsstartread_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + + REQUIRE(sock->tid == isc_nm_tid()); + UNUSED(worker); + + tls_do_bio(sock); +} + +void +isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->statichandle == handle); + REQUIRE(handle->sock->tid == isc_nm_tid()); + + isc__netievent_tlsstartread_t *ievent = NULL; + isc_nmsocket_t *sock = handle->sock; + + if (inactive(sock)) { + cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg); + return; + } + + sock->recv_cb = cb; + sock->recv_cbarg = cbarg; + + ievent = isc__nm_get_netievent_tlsstartread(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + +void +isc__nm_tls_pauseread(isc_nmhandle_t *handle) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + isc_nmsocket_t *sock = handle->sock; + + atomic_store(&sock->readpaused, true); +} + +void +isc__nm_tls_resumeread(isc_nmhandle_t *handle) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + isc_nmsocket_t *sock = handle->sock; + + atomic_store(&sock->readpaused, false); + async_tls_do_bio(sock); +} + +static void +timer_close_cb(uv_handle_t *handle) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle); + tls_close_direct(sock); +} + +static void +tls_close_direct(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); + + /* if (!sock->tlsstream.server) { */ + /* INSIST(sock->tlsstream.state != TLS_HANDSHAKE && */ + /* sock->tlsstream.state != TLS_INIT); */ + /* } */ + + sock->tlsstream.state = TLS_CLOSING; + + if (sock->timer_running) { + uv_timer_stop(&sock->timer); + sock->timer_running = false; + } + + /* We don't need atomics here, it's all in single network thread + */ + if (sock->timer_initialized) { + /* + * We need to fire the timer callback to clean it up, + * it will then call us again (via detach) so that we + * can finally close the socket. + */ + sock->timer_initialized = false; + uv_timer_stop(&sock->timer); + uv_close((uv_handle_t *)&sock->timer, timer_close_cb); + } else { + /* + * At this point we're certain that there are no + * external references, we can close everything. + */ + if (sock->outerhandle != NULL) { + isc_nm_pauseread(sock->outerhandle); + isc_nmhandle_detach(&sock->outerhandle); + } + if (sock->listener != NULL) { + isc__nmsocket_detach(&sock->listener); + } + if (sock->tlsstream.ssl != NULL) { + SSL_free(sock->tlsstream.ssl); + sock->tlsstream.ssl = NULL; + /* These are destroyed when we free SSL* */ + sock->tlsstream.ctx = NULL; + sock->tlsstream.ssl_bio = NULL; + } + if (sock->tlsstream.app_bio != NULL) { + BIO_free(sock->tlsstream.app_bio); + sock->tlsstream.app_bio = NULL; + } + sock->tlsstream.state = TLS_CLOSED; + atomic_store(&sock->closed, true); + isc__nmsocket_detach(&sock); + } +} + +void +isc__nm_tls_close(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + + if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, + true)) { + return; + } + + if (sock->tid == isc_nm_tid()) { + tls_close_direct(sock); + } else { + isc__netievent_tlsclose_t *ievent = + isc__nm_get_netievent_tlsclose(sock->mgr, sock); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); + } +} + +void +isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0; + + REQUIRE(ievent->sock->tid == isc_nm_tid()); + UNUSED(worker); + + tls_close_direct(ievent->sock); +} + +void +isc__nm_tls_stoplistening(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlslistener); + + atomic_store(&sock->listening, false); + atomic_store(&sock->closed, true); + sock->recv_cb = NULL; + sock->recv_cbarg = NULL; + if (sock->tlsstream.ssl != NULL) { + SSL_free(sock->tlsstream.ssl); + sock->tlsstream.ssl = NULL; + sock->tlsstream.ctx = NULL; + } + + if (sock->outer != NULL) { + isc_nm_stoplistening(sock->outer); + isc__nmsocket_detach(&sock->outer); + } +} + +isc_result_t +isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, + isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx, + unsigned int timeout, size_t extrahandlesize) { + isc_nmsocket_t *nsock = NULL, *tsock = NULL; + isc__netievent_tlsconnect_t *ievent = NULL; + isc_result_t result = ISC_R_DEFAULT; + + REQUIRE(VALID_NM(mgr)); + + nsock = isc_mem_get(mgr->mctx, sizeof(*nsock)); + isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local); + nsock->extrahandlesize = extrahandlesize; + nsock->result = ISC_R_DEFAULT; + nsock->connect_cb = cb; + nsock->connect_cbarg = cbarg; + nsock->connect_timeout = timeout; + nsock->tlsstream.ctx = ctx; + + ievent = isc__nm_get_netievent_tlsconnect(mgr, nsock); + ievent->local = local->addr; + ievent->peer = peer->addr; + ievent->ctx = ctx; + + isc__nmsocket_attach(nsock, &tsock); + if (isc__nm_in_netthread()) { + nsock->tid = isc_nm_tid(); + isc__nm_async_tlsconnect(&mgr->workers[nsock->tid], + (isc__netievent_t *)ievent); + isc__nm_put_netievent_tlsconnect(mgr, ievent); + } else { + nsock->tid = isc_random_uniform(mgr->nworkers); + isc__nm_enqueue_ievent(&mgr->workers[nsock->tid], + (isc__netievent_t *)ievent); + } + + LOCK(&nsock->lock); + result = nsock->result; + while (result == ISC_R_DEFAULT) { + WAIT(&nsock->cond, &nsock->lock); + result = nsock->result; + } + atomic_store(&nsock->active, true); + BROADCAST(&nsock->scond); + UNLOCK(&nsock->lock); + INSIST(VALID_NMSOCK(nsock)); + isc__nmsocket_detach(&tsock); + + INSIST(result != ISC_R_DEFAULT); + + return (result); +} + +static void +tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { + isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg; + + REQUIRE(VALID_NMSOCK(tlssock)); + + if (result != ISC_R_SUCCESS) { + tlssock->connect_cb(handle, result, tlssock->connect_cbarg); + update_result(tlssock, result); + tls_close_direct(tlssock); + return; + } + + INSIST(VALID_NMHANDLE(handle)); + + tlssock->peer = isc_nmhandle_peeraddr(handle); + isc_nmhandle_attach(handle, &tlssock->outerhandle); + result = initialize_tls(tlssock, false); + if (result != ISC_R_SUCCESS) { + tlssock->connect_cb(handle, result, tlssock->connect_cbarg); + update_result(tlssock, result); + tls_close_direct(tlssock); + return; + } +} + +void +isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tlsconnect_t *ievent = + (isc__netievent_tlsconnect_t *)ev0; + isc_nmsocket_t *tlssock = ievent->sock; + isc_result_t result; + int r; + isc_nmhandle_t *tlshandle = NULL; + + UNUSED(worker); + + /* + * We need to initialize SSL now to reference SSL_CTX properly. + */ + tlssock->tlsstream.ssl = SSL_new(tlssock->tlsstream.ctx); + if (tlssock->tlsstream.ssl == NULL) { + result = ISC_R_TLSERROR; + goto error; + } + + tlssock->tid = isc_nm_tid(); + r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop, + &tlssock->timer); + RUNTIME_CHECK(r == 0); + + tlssock->timer.data = tlssock; + tlssock->timer_initialized = true; + tlssock->tlsstream.state = TLS_INIT; + + result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local, + (isc_nmiface_t *)&ievent->peer, + tls_connect_cb, tlssock, + tlssock->connect_timeout, 0); + if (result != ISC_R_SUCCESS) { + goto error; + } + + return; +error: + tlshandle = isc__nmhandle_get(tlssock, NULL, NULL); + atomic_store(&tlssock->closed, true); + tlssock->connect_cb(tlshandle, result, tlssock->connect_cbarg); + isc_nmhandle_detach(&tlshandle); + update_result(tlssock, result); + tls_close_direct(tlssock); +} + +void +isc__nm_tls_cancelread(isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + isc__netievent_tlscancel_t *ievent = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + + sock = handle->sock; + + REQUIRE(sock->type == isc_nm_tlssocket); + + ievent = isc__nm_get_netievent_tlscancel(sock->mgr, sock, handle); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); +} + +void +isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tlscancel_t *ievent = (isc__netievent_tlscancel_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + isc_nmhandle_t *handle = ievent->handle; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(worker->id == sock->tid); + REQUIRE(sock->tid == isc_nm_tid()); + UNUSED(worker); + + tls_failed_read_cb(sock, handle, ISC_R_EOF, false); + + if (sock->outerhandle) { + isc__nm_tcp_cancelread(sock->outerhandle); + } +} + +void +isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0) { + UNUSED(worker); + isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0; + tls_do_bio(ievent->sock); +} + +void +isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) { + if (sock->tlsstream.tlslistener) { + REQUIRE(VALID_NMSOCK(sock->tlsstream.tlslistener)); + isc__nmsocket_detach(&sock->tlsstream.tlslistener); + } +} diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index ce691b6a78..c780fc2b5e 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -449,6 +449,7 @@ isc_nm_closedown isc_nm_destroy isc_nm_detach isc_nm_listentcpdns +isc_nm_listentls isc_nm_listentlsdns isc_nm_listentcp isc_nm_listenudp @@ -467,6 +468,7 @@ isc_nm_settimeouts isc_nm_tcpdns_keepalive isc_nm_tcpdns_sequential isc_nm_tid +isc_nm_tlsconnect isc_nm_tlsdnsconnect isc_nm_udpconnect isc_nmsocket_close diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index fea6c87ab4..7b6dc36a78 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -608,7 +608,7 @@ Library Source Files - + Library Source Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 72e7e0cb50..a06a1e810d 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -414,6 +414,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/util/copyrights b/util/copyrights index 639c1bb3b4..8c2c697144 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1909,6 +1909,7 @@ ./lib/isc/netmgr/tcp.c C 2019,2020,2021 ./lib/isc/netmgr/tcpdns.c C 2019,2020,2021 ./lib/isc/netmgr/tlsdns.c C 2020,2021 +./lib/isc/netmgr/tlsstream.c C 2019,2020,2021 ./lib/isc/netmgr/udp.c C 2019,2020,2021 ./lib/isc/netmgr/uv-compat.c C 2020,2021 ./lib/isc/netmgr/uv-compat.h C 2019,2020,2021 From 06951472dd52eb11136502cf1ec61abfb679da20 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 16 Sep 2020 12:21:32 -0700 Subject: [PATCH 02/11] Add parser support for DoH configuration options This commit adds stub parser support and tests for: - an "http" global option for HTTP/2 endpoint configuration. - command line options to set http or https port numbers by specifying -p http=PORT or -p https=PORT. (NOTE: this change only affects syntax; specifying HTTP and HTTPS ports on the command line currently has no effect.) - named.conf options "http-port" and "https-port" - HTTPSPORT environment variable for use when running tests. --- bin/named/config.c | 2 + bin/named/include/named/globals.h | 2 + bin/named/main.c | 14 +- bin/named/named.conf.rst | 17 +- bin/named/named.rst | 4 +- .../system/checkconf/good-doh-global.conf | 27 +++ .../system/checkconf/good-dot-global.conf | 19 ++ bin/tests/system/conf.sh.common | 1 + bin/tests/system/get_ports.sh | 1 + bin/tests/system/run.sh.in | 2 +- doc/arm/reference.rst | 19 ++ doc/man/named.8in | 2 + doc/man/named.conf.5in | 21 +- doc/misc/Makefile.am | 6 +- doc/misc/http.grammar.rst | 5 + doc/misc/options | 12 +- doc/misc/options.active | 12 +- doc/misc/options.grammar.rst | 8 +- doc/notes/notes-current.rst | 7 + lib/isccfg/Makefile.am | 6 +- lib/isccfg/httpconf.c | 180 ++++++++++++++++ lib/isccfg/include/isccfg/httpconf.h | 69 +++++++ lib/isccfg/include/isccfg/tlsconf.h | 69 +++++++ lib/isccfg/namedconf.c | 35 ++++ lib/isccfg/tlsconf.c | 194 ++++++++++++++++++ 25 files changed, 719 insertions(+), 15 deletions(-) create mode 100644 bin/tests/system/checkconf/good-doh-global.conf create mode 100644 bin/tests/system/checkconf/good-dot-global.conf create mode 100644 doc/misc/http.grammar.rst create mode 100644 lib/isccfg/httpconf.c create mode 100644 lib/isccfg/include/isccfg/httpconf.h create mode 100644 lib/isccfg/include/isccfg/tlsconf.h create mode 100644 lib/isccfg/tlsconf.c diff --git a/bin/named/config.c b/bin/named/config.c index 72094f8302..99af2dd570 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -94,6 +94,8 @@ options {\n\ # pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\ port 53;\n\ tls-port 853;\n\ + http-port 80;\n\ + https-port 443;\n\ prefetch 2 9;\n\ recursing-file \"named.recursing\";\n\ recursive-clients 1000;\n\ diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index 501bedaea4..302eb10cd3 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -73,6 +73,8 @@ EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS); EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER); EXTERN in_port_t named_g_port INIT(0); EXTERN in_port_t named_g_tlsport INIT(0); +EXTERN in_port_t named_g_httpport INIT(0); +EXTERN in_port_t named_g_httpsport INIT(0); EXTERN isc_dscp_t named_g_dscp INIT(-1); EXTERN named_server_t *named_g_server INIT(NULL); diff --git a/bin/named/main.c b/bin/named/main.c index eacb18cc81..cfdc941e05 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -705,7 +705,7 @@ parse_T_opt(char *option) { static void parse_port(char *arg) { - enum { DNSPORT, TLSPORT } ptype = DNSPORT; + enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT; char *value = arg; int port; @@ -714,6 +714,12 @@ parse_port(char *arg) { } else if (strncmp(arg, "tls=", 4) == 0) { value = arg + 4; ptype = TLSPORT; + } else if (strncmp(arg, "https=", 6) == 0) { + value = arg + 6; + ptype = HTTPSPORT; + } else if (strncmp(arg, "http=", 5) == 0) { + value = arg + 6; + ptype = HTTPPORT; } port = parse_int(value, "port"); @@ -728,6 +734,12 @@ parse_port(char *arg) { case TLSPORT: named_g_tlsport = port; break; + case HTTPSPORT: + named_g_httpsport = port; + break; + case HTTPPORT: + named_g_httpport = port; + break; default: INSIST(0); ISC_UNREACHABLE(); diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 722c514dbc..08a1476bc3 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -86,6 +86,15 @@ DYNDB dyndb string quoted_string { unspecified-text }; +HTTP +^^^^ + +:: + + http string { + endpoints { quoted_string; ... }; + }; + KEY ^^^ @@ -264,6 +273,8 @@ OPTIONS glue-cache boolean;// deprecated heartbeat-interval integer; hostname ( quoted_string | none ); + http-port integer; + https-port integer; inline-signing boolean; interface-interval duration; ipv4only-contact string; @@ -275,10 +286,12 @@ OPTIONS key-directory quoted_string; lame-ttl duration; listen-on [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; listen-on-v6 [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; lmdb-mapsize sizeval; lock-file ( quoted_string | none ); diff --git a/bin/named/named.rst b/bin/named/named.rst index 9b039cdbf0..c2b19f6dd6 100644 --- a/bin/named/named.rst +++ b/bin/named/named.rst @@ -115,7 +115,9 @@ Options ``portnum``; if not not specified, the default is port 53. If ``value`` is of the form ``tls=``, the server will listen for TLS queries on ``portnum``; the default is 853. - + If ``value`` is of the form ``https=``, the server will + listen for HTTPS queries on ``portnum``; the default is 443. + ``-s`` This option writes memory usage statistics to ``stdout`` on exit. diff --git a/bin/tests/system/checkconf/good-doh-global.conf b/bin/tests/system/checkconf/good-doh-global.conf new file mode 100644 index 0000000000..f5eb63477f --- /dev/null +++ b/bin/tests/system/checkconf/good-doh-global.conf @@ -0,0 +1,27 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +http local-http-server { + endpoints { "/dns-query"; }; +}; + +options { + listen-on { 10.53.0.1; }; + http-port 80; + https-port 443; + listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; }; + listen-on port 8080 http local-http-server { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/good-dot-global.conf b/bin/tests/system/checkconf/good-dot-global.conf new file mode 100644 index 0000000000..2a1729752d --- /dev/null +++ b/bin/tests/system/checkconf/good-dot-global.conf @@ -0,0 +1,19 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +options { + listen-on port 853 tls local-tls { 10.53.0.1; }; +}; diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index de998c050e..df88dd1d10 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -668,6 +668,7 @@ copy_setports() { atsign="@" sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \ -e "s/${atsign}TLSPORT${atsign}/${TLSPORT}/g" \ + -e "s/${atsign}HTTPSPORT${atsign}/${HTTPSPORT}/g" \ -e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \ -e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \ -e "s/${atsign}EXTRAPORT3${atsign}/${EXTRAPORT3}/g" \ diff --git a/bin/tests/system/get_ports.sh b/bin/tests/system/get_ports.sh index eac854d65c..f63ba47253 100755 --- a/bin/tests/system/get_ports.sh +++ b/bin/tests/system/get_ports.sh @@ -82,6 +82,7 @@ done echo "export PORT=$(get_port "$baseport")" echo "export TLSPORT=$(get_port)" +echo "export HTTPSPORT=$(get_port)" echo "export EXTRAPORT1=$(get_port)" echo "export EXTRAPORT2=$(get_port)" echo "export EXTRAPORT3=$(get_port)" diff --git a/bin/tests/system/run.sh.in b/bin/tests/system/run.sh.in index e802332c8c..3802181762 100644 --- a/bin/tests/system/run.sh.in +++ b/bin/tests/system/run.sh.in @@ -149,7 +149,7 @@ stop_servers() { echostart "S:$systest:$(date_with_args)" echoinfo "T:$systest:1:A" echoinfo "A:$systest:System test $systest" -echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}" +echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${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." diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index f3599be319..7f304cfc11 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1078,6 +1078,14 @@ default is used. the default is the ``named`` working directory. See :ref:`acl` for details about ``geoip`` ACLs. +.. _https_endpoint: + +``https-endpoint`` + This configures a DNS-over-HTTPS (DoH) service endpoint. It takes a + string which specifies the endpoint URL path, and an ``https-server`` + parameter specifying the server name of an HTTPS listener. (See + :ref:`Link title `.) + ``key-directory`` This is the directory where the public and private DNSSEC key files should be found when performing a dynamic update of secure zones, if different @@ -2436,6 +2444,8 @@ Interfaces The interfaces and ports that the server answers queries from may be specified using the ``listen-on`` and ``listen-on-v6`` options. +specified using the ``listen-on`` and ``listen-on-v6`` options, as +well as the ``https-server`` option for HTTPS queries. ``listen-on`` takes an optional port, an optional TLS configuration identifier, and an ``address_match_list`` of IPv4 addresses. (IPv6 @@ -2488,6 +2498,15 @@ To instruct the server not to listen on any IPv6 addresses, use: listen-on-v6 { none; }; +.. _https_server: + +``https-server`` takes a server name, an optional port, a TLS +configuration identifier, and an ``address_match_list`` of both IPv4 and +IPv6 addresses. This sets up an HTTPS responder using the key and +certificate specified in the referenced ``tls`` statement. The endpoint +for incoming HTTPS queries must be specified using the ``https-endpoint`` +option (see :ref:`Link title `). + .. _query_address: Query Address diff --git a/doc/man/named.8in b/doc/man/named.8in index db3ddd4176..f758454cde 100644 --- a/doc/man/named.8in +++ b/doc/man/named.8in @@ -115,6 +115,8 @@ for queries. If \fBvalue\fP is of the form \fB\fP or \fBportnum\fP; if not not specified, the default is port 53. If \fBvalue\fP is of the form \fBtls=\fP, the server will listen for TLS queries on \fBportnum\fP; the default is 853. +If \fBvalue\fP is of the form \fBhttps=\fP, the server will +listen for HTTPS queries on \fBportnum\fP; the default is 443. .TP .B \fB\-s\fP This option writes memory usage statistics to \fBstdout\fP on exit. diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 7884fd5f28..a26bcf3c0b 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -132,6 +132,19 @@ dyndb string quoted_string { .fi .UNINDENT .UNINDENT +.SS HTTP +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +http string { + endpoints { quoted_string; ... }; +}; +.ft P +.fi +.UNINDENT +.UNINDENT .SS KEY .INDENT 0.0 .INDENT 3.5 @@ -327,6 +340,8 @@ options { glue\-cache boolean;// deprecated heartbeat\-interval integer; hostname ( quoted_string | none ); + http\-port integer; + https\-port integer; inline\-signing boolean; interface\-interval duration; ipv4only\-contact string; @@ -338,10 +353,12 @@ options { key\-directory quoted_string; lame\-ttl duration; listen\-on [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; listen\-on\-v6 [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; lmdb\-mapsize sizeval; lock\-file ( quoted_string | none ); diff --git a/doc/misc/Makefile.am b/doc/misc/Makefile.am index d4db7194a6..e78e108171 100644 --- a/doc/misc/Makefile.am +++ b/doc/misc/Makefile.am @@ -37,7 +37,8 @@ OPTIONS_FILES = \ tls.grammar.rst \ trust-anchors.grammar.rst \ managed-keys.grammar.rst \ - trusted-keys.grammar.rst + trusted-keys.grammar.rst \ + http.grammar.rst EXTRA_DIST = \ $(OPTIONS_FILES) \ @@ -175,4 +176,7 @@ managed-keys.grammar.rst: options.active trusted-keys.grammar.rst: options.active $(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active trusted-keys > $@ +http.grammar.rst: options.active + $(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active http > $@ + endif diff --git a/doc/misc/http.grammar.rst b/doc/misc/http.grammar.rst new file mode 100644 index 0000000000..dcb66a2a47 --- /dev/null +++ b/doc/misc/http.grammar.rst @@ -0,0 +1,5 @@ +:: + + http { + endpoints { ; ... }; + }; diff --git a/doc/misc/options b/doc/misc/options index 1e5bedec9c..39da1adf99 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -42,6 +42,10 @@ dnssec-policy { dyndb { }; // may occur multiple times +http { + endpoints { ; ... }; +}; // may occur multiple times + key { algorithm ; secret ; @@ -193,6 +197,8 @@ options { glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -204,10 +210,12 @@ options { key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/misc/options.active b/doc/misc/options.active index 0e007d6855..9ac0f03351 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -41,6 +41,10 @@ dnssec-policy { dyndb { }; // may occur multiple times +http { + endpoints { ; ... }; +}; // may occur multiple times + key { algorithm ; secret ; @@ -192,6 +196,8 @@ options { glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -203,10 +209,12 @@ options { key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 30aee7b7c9..5176f572bd 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -119,6 +119,8 @@ glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -130,10 +132,12 @@ key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 844902dcf4..ae6b01a7e2 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -52,6 +52,13 @@ New Features 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). [GL !4566] + Removed Features ~~~~~~~~~~~~~~~~ diff --git a/lib/isccfg/Makefile.am b/lib/isccfg/Makefile.am index 3205e3b93b..e13294281b 100644 --- a/lib/isccfg/Makefile.am +++ b/lib/isccfg/Makefile.am @@ -7,17 +7,21 @@ libisccfg_la_HEADERS = \ 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 = \ diff --git a/lib/isccfg/httpconf.c b/lib/isccfg/httpconf.c new file mode 100644 index 0000000000..cd210b53a0 --- /dev/null +++ b/lib/isccfg/httpconf.c @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include + +#include + +#include +#include + +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) { + isc_mem_t *mctx = NULL; + + REQUIRE(storage != NULL); + + 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->count--; + + 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->count--; + } + } + + isc_mem_put(mctx, http, sizeof(*http)); + http = next; + } + } + + INSIST(storage->count == 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->count++; + } + + ISC_LINK_INIT(new, link); + ISC_LIST_PREPEND(storage->list, new, link); + storage->count++; + 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->count == 0) { + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isccfg/include/isccfg/httpconf.h b/lib/isccfg/include/isccfg/httpconf.h new file mode 100644 index 0000000000..bf049b9e6d --- /dev/null +++ b/lib/isccfg/include/isccfg/httpconf.h @@ -0,0 +1,69 @@ +/* + * 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 + +#include +#include +#include +#include + +#include + +#include +#include + +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 count; +} 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 count; +} 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 */ diff --git a/lib/isccfg/include/isccfg/tlsconf.h b/lib/isccfg/include/isccfg/tlsconf.h new file mode 100644 index 0000000000..534236ae65 --- /dev/null +++ b/lib/isccfg/include/isccfg/tlsconf.h @@ -0,0 +1,69 @@ +/* + * 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 + +#include +#include +#include +#include + +#include + +#include + +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 */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index c18196ea5b..e90ab215f8 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -81,6 +81,7 @@ static cfg_type_t cfg_type_bracketed_dscpsockaddrlist; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; static cfg_type_t cfg_type_bracketed_netaddrlist; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist; +static cfg_type_t cfg_type_bracketed_qstring_list; static cfg_type_t cfg_type_controls; static cfg_type_t cfg_type_controls_sockaddr; static cfg_type_t cfg_type_destinationlist; @@ -91,6 +92,7 @@ static cfg_type_t cfg_type_dnstap; static cfg_type_t cfg_type_dnstapoutput; static cfg_type_t cfg_type_dyndb; static cfg_type_t cfg_type_plugin; +static cfg_type_t cfg_type_http_description; static cfg_type_t cfg_type_ixfrdifftype; static cfg_type_t cfg_type_ixfrratio; static cfg_type_t cfg_type_key; @@ -108,6 +110,7 @@ static cfg_type_t cfg_type_optional_allow; static cfg_type_t cfg_type_optional_class; static cfg_type_t cfg_type_optional_dscp; static cfg_type_t cfg_type_optional_facility; +static cfg_type_t cfg_type_optional_http; static cfg_type_t cfg_type_optional_keyref; static cfg_type_t cfg_type_optional_port; static cfg_type_t cfg_type_optional_uint32; @@ -151,6 +154,7 @@ static cfg_tuplefielddef_t listenon_fields[] = { { "port", &cfg_type_optional_port, 0 }, { "dscp", &cfg_type_optional_dscp, 0 }, { "tls", &cfg_type_optional_tls, 0 }, + { "http", &cfg_type_optional_http, 0 }, { "acl", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; @@ -1088,6 +1092,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, { "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI }, + { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT }, { "masters", &cfg_type_primaries, CFG_CLAUSEFLAG_MULTI }, @@ -1221,6 +1226,8 @@ static cfg_clausedef_t options_clauses[] = { { "pid-file", &cfg_type_qstringornone, 0 }, { "port", &cfg_type_uint32, 0 }, { "tls-port", &cfg_type_uint32, 0 }, + { "http-port", &cfg_type_uint32, 0 }, + { "https-port", &cfg_type_uint32, 0 }, { "querylog", &cfg_type_boolean, 0 }, { "random-device", &cfg_type_qstringornone, 0 }, { "recursing-file", &cfg_type_qstring, 0 }, @@ -3853,3 +3860,31 @@ static cfg_type_t cfg_type_optional_tls = { "tlsoptional", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &tls_kw }; + +/* http and https */ + +static cfg_type_t cfg_type_bracketed_qstring_list = { "bracketed_qstring_list", + cfg_parse_bracketed_list, + cfg_print_bracketed_list, + cfg_doc_bracketed_list, + &cfg_rep_list, + &cfg_type_qstring }; + +static cfg_clausedef_t cfg_http_description_clauses[] = { + { "endpoints", &cfg_type_bracketed_qstring_list, 0 }, { NULL, NULL, 0 } +}; + +static cfg_clausedef_t *http_description_clausesets[] = { + cfg_http_description_clauses, NULL +}; + +static cfg_type_t cfg_type_http_description = { + "http_desc", cfg_parse_named_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, http_description_clausesets +}; + +static keyword_type_t http_kw = { "http", &cfg_type_astring }; +static cfg_type_t cfg_type_optional_http = { + "http_optional", parse_optional_keyvalue, print_keyvalue, + doc_optional_keyvalue, &cfg_rep_string, &http_kw +}; diff --git a/lib/isccfg/tlsconf.c b/lib/isccfg/tlsconf.c new file mode 100644 index 0000000000..6320fc5626 --- /dev/null +++ b/lib/isccfg/tlsconf.c @@ -0,0 +1,194 @@ +/* + * 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 + +#include + +#include +#include + +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) { + isc_mem_t *mctx = NULL; + + REQUIRE(storage != NULL); + + 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 = NULL; + 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); + } + INSIST(key_file != NULL); + + if (cfg_map_get(map, "cert-file", &cert_file) != ISC_R_SUCCESS) { + return (ISC_R_FAILURE); + } + INSIST(cert_file != NULL); + + (void)cfg_map_get(map, "dh-param", &dh_param); + (void)cfg_map_get(map, "protocols", &protocols); + (void)cfg_map_get(map, "ciphers", &ciphers); + + new = isc_mem_get(mctx, sizeof(*new)); + *new = (isc_cfg_tls_obj_t){ + .name = isc_mem_strdup(mctx, + cfg_obj_asstring(map->value.map.id)), + .key_file = isc_mem_strdup(mctx, cfg_obj_asstring(key_file)), + .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); + } + INSIST(tls != NULL); + + cfg_tls_storage_clear(storage); + + 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); +} From 931ccd225f119580bd7cc9beb7abe0f02489b72d Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 3 Feb 2021 01:34:15 -0800 Subject: [PATCH 03/11] update ARM with "http" grammar add a link to the http statement grammar and explanations and examples for configuring DoH listeners. --- doc/arm/reference.rst | 151 +++++++++++++++++++++++++++++------------- doc/man/named.8in | 2 + 2 files changed, 106 insertions(+), 47 deletions(-) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 7f304cfc11..fb9545e059 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1078,14 +1078,6 @@ default is used. the default is the ``named`` working directory. See :ref:`acl` for details about ``geoip`` ACLs. -.. _https_endpoint: - -``https-endpoint`` - This configures a DNS-over-HTTPS (DoH) service endpoint. It takes a - string which specifies the endpoint URL path, and an ``https-server`` - parameter specifying the server name of an HTTPS listener. (See - :ref:`Link title `.) - ``key-directory`` This is the directory where the public and private DNSSEC key files should be found when performing a dynamic update of secure zones, if different @@ -1264,6 +1256,20 @@ default is used. for server testing; a server using a port other than 53 is not able to communicate with the global DNS. +``tls-port`` + This is the TCP port number the server uses to receive and send + DNS-over-TLS protocol traffic. The default is 853. + +``https-port`` + This is the TCP port number the server uses to receive and send + DNS-over-HTTPS protocol traffic. The default is 443. + +``http-port`` + This is the TCP port number the server uses to receive and send + unencrypted DNS traffic via HTTP (a configuration that may be useful + when encryption is handled by third-party software or by a reverse + proxy). + ``dscp`` This is the global Differentiated Services Code Point (DSCP) value to classify outgoing DNS traffic, on operating systems that support DSCP. @@ -2442,19 +2448,36 @@ for details on how to specify IP address lists. Interfaces ^^^^^^^^^^ -The interfaces and ports that the server answers queries from may be -specified using the ``listen-on`` and ``listen-on-v6`` options. -specified using the ``listen-on`` and ``listen-on-v6`` options, as -well as the ``https-server`` option for HTTPS queries. +The interfaces, ports, and protocols that the server can use to answer +queries may be specified using the ``listen-on`` and ``listen-on-v6`` options. -``listen-on`` takes an optional port, an optional TLS configuration -identifier, and an ``address_match_list`` of IPv4 addresses. (IPv6 -addresses are ignored, with a logged warning.) The server listens on all -interfaces allowed by the address match list. If a TLS configuration is -specified, ``named`` will listen for DNS-over-TLS (DoT) connections, using -the key and certificate specified in the referenced ``tls`` statement. If a -port number is not specified, the default is 53 for standard DNS and 853 -for DNS-over-TLS. +``listen-on`` and ``listen-on-v6`` statements can each take an optional +port, TLS configuration identifier, and/or HTTP configuration identifier, +in addition to an ``address_match_list``. + +The ``address_match_list`` in ``listen-on`` specifies the IPv4 addresses +on which the server will listen. (IPv6 addresses are ignored, with a +logged warning.) The server listens on all interfaces allowed by the +address match list. If no ``listen-on`` is specified, the default is +to listen for standard DNS queries on port 53 of all IPv4 interfaces. + +``listen-on-v6`` takes an ``address_match_list`` of IPv6 addresses. +The server listens on all interfaces allowed by the address match list. +If no ``listen-on-v6`` is specified, the default is to listen for standard +DNS queries on port 53 of all IPv6 interfaces. + +If a TLS configuration is specified, ``named`` will listen for DNS-over-TLS +(DoT) connections, using the key and certificate specified in the +referenced ``tls`` statement. + +If an HTTP configuration is specified, ``named`` will listen for +DNS-over-HTTPS (DoH) connections using the HTTP endpoint specified in the +referenced ``http`` statement. Normally, ``http`` and ``tls`` +configurations will be used together, but ``tls`` may be omitted if +encryption is being handled by external software. + +If a port number is not specified, the default is 53 for standard DNS, 853 +for DNS-over-TLS, and 443 for DNS-over-HTTPS. Multiple ``listen-on`` statements are allowed. For example: @@ -2463,20 +2486,17 @@ Multiple ``listen-on`` statements are allowed. For example: listen-on { 5.6.7.8; }; listen-on port 1234 { !1.2.3.4; 1.2/16; }; listen-on port 8853 tls ephemeral { 4.3.2.1; }; + listen-on port 8453 tls ephemeral http myserver { 8.7.6.5; }; -enables the name server to listen for standard DNS queries on port 53 of the -IP address 5.6.7.8 and on port 1234 of an address on the machine in net 1.2 -that is not 1.2.3.4, and to listen for DNS-over-TLS connections on port -8853 of the IP address 4.3.2.1, using an ephemeral TLS key and certificate -created for the currently running ``named`` process. - -If no ``listen-on`` is specified, the server listens for standard DNS -on port 53 of all IPv4 interfaces. - -The ``listen-on-v6`` option is used to specify the interfaces and the ports -on which the server listens for incoming queries sent using IPv6. If not -specified, the server listens for standard DNS queries on port 53 of all -IPv6 interfaces. +The first two lines instruct the name server to listen for standard DNS +queries on port 53 of the IP address 5.6.7.8 and on port 1234 of an address +on the machine in net 1.2 that is not 1.2.3.4. The third line instructs the +server to listen for DNS-over-TLS connections on port 8853 of the IP +address 4.3.2.1 using an ephemeral TLS key and certificate created for the +currently running ``named`` process. The fourth line enables DNS-over-HTTPS +connections on port 8453 of address 8.7.6.5, using the same ephemeral +key and certificate, and the HTTP endpoint or endpoints configured in +an ``http`` statement with the name ``myserver``. Multiple ``listen-on-v6`` options can be used. For example: @@ -2485,12 +2505,19 @@ Multiple ``listen-on-v6`` options can be used. For example: listen-on-v6 { any; }; listen-on-v6 port 1234 { !2001:db8::/32; any; }; listen-on port 8853 tls example-tls { 2001:db8::100; }; + listen-on port 8453 tls example-tls http myserver { 2001:db8::100; }; + listen-on port 8000 http myserver { 2001:db8::100; }; -enables the name server to listen for standard DNS queries on port 53 of -any IPv6 addresses and on port 1234 of IPv6 addresses that are not in the -prefix 2001:db8::/32, and for DNS-over-TLS connections on port 8853 of -the address 2001:db8::100, using a TLS key and certificate specified in -the a ``tls`` statement with the name ``example-tls``. +The first two lines instruct the name server to listen for standard DNS +queries on port 53 of any IPv6 addresses, and on port 1234 of IPv6 +addresses that are not in the prefix 2001:db8::/32. The third line +instructs the server to listen for for DNS-over-TLS connections on port +8853 of the address 2001:db8::100, using a TLS key and certificate specified +in the a ``tls`` statement with the name ``example-tls``. The fourth +instructs the server to listen for DNS-over-HTTPS connections, again using +``example-tls``, on the HTTP endpoint specified in ``http myserver``. The +fifth line, in which the ``tls`` parameter is omitted, instructs the server +to listen for *unencrypted* DNS queries over HTTP. To instruct the server not to listen on any IPv6 addresses, use: @@ -2498,15 +2525,6 @@ To instruct the server not to listen on any IPv6 addresses, use: listen-on-v6 { none; }; -.. _https_server: - -``https-server`` takes a server name, an optional port, a TLS -configuration identifier, and an ``address_match_list`` of both IPv4 and -IPv6 addresses. This sets up an HTTPS responder using the key and -certificate specified in the referenced ``tls`` statement. The endpoint -for incoming HTTPS queries must be specified using the ``https-endpoint`` -option (see :ref:`Link title `). - .. _query_address: Query Address @@ -4643,6 +4661,45 @@ The following options can be specified in a ``tls`` statement: The built-in ``ephemeral`` TLS connection object represents a temporary key and certificate created for the current ``named`` session only. +.. _http: + +``http`` Statement Grammar +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: ../misc/http.grammar.rst + +``http`` Statement Definition and Usage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``http`` statement is used to configure HTTP endpoints on which +to listen for DNS-over-HTTPS (DoH) queries. This configuration can +then be referenced by a ``listen-on`` or ``listen-on-v6`` statement to +cause ``named`` to listen for incoming requests over HTTPS. + +``http`` can only be set at the top level of ``named.conf``. + +The following options can be specified in an ``http`` statement: + + ``endpoints`` + A list of HTTP query paths on which to listen. A typical path + is "/dns-query". + +for example, the following configuration enables DNS-over-HTTPS queries on +all local addresses: + +:: + + http local { + endpoints { "/dns-query"; }; + }; + + options { + .... + listen-on tls ephemeral http local { any; }; + listen-on-v6 tls ephemeral http local { any; }; + }; + + .. _trust_anchors: ``trust-anchors`` Statement Grammar diff --git a/doc/man/named.8in b/doc/man/named.8in index f758454cde..c5d81eac5c 100644 --- a/doc/man/named.8in +++ b/doc/man/named.8in @@ -117,6 +117,8 @@ for queries. If \fBvalue\fP is of the form \fB\fP or listen for TLS queries on \fBportnum\fP; the default is 853. If \fBvalue\fP is of the form \fBhttps=\fP, the server will listen for HTTPS queries on \fBportnum\fP; the default is 443. +If \fBvalue\fP is of the form \fBhttp=\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. From cdf9d21731b14ab6967be71d1b527554573dd618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Kr=C4=99cicki?= Date: Mon, 26 Oct 2020 14:17:05 +0100 Subject: [PATCH 04/11] Add isc_mem_strndup() function This commit adds an implementation of strndup() function which allocates memory from the supplied isc_mem_t memory context. --- lib/isc/include/isc/mem.h | 7 ++++++- lib/isc/mem.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/isc/include/isc/mem.h b/lib/isc/include/isc/mem.h index 97b249cc65..2aee5fb00c 100644 --- a/lib/isc/include/isc/mem.h +++ b/lib/isc/include/isc/mem.h @@ -172,6 +172,8 @@ typedef struct isc_memmethods { void *(*memreallocate)(isc_mem_t *mctx, void *ptr, size_t size _ISC_MEM_FLARG); char *(*memstrdup)(isc_mem_t *mctx, const char *s _ISC_MEM_FLARG); + char *(*memstrndup)(isc_mem_t *mctx, const char *s, + size_t size _ISC_MEM_FLARG); void (*memfree)(isc_mem_t *mctx, void *ptr _ISC_MEM_FLARG); } isc_memmethods_t; @@ -226,7 +228,9 @@ struct isc_mempool { #define isc_mem_reallocate(c, p, s) \ ISCMEMFUNC(reallocate)((c), (p), (s)_ISC_MEM_FILELINE) #define isc_mem_strdup(c, p) ISCMEMFUNC(strdup)((c), (p)_ISC_MEM_FILELINE) -#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c)_ISC_MEM_FILELINE) +#define isc_mem_strndup(c, p, l) \ + ISCMEMFUNC(strndup)((c), (p), (l)_ISC_MEM_FILELINE) +#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c)_ISC_MEM_FILELINE) #define isc_mem_put(c, p, s) \ do { \ @@ -596,6 +600,7 @@ void *ISCMEMFUNC(allocate)(isc_mem_t *, size_t _ISC_MEM_FLARG); void *ISCMEMFUNC(reallocate)(isc_mem_t *, void *, size_t _ISC_MEM_FLARG); void ISCMEMFUNC(free)(isc_mem_t *, void *_ISC_MEM_FLARG); char *ISCMEMFUNC(strdup)(isc_mem_t *, const char *_ISC_MEM_FLARG); +char *ISCMEMFUNC(strndup)(isc_mem_t *, const char *, size_t _ISC_MEM_FLARG); void *ISCMEMPOOLFUNC(get)(isc_mempool_t *_ISC_MEM_FLARG); void ISCMEMPOOLFUNC(put)(isc_mempool_t *, void *_ISC_MEM_FLARG); diff --git a/lib/isc/mem.c b/lib/isc/mem.c index af3d57f6f8..879b6232f9 100644 --- a/lib/isc/mem.c +++ b/lib/isc/mem.c @@ -244,13 +244,15 @@ static void * isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG); static char * isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG); +static char * +isc___mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG); static void isc___mem_free(isc_mem_t *ctx, void *ptr FLARG); static isc_memmethods_t memmethods = { isc___mem_get, isc___mem_put, isc___mem_putanddetach, isc___mem_allocate, isc___mem_reallocate, isc___mem_strdup, - isc___mem_free, + isc___mem_strndup, isc___mem_free, }; #if ISC_MEM_TRACKLINES @@ -1439,6 +1441,29 @@ isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) { return (ns); } +char * +isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG) { + REQUIRE(VALID_CONTEXT(mctx0)); + REQUIRE(s != NULL); + + isc__mem_t *mctx = (isc__mem_t *)mctx0; + size_t len; + char *ns; + + len = strlen(s) + 1; + if (len > size) { + len = size; + } + + ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS); + + if (ns != NULL) { + strlcpy(ns, s, len); + } + + return (ns); +} + void isc_mem_setdestroycheck(isc_mem_t *ctx0, bool flag) { REQUIRE(VALID_CONTEXT(ctx0)); @@ -2467,6 +2492,13 @@ isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { return (mctx->methods->memstrdup(mctx, s FLARG_PASS)); } +char * +isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + return (mctx->methods->memstrndup(mctx, s, size FLARG_PASS)); +} + void isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) { REQUIRE(ISCAPI_MCTX_VALID(mctx)); From 7a9608136056f7cbe97a0a7888e7a2dcb2343f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Kr=C4=99cicki?= Date: Sat, 31 Oct 2020 20:42:18 +0100 Subject: [PATCH 05/11] nghttp2-based HTTP layer in netmgr This commit includes work-in-progress implementation of DNS-over-HTTP(S). Server-side code remains mostly untested, and there is only support for POST requests. --- .gitlab-ci.yml | 1 + COPYRIGHT | 22 + bin/win32/BINDInstall/BINDInstallDlg.cpp | 3 +- configure.ac | 11 +- lib/isc/Makefile.am | 3 + lib/isc/include/isc/netmgr.h | 39 + lib/isc/include/isc/url.h | 80 ++ lib/isc/netmgr/http.c | 1077 ++++++++++++++++++++++ lib/isc/netmgr/netmgr-int.h | 57 +- lib/isc/netmgr/netmgr.c | 3 + lib/isc/netmgr/tcp.c | 4 +- lib/isc/netmgr/tlsstream.c | 6 +- lib/isc/netmgr/udp.c | 4 +- lib/isc/url.c | 667 ++++++++++++++ lib/isc/win32/libisc.def.in | 2 + lib/isc/win32/libisc.vcxproj.filters.in | 6 + lib/isc/win32/libisc.vcxproj.in | 27 +- util/copyrights | 3 + win32utils/Configure | 73 ++ 19 files changed, 2061 insertions(+), 27 deletions(-) create mode 100644 lib/isc/include/isc/url.h create mode 100644 lib/isc/netmgr/http.c create mode 100644 lib/isc/url.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6412ea52ae..d6156b99e0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -285,6 +285,7 @@ stages: "with-openssl=C:/OpenSSL" "with-libxml2=C:/libxml2" "with-libuv=C:/libuv" + "with-nghttp2=C:/nghttp2" "without-python" "with-system-tests" x64' diff --git a/COPYRIGHT b/COPYRIGHT index 830bcaa129..ecb9bc38ea 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -367,3 +367,25 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +----------------------------------------------------------------------------- + +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/bin/win32/BINDInstall/BINDInstallDlg.cpp b/bin/win32/BINDInstall/BINDInstallDlg.cpp index 06db516425..b4711ed180 100644 --- a/bin/win32/BINDInstall/BINDInstallDlg.cpp +++ b/bin/win32/BINDInstall/BINDInstallDlg.cpp @@ -138,7 +138,8 @@ const FileData installFiles[] = {"libdns.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, {"libirs.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, {"libeay32.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, - {"libuv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, + {"nghttp2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, + {"uv.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, #ifdef HAVE_LIBXML2 {"libxml2.dll", FileData::BinDir, FileData::Critical, FALSE, TRUE}, #endif diff --git a/configure.ac b/configure.ac index 2944b3ca44..1ff23d73c1 100644 --- a/configure.ac +++ b/configure.ac @@ -124,7 +124,7 @@ AS_IF([test "$enable_static" != "no" && test "$enable_developer" != "yes"], # # Set the default CFLAGS and CPPFLAGS # -STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wcast-qual -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow" +STD_CFLAGS="-Wall -Wextra -Wwrite-strings -Wpointer-arith -Wno-missing-field-initializers -Wformat -Wshadow" # These should be always errors STD_CFLAGS="$STD_CFLAGS -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=format-security -Werror=parentheses -Werror=implicit -Werror=strict-prototypes" @@ -574,6 +574,15 @@ LIBS="$LIBS $LIBUV_LIBS" AC_CHECK_FUNCS([uv_handle_get_data uv_handle_set_data uv_import uv_udp_connect uv_translate_sys_error]) AX_RESTORE_FLAGS([libuv]) +# libnghttp2 +AC_MSG_CHECKING([for libnghttp2]) +PKG_CHECK_MODULES([LIBNGHTTP2], [libnghttp2 >= 1.6.0], [], + [AC_MSG_ERROR([libnghttp2 not found])]) +AX_SAVE_FLAGS([libnghttp2]) + +CFLAGS="$CFLAGS $LIBNGHTTP2_CFLAGS" +LIBS="$LIBS $LIBNGHTTP2_LIBS" + # # flockfile is usually provided by pthreads # diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index c84832b294..48ead7c17f 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -90,6 +90,7 @@ libisc_la_HEADERS = \ include/isc/tls.h \ include/isc/tm.h \ include/isc/types.h \ + include/isc/url.h \ include/isc/utf8.h \ include/isc/util.h \ pthreads/include/isc/condition.h\ @@ -124,6 +125,7 @@ libisc_la_SOURCES = \ $(libisc_la_HEADERS) \ $(pk11_HEADERS) \ $(pkcs11_HEADERS) \ + netmgr/http.c \ netmgr/netmgr-int.h \ netmgr/netmgr.c \ netmgr/tcp.c \ @@ -150,6 +152,7 @@ libisc_la_SOURCES = \ unix/stdtime.c \ unix/syslog.c \ unix/time.c \ + url.c \ pk11.c \ pk11_result.c \ aes.c \ diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 0f25e86819..d2e4ef4c0a 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -505,3 +505,42 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, * The connected socket can only be accessed via the handle passed to * 'cb'. */ + +typedef void (*isc_nm_http_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult, + isc_region_t *postdata, isc_region_t *getdata, + 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 + * 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_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_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_result_t +isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri, + isc_nm_http_cb_t cb, void *cbarg, + size_t extrahandlesize); + +isc_result_t +isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri, + isc_nm_recv_cb_t cb, void *cbarg, + size_t extrahandlesize); diff --git a/lib/isc/include/isc/url.h b/lib/isc/include/isc/url.h new file mode 100644 index 0000000000..9dd95245c3 --- /dev/null +++ b/lib/isc/include/isc/url.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include + +/* + * Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +#define HTTP_PARSER_STRICT 1 +#endif + +typedef enum { + ISC_UF_SCHEMA = 0, + ISC_UF_HOST = 1, + ISC_UF_PORT = 2, + ISC_UF_PATH = 3, + ISC_UF_QUERY = 4, + ISC_UF_FRAGMENT = 5, + ISC_UF_USERINFO = 6, + ISC_UF_MAX = 7 +} isc_url_field_t; + +/* Result structure for isc_url_parse(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +typedef struct { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[ISC_UF_MAX]; +} isc_url_parser_t; + +isc_result_t +isc_url_parse(const char *buf, size_t buflen, bool is_connect, + isc_url_parser_t *up); +/*%< + * Parse a URL; return nonzero on failure + */ diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c new file mode 100644 index 0000000000..f11f4dfea9 --- /dev/null +++ b/lib/isc/netmgr/http.c @@ -0,0 +1,1077 @@ +/* + * 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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "netmgr-int.h" + +#define AUTHEXTRA 7 + +typedef struct http2_client_stream { + isc_nm_recv_cb_t cb; + void *cbarg; + + char *uri; + isc_url_parser_t up; + + char *authority; + size_t authoritylen; + char *path; + + uint8_t rbuf[65535]; + size_t rbufsize; + + size_t pathlen; + int32_t stream_id; + isc_region_t *postdata; + size_t postdata_pos; +} http2_client_stream_t; + +#define HTTP2_SESSION_MAGIC ISC_MAGIC('H', '2', 'S', 'S') +#define VALID_HTTP2_SESSION(t) ISC_MAGIC_VALID(t, HTTP2_SESSION_MAGIC) + +struct isc_nm_http2_session { + unsigned int magic; + isc_mem_t *mctx; + bool sending; + bool closed; + bool reading; + + nghttp2_session *ngsession; + http2_client_stream_t *cstream; + ISC_LIST(isc_nmsocket_h2_t) sstreams; + + isc_nmhandle_t *handle; + isc_nmsocket_t *serversocket; + + isc_region_t r; + uint8_t buf[65535]; + size_t bufsize; + + SSL_CTX *ctx; +}; + +static bool +http2_do_bio(isc_nm_http2_session_t *session); + +static isc_result_t +get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp, + const char *uri, uint16_t *port) { + http2_client_stream_t *stream = NULL; + int rv; + + 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->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)); + isc_mem_free(mctx, stream->uri); + return (ISC_R_FAILURE); + } + + stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len; + stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA); + memmove(stream->authority, &uri[stream->up.field_data[ISC_UF_HOST].off], + stream->up.field_data[ISC_UF_HOST].len); + + if (stream->up.field_set & (1 << ISC_UF_PORT)) { + stream->authoritylen += (size_t)snprintf( + stream->authority + + stream->up.field_data[ISC_UF_HOST].len, + AUTHEXTRA, ":%u", stream->up.port); + } + + /* If we don't have path in URI, we use "/" as path. */ + stream->pathlen = 1; + if (stream->up.field_set & (1 << ISC_UF_PATH)) { + stream->pathlen = stream->up.field_data[ISC_UF_PATH].len; + } + if (stream->up.field_set & (1 << ISC_UF_QUERY)) { + /* +1 for '?' character */ + stream->pathlen += + (size_t)(stream->up.field_data[ISC_UF_QUERY].len + 1); + } + + stream->path = isc_mem_get(mctx, stream->pathlen); + if (stream->up.field_set & (1 << ISC_UF_PATH)) { + memmove(stream->path, + &uri[stream->up.field_data[ISC_UF_PATH].off], + stream->up.field_data[ISC_UF_PATH].len); + } else { + stream->path[0] = '/'; + } + + if (stream->up.field_set & (1 << ISC_UF_QUERY)) { + stream->path[stream->pathlen - + stream->up.field_data[ISC_UF_QUERY].len - 1] = '?'; + memmove(stream->path + stream->pathlen - + stream->up.field_data[ISC_UF_QUERY].len, + &uri[stream->up.field_data[ISC_UF_QUERY].off], + stream->up.field_data[ISC_UF_QUERY].len); + } + + *port = 443; + if ((stream->up.field_set & (1 << ISC_UF_PORT)) != 0) { + *port = stream->up.port; + } + + *streamp = stream; + + return (ISC_R_SUCCESS); +} + +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, sizeof(http2_client_stream_t)); +} + +static void +delete_http2_session(isc_nm_http2_session_t *session) { + if (session->handle != NULL) { + isc_nm_pauseread(session->handle); + isc_nmhandle_detach(&session->handle); + } + if (session->ngsession != NULL) { + nghttp2_session_del(session->ngsession); + session->ngsession = NULL; + } + if (session->cstream != NULL) { + put_http2_client_stream(session->mctx, session->cstream); + session->cstream = NULL; + } + + /* + * There might be leftover callbacks waiting to be received + */ + 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)); + } +} + +static int +on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags, + int32_t stream_id, const uint8_t *data, size_t len, + void *user_data) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data; + + 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; + } + } 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; + break; + } + sock_h2 = ISC_LIST_NEXT(sock_h2, link); + } + } + + return (0); +} + +static int +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; + + 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); + } + } + } else { + /* XXX */ + } + + /* 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. + */ +static int +select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + UNUSED(ssl); + UNUSED(arg); + + if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { + /* TODO */ + } + return (SSL_TLSEXT_ERR_OK); +} +#endif /* !OPENSSL_NO_NEXTPROTONEG */ + +/* Create SSL_CTX. */ +static SSL_CTX * +create_ssl_ctx(void) { + SSL_CTX *ssl_ctx = NULL; + + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + 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); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ + + return (ssl_ctx); +} + +static void +initialize_nghttp2_client_session(isc_nm_http2_session_t *session) { + nghttp2_session_callbacks *callbacks = NULL; + + nghttp2_session_callbacks_new(&callbacks); + + 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_client_new(&session->ngsession, callbacks, session); + + nghttp2_session_callbacks_del(callbacks); +} + +static void +send_client_connection_header(isc_nm_http2_session_t *session) { + nghttp2_settings_entry iv[1] = { + { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 } + }; + int rv; + + rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv, + 1); + if (rv != 0) { + /* TODO */ + } + + http2_do_bio(session); +} + +#define MAKE_NV(NAME, VALUE, VALUELEN) \ + { \ + (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \ + sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE \ + } + +#define MAKE_NV2(NAME, VALUE) \ + { \ + (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \ + sizeof(NAME) - 1, sizeof(VALUE) - 1, \ + NGHTTP2_NV_FLAG_NONE \ + } + +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) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data; + + REQUIRE(session->cstream != NULL); + + UNUSED(ngsession); + UNUSED(source); + + if (session->cstream->stream_id == stream_id) { + size_t len = session->cstream->postdata->length - + session->cstream->postdata_pos; + + if (len > length) { + len = length; + } + + memmove(buf, + session->cstream->postdata->base + + session->cstream->postdata_pos, + len); + session->cstream->postdata_pos += len; + + if (session->cstream->postdata_pos == + session->cstream->postdata->length) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return (len); + } + + return (0); +} + +/* Send HTTP request to the remote peer */ +static isc_result_t +client_submit_request(isc_nm_http2_session_t *session) { + 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_id < 0) { + return (ISC_R_FAILURE); + } + + stream->stream_id = stream_id; + http2_do_bio(session); + + return (ISC_R_SUCCESS); +} + +/* + * Read callback from TLS socket. + */ +static void +https_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, + void *data) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)data; + ssize_t readlen; + + REQUIRE(VALID_HTTP2_SESSION(session)); + + UNUSED(handle); + UNUSED(result); + + if (result != ISC_R_SUCCESS) { + session->reading = false; + delete_http2_session(session); + /* TODO callback! */ + return; + } + + readlen = nghttp2_session_mem_recv(session->ngsession, region->base, + region->length); + if (readlen < 0) { + delete_http2_session(session); + /* TODO callback! */ + return; + } + + if (readlen < region->length) { + INSIST(session->bufsize == 0); + INSIST(region->length - readlen < 65535); + memmove(session->buf, region->base, region->length - readlen); + session->bufsize = region->length - readlen; + isc_nm_pauseread(session->handle); + } + + /* We might have something to receive or send, do IO */ + http2_do_bio(session); +} + +static void +https_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)arg; + + 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); +} + +static bool +http2_do_bio(isc_nm_http2_session_t *session) { + REQUIRE(VALID_HTTP2_SESSION(session)); + + if (session->closed || + (nghttp2_session_want_read(session->ngsession) == 0 && + nghttp2_session_want_write(session->ngsession) == 0)) + { + delete_http2_session(session); + return (false); + } + + if (nghttp2_session_want_read(session->ngsession) != 0) { + if (!session->reading) { + /* We have not yet started reading from this handle */ + isc_nm_read(session->handle, https_readcb, session); + session->reading = true; + } else if (session->bufsize > 0) { + /* Leftover data in the buffer, use it */ + size_t readlen = nghttp2_session_mem_recv( + session->ngsession, session->buf, + session->bufsize); + + if (readlen == session->bufsize) { + session->bufsize = 0; + } else { + memmove(session->buf, session->buf + readlen, + session->bufsize - readlen); + session->bufsize -= readlen; + } + + http2_do_bio(session); + return (false); + } else { + /* Resume reading, it's idempotent, wait for more */ + isc_nm_resumeread(session->handle); + } + } else { + /* We don't want more data, stop reading for now */ + isc_nm_pauseread(session->handle); + } + + if (!session->sending && + nghttp2_session_want_write(session->ngsession) != 0) { + const uint8_t *data = NULL; + size_t sz; + + /* + * XXXWPK TODO + * This function may produce a very small byte string. If + * that is the case, and application disables Nagle + * algorithm (``TCP_NODELAY``), then writing this small + * chunk leads to a very small packet, and it is very + * inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid + * this situation. + */ + sz = nghttp2_session_mem_send(session->ngsession, &data); + INSIST(session->r.base == NULL); + session->r.base = isc_mem_get(session->mctx, sz); + session->r.length = sz; + memmove(session->r.base, data, sz); + session->sending = true; + isc_nm_send(session->handle, &session->r, https_writecb, + session); + return (true); + } + + return (false); +} + +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; + + if (result != ISC_R_SUCCESS) { + delete_http2_session(session); + return; + } + + isc_nmhandle_attach(handle, &session->handle); + +#if 0 +/* TODO H2 */ +#ifndef OPENSSL_NO_NEXTPROTONEG + SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + 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; + } +#endif + + initialize_nghttp2_client_session(session); + send_client_connection_header(session); + client_submit_request(session); + http2_do_bio(session); +} + +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) { + REQUIRE(VALID_NM(mgr)); + + UNUSED(local); + UNUSED(peer); + UNUSED(uri); + UNUSED(cb); + UNUSED(cbarg); + UNUSED(timeout); + UNUSED(extrahandlesize); + + return (ISC_R_NOTIMPLEMENTED); +} + +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; + http2_client_stream_t *cstream = NULL; + struct addrinfo hints; + struct addrinfo *res = NULL; + isc_sockaddr_t local, peer; + isc_result_t result; + int s; + + if (ctx == NULL) { + ctx = create_ssl_ctx(); + } + + 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); + + result = get_http2_client_stream(mgr->mctx, &cstream, uri, &port); + if (result != ISC_R_SUCCESS) { + delete_http2_session(session); + return (result); + } + + cstream->postdata = region; + cstream->postdata_pos = 0; + cstream->cb = cb; + cstream->cbarg = cbarg; + + session->cstream = cstream; + +#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); + } +#endif /* WIN32 */ + + isc_sockaddr_fromsockaddr(&peer, res->ai_addr); + isc_sockaddr_setport(&peer, port); + isc_sockaddr_anyofpf(&local, res->ai_family); + + freeaddrinfo(res); + + 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 */ + if (result != ISC_R_SUCCESS) { + return (result); + } + + return (ISC_R_SUCCESS); +} + +static int +server_on_begin_headers_callback(nghttp2_session *ngsession, + const nghttp2_frame *frame, void *user_data) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data; + isc_nmsocket_t *socket = NULL; + isc_sockaddr_t iface; + + if (frame->hd.type != NGHTTP2_HEADERS || + frame->headers.cat != NGHTTP2_HCAT_REQUEST) + { + return (0); + } + + socket = isc_mem_get(session->mctx, sizeof(isc_nmsocket_t)); + 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 }; + + ISC_LINK_INIT(&socket->h2, link); + ISC_LIST_APPEND(session->sstreams, &socket->h2, link); + 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"; + + 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 || socket->h2.request_path != NULL) { + break; + } + + 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); + } + } + break; + } + + return (0); +} + +static ssize_t +server_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; + isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr; + size_t buflen; + + REQUIRE(socket->h2.stream_id == stream_id); + + UNUSED(ngsession); + UNUSED(session); + + buflen = socket->h2.bufsize - socket->h2.bufpos; + if (buflen > length) { + buflen = length; + } + + memmove(buf, socket->h2.buf + socket->h2.bufpos, buflen); + socket->h2.bufpos += buflen; + if (socket->h2.bufpos == socket->h2.bufsize) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + + return (buflen); +} + +static int +server_send_response(nghttp2_session *ngsession, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + isc_nmsocket_t *socket) { + int rv; + nghttp2_data_provider data_prd; + data_prd.source.ptr = socket; + data_prd.read_callback = server_read_callback; + + rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen, + &data_prd); + if (rv != 0) { + return (-1); + } + return (0); +} + +static const char ERROR_HTML[] = "404" + "

404 Not Found

"; + +static int +error_reply(nghttp2_session *ngsession, isc_nmsocket_t *socket) { + const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "404") }; + + memmove(socket->h2.buf, ERROR_HTML, sizeof(ERROR_HTML)); + socket->h2.bufsize = sizeof(ERROR_HTML); + socket->h2.bufpos = 0; + + server_send_response(ngsession, socket->h2.stream_id, hdrs, + sizeof(hdrs) / sizeof(nghttp2_nv), socket); + return (0); +} + +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); + } + + 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 (handler == NULL) { + if (error_reply(ngsession, socket) != 0) { + return (NGHTTP2_ERR_CALLBACK_FAILURE); + } + return (0); + } + + 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); + 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; + + /* TODO FIXME do it asynchronously!!! */ + memcpy(sock->h2.buf, region->base, region->length); + sock->h2.bufsize = region->length; + 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); + } else { + cb(handle, ISC_R_SUCCESS, cbarg); + } +} + +static int +server_on_frame_recv_callback(nghttp2_session *ngsession, + const nghttp2_frame *frame, void *user_data) { + isc_nm_http2_session_t *session = (isc_nm_http2_session_t *)user_data; + isc_nmsocket_t *socket = NULL; + + switch (frame->hd.type) { + case NGHTTP2_DATA: + case NGHTTP2_HEADERS: + /* Check that the client request has finished */ + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + socket = nghttp2_session_get_stream_user_data( + ngsession, frame->hd.stream_id); + + /* + * For DATA and HEADERS frame, this callback may be + * called after on_stream_close_callback. Check that + * the stream is still alive. + */ + if (socket == NULL) { + return (0); + } + + return (server_on_request_recv(ngsession, session, + socket)); + } + break; + default: + break; + } + return (0); +} + +static void +initialize_nghttp2_server_session(isc_nm_http2_session_t *session) { + nghttp2_session_callbacks *callbacks = NULL; + + nghttp2_session_callbacks_new(&callbacks); + + 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, server_on_header_callback); + + nghttp2_session_callbacks_set_on_begin_headers_callback( + callbacks, server_on_begin_headers_callback); + + nghttp2_session_callbacks_set_on_frame_recv_callback( + callbacks, server_on_frame_recv_callback); + + nghttp2_session_server_new(&session->ngsession, callbacks, session); + + nghttp2_session_callbacks_del(callbacks); +} + +static int +server_send_connection_header(isc_nm_http2_session_t *session) { + nghttp2_settings_entry iv[1] = { + { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 } + }; + int rv; + + rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv, + 1); + if (rv != 0) { + return (-1); + } + return (0); +} + +static isc_result_t +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; + + if (result != ISC_R_SUCCESS) { + /* XXXWPK do nothing? */ + return (result); + } + + session = isc_mem_get(httplistensock->mgr->mctx, + sizeof(isc_nm_http2_session_t)); + *session = (isc_nm_http2_session_t){ .magic = HTTP2_SESSION_MAGIC }; + initialize_nghttp2_server_session(session); + + isc_mem_attach(httplistensock->mgr->mctx, &session->mctx); + isc_nmhandle_attach(handle, &session->handle); + isc__nmsocket_attach(httplistensock, &session->serversocket); + server_send_connection_header(session); + + /* TODO H2 */ + http2_do_bio(session); + return (ISC_R_SUCCESS); +} + +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_nmsocket_t *sock = NULL; + isc_result_t result; + + isc_mem_get(mgr->mctx, sizeof(*sock)); + isc__nmsocket_init(sock, mgr, isc_nm_httplistener, iface); + + if (ctx != NULL) { + result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock, + sizeof(isc_nm_http2_session_t), + backlog, quota, ctx, &sock->outer); + } else { + result = isc_nm_listentcp(mgr, iface, httplisten_acceptcb, sock, + sizeof(isc_nm_http2_session_t), + backlog, quota, &sock->outer); + } + + if (result != ISC_R_SUCCESS) { + atomic_store(&sock->closed, true); + isc__nmsocket_detach(&sock); + return (result); + } + + atomic_store(&sock->listening, true); + *sockp = sock; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri, + isc_nm_http_cb_t cb, void *cbarg, + size_t extrahandlesize) { + isc_nm_http2_server_handler_t *handler = NULL; + + 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); + + return (ISC_R_SUCCESS); +} + +typedef struct { + isc_nm_recv_cb_t cb; + void *cbarg; +} cbarg_t; + +static unsigned char doh_error[] = + "No request" + "

No request

"; + +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; + + 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); + /* 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_result_t +isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri, + isc_nm_recv_cb_t cb, void *cbarg, + size_t extrahandlesize) { + isc_result_t result; + 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 }; + + 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)); + } + + return (result); +} diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index d3c026cf0c..3ad09823bf 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -155,6 +155,8 @@ isc__nm_dump_active(isc_nm_t *nm); #define isc__nmsocket_prep_destroy(sock) isc___nmsocket_prep_destroy(sock) #endif +typedef struct isc_nm_http2_session isc_nm_http2_session_t; + /* * Single network event loop worker. */ @@ -207,6 +209,8 @@ struct isc_nmhandle { isc_nmsocket_t *sock; size_t ah_pos; /* Position in the socket's 'active handles' array */ + isc_nm_http2_session_t *httpsession; + isc_sockaddr_t peer; isc_sockaddr_t local; isc_nm_opaquecb_t doreset; /* reset extra callback, external */ @@ -293,11 +297,22 @@ typedef enum isc__netievent_type { typedef union { isc_nm_recv_cb_t recv; + isc_nm_http_cb_t http; isc_nm_cb_t send; isc_nm_cb_t connect; isc_nm_accept_cb_t accept; } isc__nm_cb_t; +typedef struct isc_nm_http2_server_handler isc_nm_http2_server_handler_t; + +struct isc_nm_http2_server_handler { + char *path; + isc_nm_http_cb_t cb; + void *cbarg; + size_t extrahandlesize; + LINK(isc_nm_http2_server_handler_t) link; +}; + /* * Wrapper around uv_req_t with 'our' fields in it. req->data should * always point to its parent. Note that we always allocate more than @@ -652,7 +667,9 @@ typedef enum isc_nmsocket_type { isc_nm_tlslistener, isc_nm_tlssocket, isc_nm_tlsdnslistener, - isc_nm_tlsdnssocket + isc_nm_tlsdnssocket, + isc_nm_httplistener, + isc_nm_httpstream } isc_nmsocket_type; /*% @@ -684,12 +701,26 @@ typedef struct isc_nmsocket_tls_send_req { isc_region_t data; } isc_nmsocket_tls_send_req_t; +typedef struct isc_nmsocket_h2 { + isc_nmsocket_t *psock; /* owner of the structure */ + char *request_path; + char *query_data; + isc_nm_http2_server_handler_t *handler; + + uint8_t buf[65535]; + size_t bufsize; + size_t bufpos; + + int32_t stream_id; + LINK(struct isc_nmsocket_h2) link; +} isc_nmsocket_h2_t; struct isc_nmsocket { /*% Unlocked, RO */ int magic; int tid; isc_nmsocket_type type; isc_nm_t *mgr; + /*% Parent socket for multithreaded listeners */ isc_nmsocket_t *parent; /*% Listener socket this connection was accepted on */ @@ -740,6 +771,7 @@ struct isc_nmsocket { ISC_LIST(isc__nm_uvreq_t) sends; } tlsstream; + isc_nmsocket_h2_t h2; /*% * quota is the TCP client, attached when a TCP connection * is established. pquota is a non-attached pointer to the @@ -941,6 +973,9 @@ struct isc_nmsocket { void *accept_cbarg; 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; @@ -1105,8 +1140,8 @@ isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0); */ void -isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg); +isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); /*%< * Back-end implementation of isc_nm_send() for UDP handles. */ @@ -1167,8 +1202,8 @@ isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0); */ void -isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg); +isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); /*%< * Back-end implementation of isc_nm_send() for TCP handles. */ @@ -1349,6 +1384,10 @@ void isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, void *cbarg); +void +isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); + void isc__nm_tls_cancelread(isc_nmhandle_t *handle); @@ -1405,10 +1444,6 @@ isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle); * Stop reading on a connected TLSDNS handle. */ -void -isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg); - void isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg); @@ -1437,6 +1472,10 @@ isc__nm_tls_cleanup_data(isc_nmsocket_t *sock); void isc__nm_tls_stoplistening(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); + #define isc__nm_uverr2result(x) \ isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__) isc_result_t diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 3253dab999..d1331cea2b 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1696,6 +1696,9 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_tlsdnssocket: isc__nm_tlsdns_send(handle, region, cb, cbarg); break; + case isc_nm_httpstream: + isc__nm_http_send(handle, region, cb, cbarg); + break; default: INSIST(0); ISC_UNREACHABLE(); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 24f43c9856..6c52aed62c 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -1155,8 +1155,8 @@ failure: } void -isc__nm_tcp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg) { +isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 3b4c01f11d..a2d4f80d62 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -559,11 +559,12 @@ isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) { } void -isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg) { +isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { isc__netievent_tlssend_t *ievent = NULL; isc__nm_uvreq_t *uvreq = NULL; isc_nmsocket_t *sock = NULL; + REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); @@ -875,7 +876,6 @@ isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) { if (result != ISC_R_SUCCESS) { goto error; } - return; error: tlshandle = isc__nmhandle_get(tlssock, NULL, NULL); diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 411b8deacf..8651fd2c00 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -478,8 +478,8 @@ free: * another thread. */ void -isc__nm_udp_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, - void *cbarg) { +isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { isc_nmsocket_t *sock = handle->sock; isc_nmsocket_t *psock = NULL, *rsock = sock; isc_sockaddr_t *peer = &handle->peer; diff --git a/lib/isc/url.c b/lib/isc/url.c new file mode 100644 index 0000000000..e64bcd8ae0 --- /dev/null +++ b/lib/isc/url.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +#ifndef BIT_AT +#define BIT_AT(a, i) \ + (!!((unsigned int)(a)[(unsigned int)(i) >> 3] & \ + (1 << ((unsigned int)(i)&7)))) +#endif + +#if HTTP_PARSER_STRICT +#define T(v) 0 +#else +#define T(v) v +#endif + +static const uint8_t normal_url_char[32] = { + /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, + /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, + /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, + /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +}; + +#undef T + +typedef enum { + s_dead = 1, /* important that this is > 0 */ + + s_start_req_or_res, + s_res_or_resp_H, + s_start_res, + s_res_H, + s_res_HT, + s_res_HTT, + s_res_HTTP, + s_res_http_major, + s_res_http_dot, + s_res_http_minor, + s_res_http_end, + s_res_first_status_code, + s_res_status_code, + s_res_status_start, + s_res_status, + s_res_line_almost_done, + + s_start_req, + + s_req_method, + s_req_spaces_before_url, + s_req_schema, + s_req_schema_slash, + s_req_schema_slash_slash, + s_req_server_start, + s_req_server, + s_req_server_with_at, + s_req_path, + s_req_query_string_start, + s_req_query_string, + s_req_fragment_start, + s_req_fragment, + s_req_http_start, + s_req_http_H, + s_req_http_HT, + s_req_http_HTT, + s_req_http_HTTP, + s_req_http_I, + s_req_http_IC, + s_req_http_major, + s_req_http_dot, + s_req_http_minor, + s_req_http_end, + s_req_line_almost_done, + + s_header_field_start, + s_header_field, + s_header_value_discard_ws, + s_header_value_discard_ws_almost_done, + s_header_value_discard_lws, + s_header_value_start, + s_header_value, + s_header_value_lws, + + s_header_almost_done, + + s_chunk_size_start, + s_chunk_size, + s_chunk_parameters, + s_chunk_size_almost_done, + + s_headers_almost_done, + s_headers_done, + + /* + * Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + s_chunk_data, + s_chunk_data_almost_done, + s_chunk_data_done, + + s_body_identity, + s_body_identity_eof, + + s_message_done +} state_t; + +typedef enum { + s_http_host_dead = 1, + s_http_userinfo_start, + s_http_userinfo, + s_http_host_start, + s_http_host_v6_start, + s_http_host, + s_http_host_v6, + s_http_host_v6_end, + s_http_host_v6_zone_start, + s_http_host_v6_zone, + s_http_host_port_start, + s_http_host_port +} host_state_t; + +/* Macros for character classes; depends on strict-mode */ +#define IS_MARK(c) \ + ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \ + (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')') +#define IS_USERINFO_CHAR(c) \ + (isalnum(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || (c) == ':' || \ + (c) == '&' || (c) == '=' || (c) == '+' || (c) == '$' || (c) == ',') + +#if HTTP_PARSER_STRICT +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '-') +#else +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80)) +#define IS_HOST_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + +/* + * Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static state_t +parse_url_char(state_t s, const char ch) { + if (ch == ' ' || ch == '\r' || ch == '\n') { + return (s_dead); + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return (s_dead); + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI + * (alpha). All methods except CONNECT are followed by '/' or + * '*'. + */ + + if (ch == '/' || ch == '*') { + return (s_req_path); + } + + if (isalpha(ch)) { + return (s_req_schema); + } + + break; + + case s_req_schema: + if (isalpha(ch)) { + return (s); + } + + if (ch == ':') { + return (s_req_schema_slash); + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return (s_req_schema_slash_slash); + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return (s_req_server_start); + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return (s_dead); + } + + /* FALLTHROUGH */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return (s_req_path); + } + + if (ch == '?') { + return (s_req_query_string_start); + } + + if (ch == '@') { + return (s_req_server_with_at); + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return (s_req_server); + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return (s); + } + + switch (ch) { + case '?': + return (s_req_query_string_start); + + case '#': + return (s_req_fragment_start); + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return (s_req_query_string); + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return (s_req_query_string); + + case '#': + return (s_req_fragment_start); + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return (s_req_fragment); + } + + switch (ch) { + case '?': + return (s_req_fragment); + + case '#': + return (s); + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return (s); + } + + switch (ch) { + case '?': + case '#': + return (s); + } + + break; + + default: + break; + } + + /* + * We should never fall out of the switch above unless there's an + * error. + */ + return (s_dead); +} + +static host_state_t +http_parse_host_char(host_state_t s, const char ch) { + switch (s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return (s_http_host_start); + } + + if (IS_USERINFO_CHAR(ch)) { + return (s_http_userinfo); + } + break; + + case s_http_host_start: + if (ch == '[') { + return (s_http_host_v6_start); + } + + if (IS_HOST_CHAR(ch)) { + return (s_http_host); + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return (s_http_host); + } + + /* FALLTHROUGH */ + case s_http_host_v6_end: + if (ch == ':') { + return (s_http_host_port_start); + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return (s_http_host_v6_end); + } + + /* FALLTHROUGH */ + case s_http_host_v6_start: + if (isxdigit(ch) || ch == ':' || ch == '.') { + return (s_http_host_v6); + } + + if (s == s_http_host_v6 && ch == '%') { + return (s_http_host_v6_zone_start); + } + break; + + case s_http_host_v6_zone: + if (ch == ']') { + return (s_http_host_v6_end); + } + + /* FALLTHROUGH */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (isalnum(ch) || ch == '%' || ch == '.' || ch == '-' || + ch == '_' || ch == '~') + { + return (s_http_host_v6_zone); + } + break; + + case s_http_host_port: + case s_http_host_port_start: + if (isdigit(ch)) { + return (s_http_host_port); + } + + break; + + default: + break; + } + + return (s_http_host_dead); +} + +static isc_result_t +http_parse_host(const char *buf, isc_url_parser_t *up, int found_at) { + host_state_t s; + const char *p = NULL; + size_t buflen = up->field_data[ISC_UF_HOST].off + + up->field_data[ISC_UF_HOST].len; + + REQUIRE((up->field_set & (1 << ISC_UF_HOST)) != 0); + + up->field_data[ISC_UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + up->field_data[ISC_UF_HOST].off; p < buf + buflen; p++) { + host_state_t new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return (ISC_R_FAILURE); + } + + switch (new_s) { + case s_http_host: + if (s != s_http_host) { + up->field_data[ISC_UF_HOST].off = + (uint16_t)(p - buf); + } + up->field_data[ISC_UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + up->field_data[ISC_UF_HOST].off = + (uint16_t)(p - buf); + } + up->field_data[ISC_UF_HOST].len++; + break; + + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + up->field_data[ISC_UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + up->field_data[ISC_UF_PORT].off = + (uint16_t)(p - buf); + up->field_data[ISC_UF_PORT].len = 0; + up->field_set |= (1 << ISC_UF_PORT); + } + up->field_data[ISC_UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + up->field_data[ISC_UF_USERINFO].off = + (uint16_t)(p - buf); + up->field_data[ISC_UF_USERINFO].len = 0; + up->field_set |= (1 << ISC_UF_USERINFO); + } + up->field_data[ISC_UF_USERINFO].len++; + break; + + default: + break; + } + + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return (ISC_R_FAILURE); + default: + break; + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_url_parse(const char *buf, size_t buflen, bool is_connect, + isc_url_parser_t *up) { + state_t s; + isc_url_field_t uf, old_uf; + int found_at = 0; + const char *p = NULL; + + if (buflen == 0) { + return (ISC_R_FAILURE); + } + + up->port = up->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = ISC_UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return (ISC_R_FAILURE); + + /* Skip delimiters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = ISC_UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + /* FALLTHROUGH */ + case s_req_server: + uf = ISC_UF_HOST; + break; + + case s_req_path: + uf = ISC_UF_PATH; + break; + + case s_req_query_string: + uf = ISC_UF_QUERY; + break; + + case s_req_fragment: + uf = ISC_UF_FRAGMENT; + break; + + default: + INSIST(0); + ISC_UNREACHABLE(); + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + up->field_data[uf].len++; + continue; + } + + up->field_data[uf].off = (uint16_t)(p - buf); + up->field_data[uf].len = 1; + + up->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((up->field_set & (1 << ISC_UF_SCHEMA)) && + (up->field_set & (1 << ISC_UF_HOST)) == 0) + { + return (ISC_R_FAILURE); + } + + if (up->field_set & (1 << ISC_UF_HOST)) { + isc_result_t result; + + result = http_parse_host(buf, up, found_at); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && + up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT))) { + return (ISC_R_FAILURE); + } + + if (up->field_set & (1 << ISC_UF_PORT)) { + uint16_t off; + uint16_t len; + const char *pp = NULL; + const char *end = NULL; + unsigned long v; + + off = up->field_data[ISC_UF_PORT].off; + len = up->field_data[ISC_UF_PORT].len; + end = buf + off + len; + + /* + * NOTE: The characters are already validated and are in the + * [0-9] range + */ + INSIST(off + len <= buflen); + + v = 0; + for (pp = buf + off; pp < end; p++) { + v *= 10; + v += *pp - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return (ISC_R_RANGE); + } + } + + up->port = (uint16_t)v; + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index c780fc2b5e..e08c04753c 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -448,6 +448,7 @@ isc_nm_cancelread isc_nm_closedown isc_nm_destroy isc_nm_detach +isc_nm_listenhttps isc_nm_listentcpdns isc_nm_listentls isc_nm_listentlsdns @@ -708,6 +709,7 @@ isc_tlsctx_createserver isc_tlsctx_free isc_tm_timegm isc_tm_strptime +isc_url_parse isc_utf8_bom isc_utf8_valid isc_win32os_versioncheck diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index 7b6dc36a78..aab1a899ae 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -260,6 +260,9 @@ Library Header Files + + Library Header Files + Library Header Files @@ -614,6 +617,9 @@ Library Source Files + + Library Source Files + Library Source Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index a06a1e810d..7d989c5591 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -62,11 +62,11 @@ @IF PKCS11 BIND9;@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) ..\..\..\config.h - .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) @ELSE PKCS11 BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) ..\..\..\config.h - .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) @END PKCS11 true .\$(Configuration)\$(TargetName).pch @@ -80,7 +80,7 @@ Console true ..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt) - @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) + @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) $(ProjectName).def .\$(Configuration)\$(ProjectName).lib @@ -116,6 +116,9 @@ echo Copying the libxml DLL. copy @LIBXML2_DLL@ ..\Build\Debug\ @END LIBXML2 +echo Copying nghttp2 DLL. +copy @NGHTTP2_DLL@ ..\Build\Debug\ + @IF GSSAPI echo Copying the GSSAPI and KRB5 DLLs. @@ -163,11 +166,11 @@ copy InstallFiles ..\Build\Debug\ @IF PKCS11 BIND9;@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) ..\..\..\config.h - .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) @ELSE PKCS11 BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) ..\..\..\config.h - .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) @END PKCS11 OnlyExplicitInline false @@ -184,7 +187,7 @@ copy InstallFiles ..\Build\Debug\ true true ..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt) - @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) + @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) $(ProjectName).def .\$(Configuration)\$(ProjectName).lib Default @@ -211,6 +214,9 @@ copy @OPENSSL_PATH@\LICENSE ..\Build\Release\OpenSSL-LICENSE echo Copying libuv DLL. copy @LIBUV_DLL@ ..\Build\Release\ +echo Copying nghttp2 DLL. +copy @NGHTTP2_DLL@ ..\Build\Release\ + @IF LIBXML2 echo Copying the libxml DLL. @@ -337,6 +343,7 @@ copy InstallFiles ..\Build\Release\ + @IF PKCS11 @@ -408,14 +415,15 @@ copy InstallFiles ..\Build\Release\ + - - - + + + @@ -443,6 +451,7 @@ copy InstallFiles ..\Build\Release\ + @IF PKCS11 diff --git a/util/copyrights b/util/copyrights index 8c2c697144..4bf65ac951 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1887,6 +1887,7 @@ ./lib/isc/include/isc/tls.h C 2021 ./lib/isc/include/isc/tm.h C 2014,2016,2018,2019,2020,2021 ./lib/isc/include/isc/types.h C 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2017,2018,2019,2020,2021 +./lib/isc/include/isc/url.h C 2021 ./lib/isc/include/isc/utf8.h C 2020,2021 ./lib/isc/include/isc/util.h C 1998,1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2015,2016,2017,2018,2019,2020,2021 ./lib/isc/include/pk11/constants.h C 2014,2016,2017,2018,2019,2020,2021 @@ -1904,6 +1905,7 @@ ./lib/isc/mem_p.h C 2018,2019,2020,2021 ./lib/isc/mutexblock.c C 1999,2000,2001,2004,2005,2007,2011,2012,2016,2018,2019,2020,2021 ./lib/isc/netaddr.c C 1999,2000,2001,2002,2004,2005,2007,2010,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021 +./lib/isc/netmgr/http.c C 2021 ./lib/isc/netmgr/netmgr-int.h C 2019,2020,2021 ./lib/isc/netmgr/netmgr.c C 2019,2020,2021 ./lib/isc/netmgr/tcp.c C 2019,2020,2021 @@ -2019,6 +2021,7 @@ ./lib/isc/unix/stdtime.c C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/isc/unix/syslog.c C 2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/isc/unix/time.c C 1998,1999,2000,2001,2003,2004,2005,2006,2007,2008,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021 +./lib/isc/url.c C 2021 ./lib/isc/utf8.c C 2020,2021 ./lib/isc/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020,2021 ./lib/isc/win32/condition.c C 1998,1999,2000,2001,2004,2006,2007,2016,2018,2019,2020,2021 diff --git a/win32utils/Configure b/win32utils/Configure index 7ae633d40f..57e94d70a4 100644 --- a/win32utils/Configure +++ b/win32utils/Configure @@ -221,6 +221,7 @@ my @substinc = ("GSSAPI_INC", "IDN_INC", "LIBXML2_INC", "LIBUV_INC", + "NGHTTP2_INC", "OPENSSL_INC", "READLINE_INC", "ZLIB_INC"); @@ -235,6 +236,7 @@ my @substlib = ("GSSAPI_LIB", "KRB5_LIB", "LIBXML2_LIB", "LIBUV_LIB", + "NGHTTP2_LIB", "OPENSSL_LIBCRYPTO", "OPENSSL_LIBSSL", "READLINE_LIB", @@ -253,6 +255,7 @@ my @substdll = ("COMERR_DLL", "K5SPRT_DLL", "LIBXML2_DLL", "LIBUV_DLL", + "NGHTTP2_DLL", "OPENSSL_DLLCRYPTO", "OPENSSL_DLLSSL", "WSHELP_DLL", @@ -341,6 +344,7 @@ my @withlist = ("aes", "idn", "openssl", "libxml2", + "nghttp2", "pkcs11", "pssuspend", "python", @@ -389,6 +393,7 @@ my @help = ( " with-samples build with sample programs\n", " with-openssl[=PATH] build with OpenSSL yes|path (mandatory)\n", " with-libuv[=PATH] build with libuv yes|path (mandatory)\n", +" with-nghttp2[=PATH] build with nghttp2 yes|path (mandatory)\n", " with-pkcs11[=PATH] build with PKCS#11 support yes|no|provider-path\n", " with-gssapi[=PATH] build with MIT KfW GSSAPI yes|no|path\n", " with-libxml2[=PATH] build with libxml2 library yes|no|path\n", @@ -431,6 +436,8 @@ my $use_stests = "no"; my $use_samples = "no"; my $use_libuv = "auto"; my $libuv_path = "../../"; +my $nghttp2_path = "../../"; +my $use_nghttp2 = "auto"; my $use_openssl = "auto"; my $openssl_path = "../../"; my $use_pkcs11 = "no"; @@ -758,6 +765,13 @@ sub mywith { $use_libuv = "yes"; $libuv_path = $val; } + } elsif ($key =~ /^nghttp2$/i) { + if ($val =~ /^no$/i) { + die "nghttp2 is required\n"; + } elsif ($val !~ /^yes$/i) { + $use_nghttp2 = "yes"; + $nghttp2_path = $val; + } } elsif ($key =~ /^pkcs11$/i) { if ($val =~ /^yes$/i) { $use_pkcs11 = "yes"; @@ -949,6 +963,7 @@ if ($verbose) { print "querytrace: disabled\n"; } print "libuv-path: $libuv_path\n"; + print "nghttp2-path: $nghttp2_path\n"; print "openssl-path: $openssl_path\n"; if ($use_tests eq "yes") { print "tests: enabled\n"; @@ -1327,6 +1342,63 @@ if ($use_libuv eq "yes") { # $configdefh{"HAVE_UV_IMPORT"} = 1; } +# with-nghttp2 +if ($use_nghttp2 eq "auto") { + if ($verbose) { + print "checking for an nghttp2 built directory at sibling root\n"; + } + opendir DIR, $nghttp2_path || die "No Directory: $!\n"; + my @dirlist = grep (/^nghttp2-[0-9]+\.[0-9]+\.[0-9]+$/i, readdir(DIR)); + closedir(DIR); + + # Make sure we have something + if (scalar(@dirlist) == 0) { + die "can't find an nghttp2 at sibling root\n"; + } + # Now see if we have a directory or just a file. + # Make sure we are case insensitive + my $file; + foreach $file (sort {uc($b) cmp uc($a)} @dirlist) { + if (-f File::Spec->catfile($nghttp2_path, + $file, + "include", "nghttp2", "nghttp2.h")) { + $nghttp2_path = File::Spec->catdir($nghttp2_path, $file); + $use_nghttp2 = "yes"; + last; + } + } + + # If we have one use it otherwise report the error + if ($use_nghttp2 eq "auto") { + die "can't find an nghttp2 built directory at sibling root\n"; + } +} + +if ($use_nghttp2 eq "yes") { + $nghttp2_path = File::Spec->rel2abs($nghttp2_path); + if ($verbose) { + print "checking for nghttp2 directory at \"$nghttp2_path\"\n"; + } + if (!-f File::Spec->catfile($nghttp2_path, + "include", "nghttp2", "nghttp2.h")) { + die "can't find nghttp2 nghttp2.h include\n"; + } + my $nghttp2_inc = File::Spec->catdir($nghttp2_path, "include"); + my $nghttp2_bindir = File::Spec->catdir($nghttp2_path, "bin"); + my $nghttp2_libdir = File::Spec->catdir($nghttp2_path, "lib"); + my $nghttp2_dll = File::Spec->catfile($nghttp2_bindir, "nghttp2.dll"); + my $nghttp2_lib = File::Spec->catfile($nghttp2_libdir, "nghttp2.lib"); + if (!-f $nghttp2_lib) { + die "can't find nghttp2.lib library\n"; + } + if (!-f $nghttp2_dll) { + die "can't find nghttp2.dll library\n"; + } + $configinc{"NGHTTP2_INC"} = "$nghttp2_inc"; + $configlib{"NGHTTP2_LIB"} = "$nghttp2_lib"; + $configdll{"NGHTTP2_DLL"} = "$nghttp2_dll"; +} + # with-openssl if ($use_openssl eq "auto") { if ($verbose) { @@ -2433,6 +2505,7 @@ sub makeinstallfile { print LOUT "libdns.dll-BCFT\n"; print LOUT "libirs.dll-BCFT\n"; print LOUT "libns.dll-BCFT\n"; + print LOUT "nghttp2.dll-BCFT\n"; print LOUT "uv.dll-BCFT\n"; if ($use_openssl eq "yes") { my $v; From 08da09bc76c83e82c7aa19641ccda54eb24fa155 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 7 Dec 2020 14:19:10 +0200 Subject: [PATCH 06/11] Initial support for DNS-over-HTTP(S) This commit completes the support for DNS-over-HTTP(S) built on top of nghttp2 and plugs it into the BIND. Support for both GET and POST requests is present, as required by RFC8484. 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 simplify TLS certificates management). --- CHANGES | 5 + bin/named/include/named/globals.h | 2 +- bin/named/named.rst | 2 + bin/named/server.c | 206 ++- bin/tests/system/conf.sh.common | 1 + bin/tests/system/get_ports.sh | 1 + bin/tests/system/run.sh.in | 2 +- lib/isc/include/isc/netmgr.h | 30 +- lib/isc/netmgr/http.c | 2160 ++++++++++++++++++++++++----- lib/isc/netmgr/netmgr-int.h | 93 +- lib/isc/netmgr/netmgr.c | 75 +- lib/isc/netmgr/tcp.c | 45 +- lib/isc/netmgr/tlsstream.c | 2 +- lib/isc/tests/Makefile.am | 31 +- lib/isc/tests/doh_test.c | 1875 +++++++++++++++++++++++++ lib/isc/tests/tls_test_cert_key.h | 465 +++++++ lib/isc/tls.c | 6 +- lib/isc/url.c | 2 +- lib/isc/win32/libisc.def.in | 8 +- lib/ns/include/ns/interfacemgr.h | 2 + lib/ns/include/ns/listenlist.h | 12 + lib/ns/interfacemgr.c | 71 + lib/ns/listenlist.c | 59 +- lib/ns/win32/libns.def | 1 + util/copyrights | 6 + 25 files changed, 4734 insertions(+), 428 deletions(-) create mode 100644 lib/isc/tests/doh_test.c create mode 100644 lib/isc/tests/tls_test_cert_key.h diff --git a/CHANGES b/CHANGES index 4372665812..cd946939a1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5576. [func] Initial support for DNS-over-HTTP(S). BIND now + includes DNS-over-HTTP(S) layer built on top of nghttp2. + Both encrypted and unencrypted HTTP/2 connections + are supported. [GL !4566] + 5575. [bug] When migrating to dnssec-policy, BIND considered keys with the "Inactive" and/or "Delete" timing metadata as possible active keys. This has been fixed. [GL #2406] diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index 302eb10cd3..b8ea5946a4 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -73,8 +73,8 @@ EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS); EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER); EXTERN in_port_t named_g_port INIT(0); EXTERN in_port_t named_g_tlsport INIT(0); -EXTERN in_port_t named_g_httpport INIT(0); EXTERN in_port_t named_g_httpsport INIT(0); +EXTERN in_port_t named_g_httpport INIT(0); EXTERN isc_dscp_t named_g_dscp INIT(-1); EXTERN named_server_t *named_g_server INIT(NULL); diff --git a/bin/named/named.rst b/bin/named/named.rst index c2b19f6dd6..3fa96e038c 100644 --- a/bin/named/named.rst +++ b/bin/named/named.rst @@ -117,6 +117,8 @@ Options listen for TLS queries on ``portnum``; the default is 853. If ``value`` is of the form ``https=``, the server will listen for HTTPS queries on ``portnum``; the default is 443. + If ``value`` is of the form ``http=``, the server will + listen for HTTP queries on ``portnum``; the default is 80. ``-s`` This option writes memory usage statistics to ``stdout`` on exit. diff --git a/bin/named/server.c b/bin/named/server.c index 3d39d8f657..f46202633a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -101,8 +101,10 @@ #include #include +#include #include #include +#include #include #include @@ -397,14 +399,24 @@ fatal(named_server_t *server, const char *msg, isc_result_t result); 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, @@ -8505,6 +8517,8 @@ load_configuration(const char *filename, named_server_t *server, 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); @@ -8512,6 +8526,9 @@ load_configuration(const char *filename, named_server_t *server, 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); @@ -8573,6 +8590,19 @@ load_configuration(const char *filename, named_server_t *server, maps[i++] = named_g_defaults; maps[i] = NULL; + obj = NULL; + result = named_config_get(maps, "http-port", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_httpport = (in_port_t)cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "https-port", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj); + + 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 @@ -8991,7 +9021,8 @@ load_configuration(const char *filename, named_server_t *server, /* 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. @@ -9019,7 +9050,8 @@ load_configuration(const char *filename, named_server_t *server, /* 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. @@ -9780,6 +9812,9 @@ cleanup: 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)); @@ -10987,7 +11022,9 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) { static isc_result_t ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, - uint16_t family, ns_listenlist_t **target) { + 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; @@ -11005,7 +11042,8 @@ ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); result = ns_listenelt_fromconfig(listener, config, actx, mctx, - family, &delt); + family, http_servers, + tls_storage, &delt); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -11026,14 +11064,18 @@ 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. */ @@ -11042,43 +11084,60 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, 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_httpsport != 0) { + port = named_g_httpsport; + } else { + result = named_config_getport( + config, "https-port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } else if (http && !tls) { + if (named_g_httpport != 0) { + port = named_g_port; + } else { + result = named_config_getport( + config, "http-port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } else if (tls) { if (named_g_tlsport != 0) { port = named_g_tlsport; } else { @@ -11122,8 +11181,14 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj); } - result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key, cert, - &delt); + if (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); } @@ -11139,6 +11204,65 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, 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_httpsport : named_g_httpport; + } + + 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; diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index df88dd1d10..76f8c7cdf4 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -668,6 +668,7 @@ copy_setports() { atsign="@" sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \ -e "s/${atsign}TLSPORT${atsign}/${TLSPORT}/g" \ + -e "s/${atsign}HTTPPORT${atsign}/${HTTPSPORT}/g" \ -e "s/${atsign}HTTPSPORT${atsign}/${HTTPSPORT}/g" \ -e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \ -e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \ diff --git a/bin/tests/system/get_ports.sh b/bin/tests/system/get_ports.sh index f63ba47253..a8bb7ae6a2 100755 --- a/bin/tests/system/get_ports.sh +++ b/bin/tests/system/get_ports.sh @@ -82,6 +82,7 @@ done echo "export PORT=$(get_port "$baseport")" echo "export TLSPORT=$(get_port)" +echo "export HTTPPORT=$(get_port)" echo "export HTTPSPORT=$(get_port)" echo "export EXTRAPORT1=$(get_port)" echo "export EXTRAPORT2=$(get_port)" diff --git a/bin/tests/system/run.sh.in b/bin/tests/system/run.sh.in index 3802181762..7027ab2192 100644 --- a/bin/tests/system/run.sh.in +++ b/bin/tests/system/run.sh.in @@ -149,7 +149,7 @@ stop_servers() { echostart "S:$systest:$(date_with_args)" echoinfo "T:$systest:1:A" echoinfo "A:$systest:System test $systest" -echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${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." diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index d2e4ef4c0a..cb505f9237 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -507,33 +507,37 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, */ 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, diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index f11f4dfea9..f6b5a778d4 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -9,6 +9,9 @@ * information regarding copyright ownership. */ +#include +#include +#include #include #include #include @@ -20,15 +23,31 @@ #include #include #include +#include #include #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; @@ -37,13 +56,21 @@ typedef struct http2_client_stream { 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') @@ -57,42 +84,94 @@ struct isc_nm_http2_session { 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; }; -static bool +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 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 int +server_send_error_response(const isc_http2_error_responses_t error, + nghttp2_session *ngsession, isc_nmsocket_t *socket); + +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 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; @@ -136,10 +215,10 @@ get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp, 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; @@ -149,12 +228,36 @@ get_http2_client_stream(isc_mem_t *mctx, http2_client_stream_t **streamp, 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 != NULL) { + 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) { + REQUIRE(ISC_LIST_EMPTY(session->sstreams)); + REQUIRE(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); @@ -163,9 +266,23 @@ delete_http2_session(isc_nm_http2_session_t *session) { 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); } /* @@ -173,10 +290,6 @@ delete_http2_session(isc_nm_http2_session_t *session) { */ 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)); } } @@ -189,25 +302,45 @@ on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags, 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); @@ -217,40 +350,68 @@ static int 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, @@ -259,7 +420,7 @@ 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); } @@ -267,32 +428,109 @@ select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, /* 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 */ + ENSURE(ssl_ctx != NULL); 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); @@ -300,25 +538,26 @@ initialize_nghttp2_client_session(isc_nm_http2_session_t *session) { 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) \ @@ -335,36 +574,44 @@ send_client_connection_header(isc_nm_http2_session_t *session) { } 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); @@ -372,31 +619,55 @@ client_post_read_callback(nghttp2_session *ngsession, int32_t stream_id, /* 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); + 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)), + }; - 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_post_read_callback }; - stream_id = nghttp2_submit_request(session->ngsession, NULL, hdrs, 7, - &dp, stream); + 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); } @@ -419,26 +690,23 @@ https_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, 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); @@ -455,15 +723,16 @@ https_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) { 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)); @@ -471,8 +740,8 @@ http2_do_bio(isc_nm_http2_session_t *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) { @@ -495,7 +764,7 @@ http2_do_bio(isc_nm_http2_session_t *session) { } http2_do_bio(session); - return (false); + return; } else { /* Resume reading, it's idempotent, wait for more */ isc_nm_resumeread(session->handle); @@ -528,128 +797,404 @@ http2_do_bio(isc_nm_http2_session_t *session) { 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; + } + + 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(handle->sock->tlsstream.ssl, + &alpn, &alpnlen); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (alpn == NULL) { + SSL_get0_alpn_selected(ssl, &alpn, &alpnlen); + } +#endif + + if (alpn == NULL || alpnlen != 2 || + memcmp(NGHTTP2_PROTO_VERSION_ID, alpn, + NGHTTP2_PROTO_VERSION_ID_LEN) != 0) + { + result = ISC_R_CANCELED; + goto error; + } } isc_nmhandle_attach(handle, &session->handle); - -#if 0 -/* TODO H2 */ -#ifndef OPENSSL_NO_NEXTPROTONEG - SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen); -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - 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; - } -#endif + handle->sock->h2.session = session; initialize_nghttp2_client_session(session); - send_client_connection_header(session); - client_submit_request(session); - http2_do_bio(session); -} - -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) { - REQUIRE(VALID_NM(mgr)); - - UNUSED(local); - UNUSED(peer); - UNUSED(uri); - UNUSED(cb); - UNUSED(cbarg); - UNUSED(timeout); - UNUSED(extrahandlesize); - - return (ISC_R_NOTIMPLEMENTED); -} - -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; - http2_client_stream_t *cstream = NULL; - struct addrinfo hints; - struct addrinfo *res = NULL; - isc_sockaddr_t local, peer; - isc_result_t result; - int s; - - if (ctx == NULL) { - ctx = create_ssl_ctx(); + 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); } - 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); + 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); + } - result = get_http2_client_stream(mgr->mctx, &cstream, uri, &port); - if (result != ISC_R_SUCCESS) { + if (session != NULL) { delete_http2_session(session); + } + + if (uri != NULL) { + isc_mem_free(mctx, uri); + } +} + +isc_result_t +isc_nm_httpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, + const char *uri, bool post, isc_nm_cb_t cb, void *cbarg, + 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); } - cstream->postdata = region; - cstream->postdata_pos = 0; - cstream->cb = cb; - cstream->cbarg = cbarg; + schema_len = url_parser.field_data[ISC_UF_SCHEMA].len; + INSIST(schema_len > 0); + schema = &uri[url_parser.field_data[ISC_UF_SCHEMA].off]; - session->cstream = cstream; + 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(); + } + + if (peer == NULL || local == NULL) { + size_t hostlen = 0; + char *host = NULL; + struct addrinfo hints; + struct addrinfo *res = NULL; #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); + /* 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'; - s = getaddrinfo(host, NULL, &hints, &res); - isc_mem_free(mgr->mctx, host); - if (s != 0) { - delete_http2_session(session); - return (ISC_R_FAILURE); - } + 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 */ - isc_sockaddr_fromsockaddr(&peer, res->ai_addr); - isc_sockaddr_setport(&peer, port); - isc_sockaddr_anyofpf(&local, res->ai_family); + if ((url_parser.field_set & (1 << ISC_UF_PORT)) != 0) { + port = url_parser.port; + } - freeaddrinfo(res); + if (peer == NULL) { + (void)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; + } - 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 */ - if (result != ISC_R_SUCCESS) { - return (result); + 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 != NULL) { + 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_httprequest(isc_nmhandle_t *handle, isc_region_t *region, + isc_nm_recv_cb_t reply_cb, void *cbarg) { + http2_client_stream_t *cstream = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_nm_http2_session_t *session = NULL; + isc_mem_t *mctx; + + 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; + } + + 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; + } + + isc__buffer_usedregion(buf, &base64_region); + INSIST(base64_region.length == base64_len); + + 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; + } + + /* 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); + + INSIST(cstream->GET_path_len == (path_size - 1)); + isc_mem_free(mctx, base64url_data); + } + + ISC_LINK_INIT(cstream, link); + ISC_LIST_APPEND(session->cstreams, cstream, link); + + INSIST(cstream != NULL); + + result = client_submit_request(session, cstream); + if (result != ISC_R_SUCCESS) { + 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 @@ -669,29 +1214,214 @@ server_on_begin_headers_callback(nghttp2_session *ngsession, 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 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)); + + if (request_path == NULL || *request_path == '\0') { + return (NULL); + } + + RWLOCK(&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; + } + } + } + RWUNLOCK(&serversocket->h2.handlers_lock, isc_rwlocktype_read); + + 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; + const uint8_t *qstr = NULL; + size_t vlen = valuelen; + + qstr = memchr(value, '?', valuelen); + if (qstr != NULL) { + vlen = qstr - value; + } + + 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, vlen + 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 (qstr != NULL) { + 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 (isc__nm_parse_doh_query_string((const char *)qstr, + &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_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); @@ -704,26 +1434,41 @@ server_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, socket = nghttp2_session_get_stream_user_data( session, frame->hd.stream_id); - if (socket == NULL || socket->h2.request_path != NULL) { + if (socket == NULL) { break; } - 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 (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); } @@ -758,8 +1503,9 @@ static int server_send_response(nghttp2_session *ngsession, int32_t stream_id, const nghttp2_nv *nva, size_t nvlen, isc_nmsocket_t *socket) { - int rv; nghttp2_data_provider data_prd; + int rv; + data_prd.source.ptr = socket; data_prd.read_callback = server_read_callback; @@ -771,80 +1517,226 @@ server_send_response(nghttp2_session *ngsession, int32_t stream_id, return (0); } -static const char ERROR_HTML[] = "404" - "

404 Not Found

"; +#define MAKE_ERROR_REPLY(tag, code) \ + { \ + tag, MAKE_NV2(":status", #code) \ + } + +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), +}; static int -error_reply(nghttp2_session *ngsession, isc_nmsocket_t *socket) { - const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "404") }; +server_send_error_response(const isc_http2_error_responses_t error, + nghttp2_session *ngsession, isc_nmsocket_t *socket) { + int rv; - memmove(socket->h2.buf, ERROR_HTML, sizeof(ERROR_HTML)); - socket->h2.bufsize = sizeof(ERROR_HTML); + 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) { + rv = server_send_response( + ngsession, socket->h2.stream_id, + &error_responses[i].header, + sizeof(error_responses[i].header), socket); + return (rv); + } + } + + rv = server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession, + socket); + return (rv); } 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; + isc_http2_error_responses_t code = ISC_HTTP_ERROR_SUCCESS; + isc_region_t data; - if (!socket->h2.request_path) { - if (error_reply(ngsession, socket) != 0) { - return (NGHTTP2_ERR_CALLBACK_FAILURE); - } - return (0); - } - - for (handler = ISC_LIST_HEAD(session->serversocket->handlers); - handler != NULL; handler = ISC_LIST_NEXT(handler, link)) + /* + * 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) { - if (!strcmp(socket->h2.request_path, handler->path)) { - break; - } + 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; } - if (handler == NULL) { - if (error_reply(ngsession, socket) != 0) { - return (NGHTTP2_ERR_CALLBACK_FAILURE); - } - return (0); + if (code != ISC_HTTP_ERROR_SUCCESS) { + goto error; + } + + 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; + } + 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; - /* TODO FIXME do it asynchronously!!! */ - memcpy(sock->h2.buf, region->base, region->length); - sock->h2.bufsize = region->length; + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(handle->httpsession != NULL); + REQUIRE(VALID_NMSOCK(sock)); + + /* 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; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(VALID_UVREQ(req)); + + ievent->req = NULL; + 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 @@ -884,7 +1776,7 @@ static void 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); @@ -925,20 +1817,55 @@ static isc_result_t 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 = NULL, *httpserver = NULL; + + 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 */ @@ -946,16 +1873,55 @@ httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { return (ISC_R_SUCCESS); } +#ifndef OPENSSL_NO_NEXTPROTONEG +static int +next_proto_cb(SSL *ssl, const unsigned char **data, unsigned int *len, + void *arg) { + UNUSED(ssl); + UNUSED(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); @@ -971,6 +1937,13 @@ isc_nm_listenhttps(isc_nm_t *mgr, isc_nmiface_t *iface, int backlog, 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); @@ -985,72 +1958,42 @@ isc_nm_http_add_endpoint(isc_nmsocket_t *sock, const char *uri, 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) - }; + 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_LINK_INIT(handler, link); - ISC_LINK_INIT(handler, link); - ISC_LIST_APPEND(sock->handlers, handler, link); + RWLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write); + ISC_LIST_APPEND(sock->h2.handlers, handler, link); + RWUNLOCK(&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[] = - "No request" - "

No request

"; - -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); } @@ -1059,19 +2002,586 @@ isc_nm_http_add_doh_endpoint(isc_nmsocket_t *sock, const char *uri, 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 = (isc_nm_http_doh_cbarg_t){ .cb = cb, .cbarg = cbarg }; + ISC_LINK_INIT(dohcbarg, link); 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)); } + RWLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write); + ISC_LIST_APPEND(sock->h2.handlers_cbargs, dohcbarg, link); + RWUNLOCK(&sock->h2.handlers_lock, isc_rwlocktype_write); + return (result); } + +void +isc__nm_http_stoplistening(isc_nmsocket_t *sock) { + isc__netievent_httpstop_t *ievent = NULL; + + 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 */ + RWLOCK(&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; + } + RWUNLOCK(&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; + + REQUIRE(start != NULL); + REQUIRE(len != 0); + + 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; + + return (true); +} + +static bool +rule_key_value_pair(isc_doh_query_parser_state_t *st); + +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_value_char(isc_doh_query_parser_state_t *st); + +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_query_string(isc_doh_query_parser_state_t *st) { + if (MATCH('?')) { + ADVANCE(); + } + + while (rule_key_value_pair(st)) { + /* skip */; + } + + if (!MATCH('\0')) { + return (false); + } + + ADVANCE(); + return (true); +} + +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(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)) { + /* skip */; + } + st->last_value_len = GETP() - st->last_value; + return (true); +} + +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); +} diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 3ad09823bf..105ea4d0bb 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -273,6 +274,10 @@ typedef enum isc__netievent_type { netievent_tlsdnscycle, netievent_tlsdnsshutdown, + netievent_httpstop, + netievent_httpsend, + netievent_httpclose, + netievent_close, netievent_shutdown, netievent_stop, @@ -701,18 +706,61 @@ typedef struct isc_nmsocket_tls_send_req { 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 */ @@ -974,8 +1022,6 @@ struct isc_nmsocket { 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; @@ -1472,10 +1518,43 @@ isc__nm_tls_cleanup_data(isc_nmsocket_t *sock); void isc__nm_tls_stoplistening(isc_nmsocket_t *sock); +void +isc__nm_http_stoplistening(isc_nmsocket_t *sock); + +void +isc__nm_http_clear_handlers(isc_nmsocket_t *sock); + +void +isc__nm_http_clear_session(isc_nmsocket_t *sock); + void isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, void *cbarg); +void +isc__nm_http_close(isc_nmsocket_t *sock); + +void +isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_httpstop(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0); + +bool +isc__nm_parse_doh_query_string(const char *query_string, const char **start, + size_t *len); + +char * +isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url, + const size_t base64url_len, size_t *res_len); + +char * +isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64, + const size_t base64_len, size_t *res_len); + #define isc__nm_uverr2result(x) \ isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__) isc_result_t @@ -1608,6 +1687,10 @@ NETIEVENT_SOCKET_HANDLE_TYPE(tlsdnscancel); NETIEVENT_SOCKET_QUOTA_TYPE(tlsdnsaccept); NETIEVENT_SOCKET_TYPE(tlsdnscycle); +NETIEVENT_SOCKET_TYPE(httpstop); +NETIEVENT_SOCKET_REQ_TYPE(httpsend); +NETIEVENT_SOCKET_TYPE(httpclose); + NETIEVENT_SOCKET_REQ_TYPE(tcpconnect); NETIEVENT_SOCKET_REQ_TYPE(tcpsend); NETIEVENT_SOCKET_TYPE(tcpstartread); @@ -1668,6 +1751,10 @@ NETIEVENT_SOCKET_HANDLE_DECL(tlsdnscancel); NETIEVENT_SOCKET_QUOTA_DECL(tlsdnsaccept); NETIEVENT_SOCKET_DECL(tlsdnscycle); +NETIEVENT_SOCKET_DECL(httpstop); +NETIEVENT_SOCKET_REQ_DECL(httpsend); +NETIEVENT_SOCKET_DECL(httpclose); + NETIEVENT_SOCKET_REQ_DECL(tcpconnect); NETIEVENT_SOCKET_REQ_DECL(tcpsend); NETIEVENT_SOCKET_REQ_DECL(tlssend); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index d1331cea2b..fb8b1fa701 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -734,6 +734,10 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) { NETIEVENT_CASE(tlsdnsstop); NETIEVENT_CASE(tlsdnsshutdown); + NETIEVENT_CASE(httpstop); + NETIEVENT_CASE(httpsend); + NETIEVENT_CASE(httpclose); + NETIEVENT_CASE(connectcb); NETIEVENT_CASE(readcb); NETIEVENT_CASE(sendcb); @@ -814,6 +818,10 @@ NETIEVENT_SOCKET_QUOTA_DEF(tlsdnsaccept); NETIEVENT_SOCKET_DEF(tlsdnscycle); NETIEVENT_SOCKET_DEF(tlsdnsshutdown); +NETIEVENT_SOCKET_DEF(httpstop); +NETIEVENT_SOCKET_REQ_DEF(httpsend); +NETIEVENT_SOCKET_DEF(httpclose); + NETIEVENT_SOCKET_REQ_DEF(tcpconnect); NETIEVENT_SOCKET_REQ_DEF(tcpsend); NETIEVENT_SOCKET_REQ_DEF(tlssend); @@ -1001,6 +1009,32 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { 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); @@ -1115,6 +1149,9 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_close(sock); return; + case isc_nm_httpstream: + isc__nm_http_close(sock); + return; default: break; } @@ -1158,7 +1195,8 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) { (*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); } @@ -1221,6 +1259,8 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, case isc_nm_tcpdnslistener: case isc_nm_tlsdnssocket: case isc_nm_tlsdnslistener: + case isc_nm_httpstream: + case isc_nm_httplistener: if (family == AF_INET) { sock->statsindex = tcp4statsindex; } else { @@ -1250,6 +1290,28 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, atomic_store(&sock->active_child_connections, 0); + if (type == isc_nm_httplistener) { + ISC_LIST_INIT(sock->h2.handlers); + ISC_LIST_INIT(sock->h2.handlers_cbargs); + isc_rwlock_init(&sock->h2.handlers_lock, 0, 1); + } + + sock->h2.session = NULL; + sock->h2.httpserver = NULL; + sock->h2.query_data = NULL; + sock->h2.query_data_len = 0; + sock->h2.query_too_large = false; + sock->h2.request_path = NULL; + sock->h2.request_type = ISC_HTTP_REQ_UNSUPPORTED; + sock->h2.request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED; + sock->h2.content_length = 0; + sock->h2.content_type_verified = false; + sock->h2.accept_type_verified = false; + sock->h2.handler_cb = NULL; + sock->h2.handler_cbarg = NULL; + sock->h2.connect.uri = NULL; + sock->h2.buf = NULL; + sock->magic = NMSOCK_MAGIC; } @@ -1391,6 +1453,10 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer, sock->statichandle = handle; } + if (sock->type == isc_nm_httpstream) { + handle->httpsession = sock->h2.session; + } + return (handle); } @@ -1822,6 +1888,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { 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(); @@ -2374,6 +2443,10 @@ nmsocket_type_totext(isc_nmsocket_type type) { 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(); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 6c52aed62c..e7ad6cd634 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -167,6 +167,27 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { REQUIRE(isc__nm_in_netthread()); REQUIRE(sock->tid == isc_nm_tid()); + result = isc__nm_socket(req->peer.type.sa.sa_family, SOCK_STREAM, 0, + &sock->fd); + /* + * The socket() call can fail spuriously on FreeBSD 12, so we need to + * handle the failure early and gracefully. + */ + if (result != ISC_R_SUCCESS) { + atomic_store(&sock->closed, true); + isc__nm_uvreq_t *cbreq = NULL; + cbreq = isc__nm_uvreq_get(sock->mgr, sock); + cbreq->cb.connect = req->cb.connect; + cbreq->cbarg = req->cbarg; + isc_nmhandle_attach(req->handle, &cbreq->handle); + isc__nmsocket_clearcb(sock); + isc__nm_connectcb(sock, cbreq, result); + goto error; + } + result = isc__nm_socket_connectiontimeout(sock->fd, + sock->connect_timeout); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + worker = &sock->mgr->workers[sock->tid]; atomic_store(&sock->connecting, true); @@ -210,7 +231,7 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { done: result = isc__nm_uverr2result(r); - +error: LOCK(&sock->lock); sock->result = result; SIGNAL(&sock->cond); @@ -239,10 +260,13 @@ isc__nm_async_tcpconnect(isc__networker_t *worker, isc__netievent_t *ev0) { REQUIRE(sock->parent == NULL); REQUIRE(sock->tid == isc_nm_tid()); + sock->fd = (uv_os_sock_t)(-1); result = tcp_connect_direct(sock, req); if (result != ISC_R_SUCCESS) { atomic_store(&sock->active, false); - isc__nm_tcp_close(sock); + if (sock->fd != (uv_os_sock_t)(-1)) { + isc__nm_tcp_close(sock); + } isc__nm_uvreq_put(&req, sock); } @@ -309,36 +333,19 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer, isc_nmsocket_t *sock = NULL; isc__netievent_tcpconnect_t *ievent = NULL; isc__nm_uvreq_t *req = NULL; - sa_family_t sa_family; - uv_os_sock_t fd; REQUIRE(VALID_NM(mgr)); REQUIRE(local != NULL); REQUIRE(peer != NULL); - sa_family = peer->addr.type.sa.sa_family; - - /* - * The socket() call can fail spuriously on FreeBSD 12, so we need to - * handle the failure early and gracefully. - */ - result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &fd); - if (result != ISC_R_SUCCESS) { - return (result); - } - sock = isc_mem_get(mgr->mctx, sizeof(*sock)); isc__nmsocket_init(sock, mgr, isc_nm_tcpsocket, local); sock->extrahandlesize = extrahandlesize; sock->connect_timeout = timeout; sock->result = ISC_R_DEFAULT; - sock->fd = fd; atomic_init(&sock->client, true); - result = isc__nm_socket_connectiontimeout(fd, timeout); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - req = isc__nm_uvreq_get(mgr, sock); req->cb.connect = cb; req->cbarg = cbarg; diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index a2d4f80d62..87242e0a0b 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -510,7 +510,7 @@ isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface, *sockp = tlssock; } - return result; + return (result); } void diff --git a/lib/isc/tests/Makefile.am b/lib/isc/tests/Makefile.am index c8d07aa288..58b43607a2 100644 --- a/lib/isc/tests/Makefile.am +++ b/lib/isc/tests/Makefile.am @@ -48,6 +48,7 @@ TESTS = \ tcp_quota_test \ tcpdns_test \ tlsdns_test \ + doh_test \ time_test \ timer_test \ udp_test @@ -55,6 +56,16 @@ TESTS = \ check_PROGRAMS = \ $(TESTS) +doh_test_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) + +doh_test_LDADD = \ + $(LDADD) \ + $(LIBUV_LIBS) \ + $(OPENSSL_LIBS) + hmac_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(OPENSSL_CFLAGS) @@ -77,8 +88,8 @@ random_test_LDADD = \ tcp_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(OPENSSL_CFLAGS) \ - $(LIBUV_CFLAGS) + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) tcp_test_LDADD = \ $(LDADD) \ @@ -86,8 +97,8 @@ tcp_test_LDADD = \ tcp_quota_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(OPENSSL_CFLAGS) \ - $(LIBUV_CFLAGS) + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) tcp_quota_test_LDADD = \ $(LDADD) \ @@ -95,8 +106,8 @@ tcp_quota_test_LDADD = \ tcpdns_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(OPENSSL_CFLAGS) \ - $(LIBUV_CFLAGS) + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) tcpdns_test_LDADD = \ $(LDADD) \ @@ -104,8 +115,8 @@ tcpdns_test_LDADD = \ tlsdns_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(OPENSSL_CFLAGS) \ - $(LIBUV_CFLAGS) + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) tlsdns_test_LDADD = \ $(LDADD) \ @@ -113,8 +124,8 @@ tlsdns_test_LDADD = \ udp_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ - $(OPENSSL_CFLAGS) \ - $(LIBUV_CFLAGS) + $(LIBUV_CFLAGS) \ + $(OPENSSL_CFLAGS) udp_test_LDADD = \ $(LDADD) \ diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c new file mode 100644 index 0000000000..9e47d10cb6 --- /dev/null +++ b/lib/isc/tests/doh_test.c @@ -0,0 +1,1875 @@ +/* + * 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 /* IWYU pragma: keep */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNIT_TESTING +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +int +main(void) { + printf("1..0 # Skipped: cmocka not available\n"); + return (0); +} + +#endif /* if HAVE_CMOCKA */ diff --git a/lib/isc/tests/tls_test_cert_key.h b/lib/isc/tests/tls_test_cert_key.h new file mode 100644 index 0000000000..898b77bd85 --- /dev/null +++ b/lib/isc/tests/tls_test_cert_key.h @@ -0,0 +1,465 @@ +/* + * 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 */ diff --git a/lib/isc/tls.c b/lib/isc/tls.c index e45678a355..ea5de5dbf9 100644 --- a/lib/isc/tls.c +++ b/lib/isc/tls.c @@ -127,8 +127,10 @@ isc_tlsctx_createclient(isc_tlsctx_t **ctxp) { #if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); #else - SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); + SSL_CTX_set_options( + ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | + SSL_OP_NO_TLSv1_1 | SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #endif *ctxp = ctx; diff --git a/lib/isc/url.c b/lib/isc/url.c index e64bcd8ae0..d4e13ee70a 100644 --- a/lib/isc/url.c +++ b/lib/isc/url.c @@ -650,7 +650,7 @@ isc_url_parse(const char *buf, size_t buflen, bool is_connect, 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'; diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index e08c04753c..e460d5757e 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -448,7 +448,12 @@ isc_nm_cancelread 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 @@ -469,7 +474,6 @@ isc_nm_settimeouts isc_nm_tcpdns_keepalive isc_nm_tcpdns_sequential isc_nm_tid -isc_nm_tlsconnect isc_nm_tlsdnsconnect isc_nm_udpconnect isc_nmsocket_close diff --git a/lib/ns/include/ns/interfacemgr.h b/lib/ns/include/ns/interfacemgr.h index e210ee3bc0..152ce4b76c 100644 --- a/lib/ns/include/ns/interfacemgr.h +++ b/lib/ns/include/ns/interfacemgr.h @@ -80,6 +80,8 @@ struct ns_interface { isc_socket_t * tcpsocket; /*%< TCP socket. */ isc_nmsocket_t *udplistensocket; isc_nmsocket_t *tcplistensocket; + isc_nmsocket_t *http_listensocket; + isc_nmsocket_t *http_secure_listensocket; isc_dscp_t dscp; /*%< "listen-on" DSCP value */ isc_refcount_t ntcpaccepting; /*%< Number of clients * ready to accept new diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index fe6dffde7e..de30a5747b 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -42,9 +42,12 @@ typedef struct ns_listenlist ns_listenlist_t; struct ns_listenelt { isc_mem_t * mctx; in_port_t port; + bool is_http; isc_dscp_t dscp; /* -1 = not set, 0..63 */ dns_acl_t * acl; isc_tlsctx_t *sslctx; + char ** http_endpoints; + size_t http_endpoints_number; ISC_LINK(ns_listenelt_t) link; }; @@ -66,6 +69,15 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, * Create a listen-on list element. */ +isc_result_t +ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, + dns_acl_t *acl, 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); /*%< diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index 8f73279263..a1443410a7 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -437,6 +437,10 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, goto failure; } + ifp->tcplistensocket = NULL; + ifp->http_listensocket = NULL; + ifp->http_secure_listensocket = NULL; + *ifpret = ifp; return (ISC_R_SUCCESS); @@ -539,6 +543,54 @@ ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) { return (result); } +static isc_result_t +ns_interface_listenhttp(ns_interface_t *ifp, isc_tlsctx_t *sslctx, char **eps, + size_t neps) { + isc_result_t result; + isc_nmsocket_t *sock = NULL; + size_t i = 0; + + result = isc_nm_listenhttp(ifp->mgr->nm, (isc_nmiface_t *)&ifp->addr, + ifp->mgr->backlog, &ifp->mgr->sctx->tcpquota, + sslctx, &sock); + + if (result == ISC_R_SUCCESS) { + for (i = 0; i < neps; i++) { + result = isc_nm_http_add_doh_endpoint( + sock, eps[i], ns__client_request, ifp, + sizeof(ns_client_t)); + } + } + + if (result != ISC_R_SUCCESS) { + isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, + "creating %s socket: %s", + sslctx ? "HTTPS" : "HTTP", + isc_result_totext(result)); + return (result); + } + + if (sslctx) { + ifp->http_secure_listensocket = sock; + } else { + ifp->http_listensocket = sock; + } + + /* + * We call this now to update the tcp-highwater statistic: + * this is necessary because we are adding to the TCP quota just + * by listening. + */ + result = ns__client_tcpconn(NULL, ISC_R_SUCCESS, ifp); + if (result != ISC_R_SUCCESS) { + isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, + "updating TCP stats: %s", + isc_result_totext(result)); + } + + return (result); +} + static isc_result_t ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name, ns_interface_t **ifpret, bool accept_tcp, @@ -555,6 +607,17 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, ifp->dscp = elt->dscp; + if (elt->is_http) { + result = ns_interface_listenhttp(ifp, elt->sslctx, + elt->http_endpoints, + elt->http_endpoints_number); + if (result != ISC_R_SUCCESS) { + goto cleanup_interface; + } + *ifpret = ifp; + return (result); + } + if (elt->sslctx != NULL) { result = ns_interface_listentls(ifp, elt->sslctx); if (result != ISC_R_SUCCESS) { @@ -611,6 +674,14 @@ ns_interface_shutdown(ns_interface_t *ifp) { isc_nm_stoplistening(ifp->tcplistensocket); isc_nmsocket_close(&ifp->tcplistensocket); } + if (ifp->http_listensocket != NULL) { + isc_nm_stoplistening(ifp->http_listensocket); + isc_nmsocket_close(&ifp->http_listensocket); + } + if (ifp->http_secure_listensocket != NULL) { + isc_nm_stoplistening(ifp->http_secure_listensocket); + isc_nmsocket_close(&ifp->http_secure_listensocket); + } if (ifp->clientmgr != NULL) { ns_clientmgr_destroy(&ifp->clientmgr); } diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index c5153ca60c..5fbb59db9f 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -30,24 +30,59 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, ns_listenelt_t **target) { ns_listenelt_t *elt = NULL; isc_result_t result = ISC_R_SUCCESS; + isc_tlsctx_t *sslctx = NULL; + REQUIRE(target != NULL && *target == NULL); - elt = isc_mem_get(mctx, sizeof(*elt)); - elt->mctx = mctx; - ISC_LINK_INIT(elt, link); - elt->port = port; - elt->dscp = dscp; - elt->acl = acl; - elt->sslctx = NULL; + if (tls) { - result = isc_tlsctx_createserver(key, cert, &elt->sslctx); + result = isc_tlsctx_createserver(key, cert, &sslctx); if (result != ISC_R_SUCCESS) { return (result); } } + + elt = isc_mem_get(mctx, sizeof(*elt)); + elt->mctx = mctx; + ISC_LINK_INIT(elt, link); + elt->port = port; + elt->is_http = false; + elt->dscp = dscp; + elt->acl = acl; + elt->sslctx = sslctx; + elt->http_endpoints = NULL; + elt->http_endpoints_number = 0; + *target = elt; return (ISC_R_SUCCESS); } +isc_result_t +ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, + dns_acl_t *acl, 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) { @@ -56,6 +91,14 @@ ns_listenelt_destroy(ns_listenelt_t *elt) { if (elt->sslctx != NULL) { isc_tlsctx_free(&elt->sslctx); } + if (elt->http_endpoints != NULL) { + size_t i; + INSIST(elt->http_endpoints_number > 0); + for (i = 0; i < elt->http_endpoints_number; i++) { + isc_mem_free(elt->mctx, elt->http_endpoints[i]); + } + isc_mem_free(elt->mctx, elt->http_endpoints); + } isc_mem_put(elt->mctx, elt, sizeof(*elt)); } diff --git a/lib/ns/win32/libns.def b/lib/ns/win32/libns.def index 66d706af4f..a08ba68d1d 100644 --- a/lib/ns/win32/libns.def +++ b/lib/ns/win32/libns.def @@ -66,6 +66,7 @@ ns_interfacemgr_shutdown ns_lib_init ns_lib_shutdown ns_listenelt_create +ns_listenelt_create_http ns_listenelt_destroy ns_listenlist_attach ns_listenlist_create diff --git a/util/copyrights b/util/copyrights index 4bf65ac951..781e928906 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1955,6 +1955,7 @@ ./lib/isc/tests/buffer_test.c C 2014,2015,2016,2017,2018,2019,2020,2021 ./lib/isc/tests/counter_test.c C 2014,2016,2018,2019,2020,2021 ./lib/isc/tests/crc64_test.c C 2018,2019,2020,2021 +./lib/isc/tests/doh_test.c C 2020,2021 ./lib/isc/tests/errno_test.c C 2016,2018,2019,2020,2021 ./lib/isc/tests/file_test.c C 2014,2016,2017,2018,2019,2020,2021 ./lib/isc/tests/hash_test.c C 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 @@ -1987,6 +1988,7 @@ ./lib/isc/tests/testdata/file/keep X 2014,2018,2019,2020,2021 ./lib/isc/tests/time_test.c C 2014,2015,2016,2018,2019,2020,2021 ./lib/isc/tests/timer_test.c C 2018,2019,2020,2021 +./lib/isc/tests/tls_test_cert_key.h C 2021 ./lib/isc/tests/tlsdns_test.c C 2021 ./lib/isc/tests/udp_test.c C 2020,2021 ./lib/isc/tests/uv_wrap.h C 2020,2021 @@ -2104,18 +2106,22 @@ ./lib/isccc/win32/libisccc.vcxproj.user X 2013,2018,2019,2020,2021 ./lib/isccfg/aclconf.c C 1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 ./lib/isccfg/dnsconf.c C 2009,2016,2018,2019,2020,2021 +./lib/isccfg/httpconf.c C 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/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/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 +./lib/isccfg/tlsconf.c C 2021 ./lib/isccfg/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020,2021 ./lib/isccfg/win32/libisccfg.def X 2001,2002,2005,2009,2010,2011,2013,2014,2015,2016,2018,2019,2020,2021 ./lib/isccfg/win32/libisccfg.vcxproj.filters.in X 2013,2014,2015,2016,2018,2019,2020 From 1cc24a2c8bd57e767f15ec2c99212e89a972ef23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 2 Feb 2021 09:28:23 +0100 Subject: [PATCH 07/11] Unit-test fixes and manual page updates for DoH configuration This commit contains fixes to unit tests to make them work well on various platforms (in particular ones shipping old versions of OpenSSL) and for different configurations. It also updates the generated manpage to include DoH configuration options. --- lib/isc/netmgr/http.c | 4 + lib/isc/tests/Makefile.am | 6 +- lib/isc/tests/doh_test.c | 234 +++++---------- lib/isc/tests/tls_test_cert_key.h | 465 ------------------------------ util/copyrights | 1 - 5 files changed, 72 insertions(+), 638 deletions(-) delete mode 100644 lib/isc/tests/tls_test_cert_key.h diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index f6b5a778d4..b90abce05c 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -790,6 +790,10 @@ http2_do_bio(isc_nm_http2_session_t *session) { * this situation. */ sz = nghttp2_session_mem_send(session->ngsession, &data); + if (sz == 0) { + /* No data returned */ + return; + } INSIST(session->r.base == NULL); session->r.base = isc_mem_get(session->mctx, sz); session->r.length = sz; diff --git a/lib/isc/tests/Makefile.am b/lib/isc/tests/Makefile.am index 58b43607a2..05b97487b7 100644 --- a/lib/isc/tests/Makefile.am +++ b/lib/isc/tests/Makefile.am @@ -20,6 +20,7 @@ TESTS = \ buffer_test \ counter_test \ crc64_test \ + doh_test \ errno_test \ file_test \ hash_test \ @@ -44,13 +45,12 @@ TESTS = \ symtab_test \ task_test \ taskpool_test \ - tcp_test \ tcp_quota_test \ + tcp_test \ tcpdns_test \ - tlsdns_test \ - doh_test \ time_test \ timer_test \ + tlsdns_test \ udp_test check_PROGRAMS = \ diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index 9e47d10cb6..879e42a431 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -10,6 +10,7 @@ */ #if HAVE_CMOCKA +#include #include /* IWYU pragma: keep */ #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +44,6 @@ #include "../netmgr/uv-compat.c" #include "../netmgr/uv-compat.h" #include "isctest.h" -#include "tls_test_cert_key.h" #define MAX_NM 2 @@ -68,16 +69,11 @@ static unsigned int workers = 1; static bool reuse_supported = true; -static atomic_bool POST = true; +static atomic_bool POST = ATOMIC_VAR_INIT(true); -static atomic_bool use_TLS = false; +static atomic_bool use_TLS = ATOMIC_VAR_INIT(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 @@ -183,10 +179,6 @@ _setup(void **state) { 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); } @@ -194,11 +186,6 @@ 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); @@ -256,6 +243,9 @@ nm_setup(void **state) { assert_non_null(nm[i]); } + server_ssl_ctx = NULL; + isc_tlsctx_createserver(NULL, NULL, &server_ssl_ctx); + *state = nm; return (0); @@ -271,6 +261,10 @@ nm_teardown(void **state) { } isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0])); + if (server_ssl_ctx) { + isc_tlsctx_free(&server_ssl_ctx); + } + return (0); } @@ -301,136 +295,6 @@ sockaddr_to_url(isc_sockaddr_t *sa, const bool https, char *outbuf, 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) { @@ -512,6 +376,27 @@ doh_receive_request_cb(isc_nmhandle_t *handle, isc_result_t eresult, } } +static void +mock_doh_uv_tcp_bind(void **state) { + isc_nm_t **nm = (isc_nm_t **)*state; + isc_nm_t *listen_nm = nm[0]; + isc_result_t result = ISC_R_SUCCESS; + isc_nmsocket_t *listen_sock = NULL; + isc_sockaddr_t tcp_connect_addr; + + tcp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0); + + WILL_RETURN(uv_tcp_bind, UV_EADDRINUSE); + + result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr, + 0, NULL, NULL, &listen_sock); + assert_int_not_equal(result, ISC_R_SUCCESS); + assert_null(listen_sock); + + RESET_RETURN; +} + static void doh_noop(void **state) { isc_nm_t **nm = (isc_nm_t **)*state; @@ -900,11 +785,6 @@ doh_recv_send(void **state) { 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); @@ -981,11 +861,6 @@ doh_recv_half_send(void **state) { 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); @@ -1067,11 +942,6 @@ doh_half_recv_send(void **state) { 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); @@ -1153,11 +1023,6 @@ doh_half_recv_half_send(void **state) { 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); @@ -1727,6 +1592,7 @@ doh_base64_to_base64url(void **state) { assert_true(res_len == 0); } } + /* static char wikipedia_org_A[] = { 0xae, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x77, @@ -1789,7 +1655,28 @@ doh_cloudflare_GET(void **state) { */ int main(void) { - const struct CMUnitTest tests[] = { + const struct CMUnitTest tests_short[] = { + cmocka_unit_test_setup_teardown(mock_doh_uv_tcp_bind, nm_setup, + nm_teardown), + 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), + }; + + const struct CMUnitTest tests_long[] = { + cmocka_unit_test_setup_teardown(mock_doh_uv_tcp_bind, nm_setup, + nm_teardown), cmocka_unit_test_setup_teardown(doh_parse_GET_query_string, NULL, NULL), cmocka_unit_test_setup_teardown(doh_base64url_to_base64, NULL, @@ -1859,7 +1746,16 @@ main(void) { nm_teardown)*/ }; - return (cmocka_run_group_tests(tests, _setup, _teardown)); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + UNUSED(tests_long); + return (cmocka_run_group_tests(tests_short, _setup, _teardown)); +#else + if (getenv("CI") != NULL || !reuse_supported) { + return (cmocka_run_group_tests(tests_short, _setup, _teardown)); + } else { + return (cmocka_run_group_tests(tests_long, _setup, _teardown)); + } +#endif } #else /* HAVE_CMOCKA */ diff --git a/lib/isc/tests/tls_test_cert_key.h b/lib/isc/tests/tls_test_cert_key.h deleted file mode 100644 index 898b77bd85..0000000000 --- a/lib/isc/tests/tls_test_cert_key.h +++ /dev/null @@ -1,465 +0,0 @@ -/* - * 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 */ diff --git a/util/copyrights b/util/copyrights index 781e928906..81bccb1745 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1988,7 +1988,6 @@ ./lib/isc/tests/testdata/file/keep X 2014,2018,2019,2020,2021 ./lib/isc/tests/time_test.c C 2014,2015,2016,2018,2019,2020,2021 ./lib/isc/tests/timer_test.c C 2018,2019,2020,2021 -./lib/isc/tests/tls_test_cert_key.h C 2021 ./lib/isc/tests/tlsdns_test.c C 2021 ./lib/isc/tests/udp_test.c C 2020,2021 ./lib/isc/tests/uv_wrap.h C 2020,2021 From aa9d51c494599968edf8b9cfa3fca0e989d6bae9 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 2 Feb 2021 22:05:00 -0800 Subject: [PATCH 08/11] tls and http configuration code was unnecessarily complex removed the isc_cfg_http_t and isc_cfg_tls_t structures and the functions that loaded and accessed them; this can be done using normal config parser functions. --- bin/named/server.c | 229 +++++++++++++-------------- lib/isccfg/Makefile.am | 6 +- lib/isccfg/httpconf.c | 180 --------------------- lib/isccfg/include/isccfg/httpconf.h | 69 -------- lib/isccfg/include/isccfg/tlsconf.h | 69 -------- lib/isccfg/tlsconf.c | 194 ----------------------- util/copyrights | 4 - 7 files changed, 114 insertions(+), 637 deletions(-) delete mode 100644 lib/isccfg/httpconf.c delete mode 100644 lib/isccfg/include/isccfg/httpconf.h delete mode 100644 lib/isccfg/include/isccfg/tlsconf.h delete mode 100644 lib/isccfg/tlsconf.c diff --git a/bin/named/server.c b/bin/named/server.c index f46202633a..d553683fa3 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -101,10 +101,8 @@ #include #include -#include #include #include -#include #include #include @@ -400,23 +398,18 @@ 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); +listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, + 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, isc_cfg_http_storage_t *http_servers, - isc_cfg_tls_data_storage_t *tls_storage, - ns_listenelt_t **target); +listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + ns_listenelt_t **target); 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, isc_cfg_http_storage_t *http_servers, - isc_cfg_tls_data_storage_t *tls_storage, - ns_listenlist_t **target); +listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + ns_listenlist_t **target); static isc_result_t configure_forward(const cfg_obj_t *config, dns_view_t *view, @@ -8517,8 +8510,6 @@ load_configuration(const char *filename, named_server_t *server, 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); @@ -8526,9 +8517,6 @@ load_configuration(const char *filename, named_server_t *server, 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); @@ -8600,9 +8588,6 @@ load_configuration(const char *filename, named_server_t *server, INSIST(result == ISC_R_SUCCESS); named_g_httpsport = (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 @@ -9019,10 +9004,9 @@ load_configuration(const char *filename, named_server_t *server, } if (clistenon != NULL) { /* check return code? */ - (void)ns_listenlist_fromconfig( + (void)listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET, &http_storage, - &tls_storage, &listenon); + named_g_mctx, AF_INET, &listenon); } else { /* * Not specified, use default. @@ -9048,10 +9032,9 @@ load_configuration(const char *filename, named_server_t *server, } if (clistenon != NULL) { /* check return code? */ - (void)ns_listenlist_fromconfig( + (void)listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET6, &http_storage, - &tls_storage, &listenon); + named_g_mctx, AF_INET6, &listenon); } else { /* * Not specified, use default. @@ -9812,9 +9795,6 @@ cleanup: 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)); @@ -11020,11 +11000,9 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) { } static isc_result_t -ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, - cfg_aclconfctx_t *actx, isc_mem_t *mctx, - uint16_t family, isc_cfg_http_storage_t *http_servers, - isc_cfg_tls_data_storage_t *tls_storage, - ns_listenlist_t **target) { +listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + ns_listenlist_t **target) { isc_result_t result; const cfg_listelt_t *element; ns_listenlist_t *dlist = NULL; @@ -11041,9 +11019,8 @@ ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, { ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); - result = ns_listenelt_fromconfig(listener, config, actx, mctx, - family, http_servers, - tls_storage, &delt); + result = listenelt_fromconfig(listener, config, actx, mctx, + family, &delt); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -11057,67 +11034,98 @@ cleanup: return (result); } +static const cfg_obj_t * +find_maplist(const cfg_obj_t *config, const char *listname, const char *name) { + isc_result_t result; + const cfg_obj_t *maplist = NULL; + const cfg_listelt_t *elt = NULL; + + REQUIRE(config != NULL); + REQUIRE(name != NULL); + + result = cfg_map_get(config, listname, &maplist); + if (result != ISC_R_SUCCESS) { + return (NULL); + } + + for (elt = cfg_list_first(maplist); elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *map = cfg_listelt_value(elt); + if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) == + 0) { + return (map); + } + } + + return (NULL); +} + /* * Create a listen list from the corresponding configuration * data structure. */ static isc_result_t -ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, - cfg_aclconfctx_t *actx, isc_mem_t *mctx, - uint16_t family, isc_cfg_http_storage_t *http_servers, - isc_cfg_tls_data_storage_t *tls_storage, - ns_listenelt_t **target) { +listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + ns_listenelt_t **target) { isc_result_t result; - const cfg_obj_t *tlsobj, *portobj, *dscpobj, *httpobj; + const cfg_obj_t *tlsobj = NULL, *httpobj = NULL; + const cfg_obj_t *portobj = NULL, *dscpobj = NULL; + const cfg_obj_t *http_server = NULL; in_port_t port = 0; isc_dscp_t dscp = -1; const char *key = NULL, *cert = NULL; - bool tls = false, http = false; + bool do_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. */ tlsobj = cfg_tuple_get(listener, "tls"); if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { - if (!strcmp(cfg_obj_asstring(tlsobj), "ephemeral")) { - tls = true; - } else { - 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); + const char *tlsname = cfg_obj_asstring(tlsobj); + + if (strcmp(tlsname, "ephemeral") != 0) { + const cfg_obj_t *keyobj = NULL, *certobj = NULL; + const cfg_obj_t *tlsmap = NULL; + + tlsmap = find_maplist(config, "tls", tlsname); + if (tlsmap == NULL) { + return (ISC_R_FAILURE); } + + CHECK(cfg_map_get(tlsmap, "key-file", &keyobj)); + key = cfg_obj_asstring(keyobj); + + CHECK(cfg_map_get(tlsmap, "cert-file", &certobj)); + cert = cfg_obj_asstring(certobj); } - if (!tls) { - return (ISC_R_FAILURE); - } + + do_tls = true; } + httpobj = cfg_tuple_get(listener, "http"); if (httpobj != NULL && cfg_obj_isstring(httpobj)) { - if (tls && tls_cert == NULL) { + const char *httpname = cfg_obj_asstring(httpobj); + + if (do_tls && key == NULL) { return (ISC_R_FAILURE); } - http = true; - http_server = cfg_http_find(cfg_obj_asstring(httpobj), - http_servers); + + http_server = find_maplist(config, "http", httpname); 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)); + cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, + "http '%s' is not defined", + cfg_obj_asstring(httpobj)); return (ISC_R_FAILURE); } + + http = true; } + portobj = cfg_tuple_get(listener, "port"); if (!cfg_obj_isuint32(portobj)) { - if (http && tls) { + if (http && do_tls) { if (named_g_httpsport != 0) { port = named_g_httpsport; } else { @@ -11127,7 +11135,7 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, return (result); } } - } else if (http && !tls) { + } else if (http && !do_tls) { if (named_g_httpport != 0) { port = named_g_port; } else { @@ -11137,7 +11145,7 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, return (result); } } - } else if (tls) { + } else if (do_tls) { if (named_g_tlsport != 0) { port = named_g_tlsport; } else { @@ -11162,6 +11170,7 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, if (cfg_obj_asuint32(portobj) >= UINT16_MAX) { cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR, "port value '%u' is out of range", + cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } @@ -11183,14 +11192,11 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, if (http) { INSIST(http_server != NULL); - result = ns_listenelt_from_http(http_server, tls_cert, port, - mctx, &delt); + CHECK(listenelt_http(http_server, key, cert, port, mctx, + &delt)); } else { - result = ns_listenelt_create(mctx, port, dscp, NULL, tls, key, - cert, &delt); - } - if (result != ISC_R_SUCCESS) { - return (result); + CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, key, + cert, &delt)); } result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config, @@ -11201,65 +11207,56 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, return (result); } *target = delt; - return (ISC_R_SUCCESS); + +cleanup: + return (result); } /* * 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) { +listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, + in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target) { isc_result_t result = ISC_R_SUCCESS; ns_listenelt_t *delt = NULL; - 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); + char **endpoints = NULL; + const cfg_obj_t *eplist = NULL; + const cfg_listelt_t *elt = NULL; + size_t len, i = 0; - if (tls) { - INSIST(tls->key_file != NULL); - INSIST(tls->cert_file != NULL); - key = tls->key_file; - cert = tls->cert_file; - } + REQUIRE(target != NULL && *target == NULL); + REQUIRE((key == NULL) == (cert == NULL)); if (port == 0) { - port = tls != NULL ? named_g_httpsport : named_g_httpport; + port = (key != NULL) ? named_g_httpsport : named_g_httpport; } - for (ep = ISC_LIST_HEAD(http->endpoints), i = 0; ep != NULL; - ep = ISC_LIST_NEXT(ep, link), i++) - ; + CHECK(cfg_map_get(http, "endpoints", &eplist)); + len = cfg_list_length(eplist, false); + endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len); - 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); + for (elt = cfg_list_first(eplist); elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *ep = cfg_listelt_value(elt); + const char *path = cfg_obj_asstring(ep); + endpoints[i++] = isc_mem_strdup(mctx, path); } - INSIST(i == http_endpoints_number); + INSIST(i == len); result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, key, - cert, http_endpoints, - http_endpoints_number, &delt); - + cert, endpoints, len, &delt); if (result != ISC_R_SUCCESS) { if (delt != NULL) { ns_listenelt_destroy(delt); } - return result; + return (result); } *target = delt; + +cleanup: return (result); } diff --git a/lib/isccfg/Makefile.am b/lib/isccfg/Makefile.am index e13294281b..3205e3b93b 100644 --- a/lib/isccfg/Makefile.am +++ b/lib/isccfg/Makefile.am @@ -7,21 +7,17 @@ libisccfg_la_HEADERS = \ 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/tlsconf.h + include/isccfg/namedconf.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 = \ diff --git a/lib/isccfg/httpconf.c b/lib/isccfg/httpconf.c deleted file mode 100644 index cd210b53a0..0000000000 --- a/lib/isccfg/httpconf.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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 -#include -#include - -#include - -#include -#include - -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) { - isc_mem_t *mctx = NULL; - - REQUIRE(storage != NULL); - - 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->count--; - - 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->count--; - } - } - - isc_mem_put(mctx, http, sizeof(*http)); - http = next; - } - } - - INSIST(storage->count == 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->count++; - } - - ISC_LINK_INIT(new, link); - ISC_LIST_PREPEND(storage->list, new, link); - storage->count++; - 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->count == 0) { - return (ISC_R_FAILURE); - } - - return (ISC_R_SUCCESS); -} diff --git a/lib/isccfg/include/isccfg/httpconf.h b/lib/isccfg/include/isccfg/httpconf.h deleted file mode 100644 index bf049b9e6d..0000000000 --- a/lib/isccfg/include/isccfg/httpconf.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include - -#include - -#include -#include - -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 count; -} 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 count; -} 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 */ diff --git a/lib/isccfg/include/isccfg/tlsconf.h b/lib/isccfg/include/isccfg/tlsconf.h deleted file mode 100644 index 534236ae65..0000000000 --- a/lib/isccfg/include/isccfg/tlsconf.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include - -#include - -#include - -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 */ diff --git a/lib/isccfg/tlsconf.c b/lib/isccfg/tlsconf.c deleted file mode 100644 index 6320fc5626..0000000000 --- a/lib/isccfg/tlsconf.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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 - -#include - -#include -#include - -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) { - isc_mem_t *mctx = NULL; - - REQUIRE(storage != NULL); - - 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 = NULL; - 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); - } - INSIST(key_file != NULL); - - if (cfg_map_get(map, "cert-file", &cert_file) != ISC_R_SUCCESS) { - return (ISC_R_FAILURE); - } - INSIST(cert_file != NULL); - - (void)cfg_map_get(map, "dh-param", &dh_param); - (void)cfg_map_get(map, "protocols", &protocols); - (void)cfg_map_get(map, "ciphers", &ciphers); - - new = isc_mem_get(mctx, sizeof(*new)); - *new = (isc_cfg_tls_obj_t){ - .name = isc_mem_strdup(mctx, - cfg_obj_asstring(map->value.map.id)), - .key_file = isc_mem_strdup(mctx, cfg_obj_asstring(key_file)), - .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); - } - INSIST(tls != NULL); - - cfg_tls_storage_clear(storage); - - 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); -} diff --git a/util/copyrights b/util/copyrights index 81bccb1745..137caa0fba 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2105,22 +2105,18 @@ ./lib/isccc/win32/libisccc.vcxproj.user X 2013,2018,2019,2020,2021 ./lib/isccfg/aclconf.c C 1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 ./lib/isccfg/dnsconf.c C 2009,2016,2018,2019,2020,2021 -./lib/isccfg/httpconf.c C 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/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/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 -./lib/isccfg/tlsconf.c C 2021 ./lib/isccfg/win32/DLLMain.c C 2001,2004,2007,2016,2018,2019,2020,2021 ./lib/isccfg/win32/libisccfg.def X 2001,2002,2005,2009,2010,2011,2013,2014,2015,2016,2018,2019,2020,2021 ./lib/isccfg/win32/libisccfg.vcxproj.filters.in X 2013,2014,2015,2016,2018,2019,2020 From fe99484e14acf04d76bced13e8d12cd954c18d2d Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 2 Feb 2021 23:34:24 -0800 Subject: [PATCH 09/11] support "tls ephemeral" with https --- bin/named/server.c | 25 ++++++++++--------------- lib/ns/include/ns/listenlist.h | 4 ++-- lib/ns/listenlist.c | 8 ++++---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index d553683fa3..e4e80f6f81 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -398,8 +398,9 @@ static void named_server_reload(isc_task_t *task, isc_event_t *event); static isc_result_t -listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, - in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target); +listenelt_http(const cfg_obj_t *http, bool tls, const char *key, + const char *cert, in_port_t port, isc_mem_t *mctx, + ns_listenelt_t **target); static isc_result_t listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, @@ -11108,10 +11109,6 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, if (httpobj != NULL && cfg_obj_isstring(httpobj)) { const char *httpname = cfg_obj_asstring(httpobj); - if (do_tls && key == NULL) { - return (ISC_R_FAILURE); - } - http_server = find_maplist(config, "http", httpname); if (http_server == NULL) { cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, @@ -11192,7 +11189,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, if (http) { INSIST(http_server != NULL); - CHECK(listenelt_http(http_server, key, cert, port, mctx, + CHECK(listenelt_http(http_server, do_tls, key, cert, port, mctx, &delt)); } else { CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, key, @@ -11212,12 +11209,10 @@ cleanup: return (result); } -/* - * Create a listen list for HTTP/HTTPS - */ static isc_result_t -listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, - in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target) { +listenelt_http(const cfg_obj_t *http, bool tls, const char *key, + const char *cert, in_port_t port, isc_mem_t *mctx, + ns_listenelt_t **target) { isc_result_t result = ISC_R_SUCCESS; ns_listenelt_t *delt = NULL; char **endpoints = NULL; @@ -11229,7 +11224,7 @@ listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, REQUIRE((key == NULL) == (cert == NULL)); if (port == 0) { - port = (key != NULL) ? named_g_httpsport : named_g_httpport; + port = tls ? named_g_httpsport : named_g_httpport; } CHECK(cfg_map_get(http, "endpoints", &eplist)); @@ -11245,8 +11240,8 @@ listenelt_http(const cfg_obj_t *http, const char *key, const char *cert, INSIST(i == len); - result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, key, - cert, endpoints, len, &delt); + result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls, + key, cert, endpoints, len, &delt); if (result != ISC_R_SUCCESS) { if (delt != NULL) { ns_listenelt_destroy(delt); diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index de30a5747b..8393d67980 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -71,8 +71,8 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, 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, + dns_acl_t *acl, bool tls, const char *key, + const char *cert, char **endpoints, size_t nendpoints, ns_listenelt_t **target); /*%< * Create a listen-on list element for HTTP(S). diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 5fbb59db9f..2d88c8654f 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -58,8 +58,8 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, 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, + dns_acl_t *acl, bool tls, const char *key, + const char *cert, char **endpoints, size_t nendpoints, ns_listenelt_t **target) { isc_result_t result; @@ -67,8 +67,8 @@ ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp, REQUIRE(endpoints != NULL && *endpoints != NULL); REQUIRE(nendpoints > 0); - result = ns_listenelt_create(mctx, http_port, dscp, acl, key != NULL, - key, cert, target); + result = ns_listenelt_create(mctx, http_port, dscp, acl, tls, key, cert, + target); if (result == ISC_R_SUCCESS) { (*target)->is_http = true; (*target)->http_endpoints = endpoints; From 0aacabc6dc091ce3beb2d3a87d240ea7ddad8b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Wed, 3 Feb 2021 10:27:45 +0100 Subject: [PATCH 10/11] Drop gcc:sid:i386 from GitLab CI Building sid-i386 in Docker no longer works and we don't have a viable alternative now, so dropping gcc:sid:i386 is our only option in this very moment. --- .gitlab-ci.yml | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d6156b99e0..ae00a081ab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -70,11 +70,6 @@ stages: - linux - amd64 -.linux-i386: &linux_i386 - tags: - - linux - - i386 - .linux-stress-amd64: &linux_stress_amd64 tags: - amd64 @@ -133,10 +128,6 @@ stages: image: "$CI_REGISTRY_IMAGE:debian-sid-amd64" <<: *linux_amd64 -.debian-sid-i386: &debian_sid_i386_image - image: "$CI_REGISTRY_IMAGE:debian-sid-i386" - <<: *linux_i386 - # openSUSE Tumbleweed .tumbleweed-latest-amd64: &tumbleweed_latest_amd64_image @@ -837,30 +828,6 @@ unit:gcc:tarball: - schedules - tags -# Jobs for regular GCC builds on Debian "sid" (i386) - -gcc:sid:i386: - variables: - CC: gcc - CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "--with-libidn2" - <<: *debian_sid_i386_image - <<: *build_job - -system:gcc:sid:i386: - <<: *debian_sid_i386_image - <<: *system_test_job - needs: - - job: gcc:sid:i386 - artifacts: true - -unit:gcc:sid:i386: - <<: *debian_sid_i386_image - <<: *unit_test_job - needs: - - job: gcc:sid:i386 - artifacts: true - # Jobs for debug GCC builds on openSUSE Tumbleweed (amd64) gcc:tumbleweed:amd64: From 91718fe4fb49559271f3319c70539b80a146b5fb Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Tue, 2 Feb 2021 15:47:52 -0800 Subject: [PATCH 11/11] CHANGES, release notes --- CHANGES | 10 ++++++---- doc/notes/notes-current.rst | 12 ++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index cd946939a1..1f8591e7ae 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,9 @@ -5576. [func] Initial support for DNS-over-HTTP(S). BIND now - includes DNS-over-HTTP(S) layer built on top of nghttp2. - Both encrypted and unencrypted HTTP/2 connections - are supported. [GL !4566] +5576. [experimental] Initial server-side implementation of DNS-over-HTTPS + (DoH). Support for both TLS-encrypted and unencrypted + HTTP/2 connections has been added to the network manager + and integrated into named. (Note: there is currently no + client-side support for DNS-over-HTTPS; this will be + added to dig in a future release.) [GL #1144] 5575. [bug] When migrating to dnssec-policy, BIND considered keys with the "Inactive" and/or "Delete" timing metadata as diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index ae6b01a7e2..ba95f08b24 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -52,12 +52,12 @@ New Features 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). [GL !4566] +- ``named`` now supports DNS-over-HTTPS (DoH). Both TLS-encrypted and + unencrypted HTTP/2 connections are supported (the latter may be used to + offload encryption to other software). + + Note that there is no client-side support for HTTPS as yet; this will be + added to ``dig`` in a future release. [GL #1144] Removed Features ~~~~~~~~~~~~~~~~