From c0808532e18bcd68b4a71c982468355ec4e179f6 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 26 Jul 2022 17:07:19 +0300 Subject: [PATCH 01/33] TLS: isc_nm_bad_request() and isc__nmsocket_reset() support This commit adds implementations of isc_nm_bad_request() and isc__nmsocket_reset() to the generic TLS stream code in order to make it more compatible with TCP code. --- lib/isc/netmgr/netmgr-int.h | 3 +++ lib/isc/netmgr/netmgr.c | 13 +++++++++---- lib/isc/netmgr/tlsstream.c | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index d467b81d04..4bc5fc22a3 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1563,6 +1563,9 @@ isc__nm_tls_cleartimeout(isc_nmhandle_t *handle); * around. */ +void +isc__nmsocket_tls_reset(isc_nmsocket_t *sock); + const char * isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 9563f27ef7..d3ba6f2382 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -2330,6 +2330,11 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) { */ REQUIRE(sock->parent == NULL); break; +#ifdef HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + isc__nmsocket_tls_reset(sock); + return; +#endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); break; @@ -2548,6 +2553,10 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { return; case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: + case isc_nm_tcpsocket: +#if HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: +#endif /* HAVE_LIBNGHTTP2 */ REQUIRE(sock->parent == NULL); isc__nmsocket_reset(sock); return; @@ -2555,10 +2564,6 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { case isc_nm_httpsocket: isc__nm_http_bad_request(handle); return; -#endif /* HAVE_LIBNGHTTP2 */ - case isc_nm_tcpsocket: -#if HAVE_LIBNGHTTP2 - case isc_nm_tlssocket: #endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index a1e59b54ad..e1602461ee 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -1203,6 +1203,18 @@ isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle, } } +void +isc__nmsocket_tls_reset(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); + isc__nmsocket_reset(sock->outerhandle->sock); + } +} + const char * isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; From f18a9b3743ea48bb0c0b918923493bd242526e4c Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 26 Jul 2022 17:36:32 +0300 Subject: [PATCH 02/33] TLS: add isc__nmsocket_timer_running() support This commit adds isc__nmsocket_timer_running() support to the generic TLS code in order to make it more compatible with TCP. --- lib/isc/netmgr/netmgr-int.h | 3 +++ lib/isc/netmgr/netmgr.c | 9 +++++++++ lib/isc/netmgr/tlsstream.c | 14 ++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 4bc5fc22a3..6e8a78c688 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1583,6 +1583,9 @@ void isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout); +bool +isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock); + void isc__nm_tls_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index d3ba6f2382..ba97f97d0b 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1545,6 +1545,15 @@ bool isc__nmsocket_timer_running(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { +#ifdef HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + return (isc__nmsocket_tls_timer_running(sock)); +#endif /* HAVE_LIBNGHTTP2 */ + default: + break; + } + return (uv_is_active((uv_handle_t *)&sock->read_timer)); } diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index e1602461ee..5aae50419b 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -1215,6 +1215,20 @@ isc__nmsocket_tls_reset(isc_nmsocket_t *sock) { } } +bool +isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); + return (isc__nmsocket_timer_running(sock->outerhandle->sock)); + } + + return (false); +} + const char * isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; From f4760358f83919b2e88a4d7307e7b8c9b496b777 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 27 Jul 2022 16:26:55 +0300 Subject: [PATCH 03/33] TLS: expose the ability to (re)start and stop underlying read timer This commit adds implementation of isc__nmsocket_timer_restart() and isc__nmsocket_timer_stop() for generic TLS code in order to make its interface more compatible with that of TCP. --- lib/isc/netmgr/netmgr-int.h | 6 ++++++ lib/isc/netmgr/netmgr.c | 20 ++++++++++++++++++++ lib/isc/netmgr/tlsstream.c | 24 ++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 6e8a78c688..c23a53731a 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1586,6 +1586,12 @@ isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle, bool isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock); +void +isc__nmsocket_tls_timer_restart(isc_nmsocket_t *sock); + +void +isc__nmsocket_tls_timer_stop(isc_nmsocket_t *sock); + void isc__nm_tls_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index ba97f97d0b..fa9f60ec18 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1511,6 +1511,16 @@ void isc__nmsocket_timer_restart(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { +#ifdef HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + isc__nmsocket_tls_timer_restart(sock); + return; +#endif /* HAVE_LIBNGHTTP2 */ + default: + break; + } + if (uv_is_closing((uv_handle_t *)&sock->read_timer)) { return; } @@ -1574,6 +1584,16 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { +#ifdef HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + isc__nmsocket_tls_timer_stop(sock); + return; +#endif /* HAVE_LIBNGHTTP2 */ + default: + break; + } + /* uv_timer_stop() is idempotent, no need to check if running */ r = uv_timer_stop(&sock->read_timer); diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 5aae50419b..485c70410a 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -1229,6 +1229,30 @@ isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock) { return (false); } +void +isc__nmsocket_tls_timer_restart(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); + isc__nmsocket_timer_restart(sock->outerhandle->sock); + } +} + +void +isc__nmsocket_tls_timer_stop(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + REQUIRE(VALID_NMSOCK(sock->outerhandle->sock)); + isc__nmsocket_timer_stop(sock->outerhandle->sock); + } +} + const char * isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; From 9aabd55725bad59ac33befe49a10a0fd371b1e83 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 18 Oct 2022 15:21:10 +0300 Subject: [PATCH 04/33] TCP: add manual read timer control mode This commit adds a manual read timer control mode to the TCP code (adding isc__nmhandle_set_manual_timer() as the interface to it). Manual read timer control mode suppresses read timer restarting the read timer when receiving any amount of data. This way the read timer can be controlled manually using: * isc__nmsocket_timer_start(); * isc__nmsocket_timer_stop(); * isc__nmsocket_timer_restart(). The change is required to make it possible to implement more sophisticated read timer control policies in DNS transports, built on top of TCP. --- lib/isc/netmgr/netmgr-int.h | 11 +++++++++++ lib/isc/netmgr/netmgr.c | 19 +++++++++++++++++++ lib/isc/netmgr/tcp.c | 21 +++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index c23a53731a..18833bfbde 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1062,6 +1062,7 @@ struct isc_nmsocket { atomic_int_fast32_t active_child_connections; bool barrier_initialised; + bool manual_read_timer; #ifdef NETMGR_TRACE void *backtrace[TRACE_SIZE]; int backtrace_size; @@ -1354,6 +1355,9 @@ isc__nm_tcp_settimeout(isc_nmhandle_t *handle, uint32_t timeout); * Set the read timeout for the TCP socket associated with 'handle'. */ +void +isc__nmhandle_tcp_set_manual_timer(isc_nmhandle_t *handle, const bool manual); + void isc__nm_async_tcplisten(isc__networker_t *worker, isc__netievent_t *ev0); void @@ -2008,3 +2012,10 @@ isc__nmsocket_log(const isc_nmsocket_t *sock, int level, const char *fmt, ...) void isc__nmhandle_log(const isc_nmhandle_t *handle, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); + +void +isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual); +/* + * Set manual read timer control mode - so that it will not get reset + * automatically on read nor get started when read is initiated. + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index fa9f60ec18..350e3ba963 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -2898,6 +2898,25 @@ isc__nmhandle_log(const isc_nmhandle_t *handle, int level, const char *fmt, isc__nmsocket_log(handle->sock, level, "handle %p: %s", handle, msgbuf); } +void +isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + + switch (sock->type) { + case isc_nm_tcpsocket: + isc__nmhandle_tcp_set_manual_timer(handle, manual); + return; + default: + break; + }; + + UNREACHABLE(); +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 52e5abac16..c994622d0c 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -762,7 +762,9 @@ isc__nm_tcp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { goto failure; } - isc__nmsocket_timer_start(sock); + if (!sock->manual_read_timer) { + isc__nmsocket_timer_start(sock); + } return; failure: @@ -831,7 +833,7 @@ isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { isc__nm_readcb(sock, req, ISC_R_SUCCESS, false); /* The readcb could have paused the reading */ - if (sock->reading) { + if (sock->reading && !sock->manual_read_timer) { /* The timer will be updated */ isc__nmsocket_timer_restart(sock); } @@ -1225,3 +1227,18 @@ isc__nm_tcp_shutdown(isc_nmsocket_t *sock) { isc__nmsocket_prep_destroy(sock); } } + +void +isc__nmhandle_tcp_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tcpsocket); + REQUIRE(sock->tid == isc_tid()); + REQUIRE(!sock->reading); + REQUIRE(!sock->recv_read); + + sock->manual_read_timer = manual; +} From 15e626f1cac1f7f546ab8ed0600cf7a44ebeb252 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 20 Oct 2022 15:40:51 +0300 Subject: [PATCH 05/33] TLS: add manual read timer control mode This commit adds manual read timer control mode, similarly to TCP. This way the read timer can be controlled manually using: * isc__nmsocket_timer_start(); * isc__nmsocket_timer_stop(); * isc__nmsocket_timer_restart(). The change is required to make it possible to implement more sophisticated read timer control policies in DNS transports, built on top of TLS. --- lib/isc/netmgr/netmgr-int.h | 3 +++ lib/isc/netmgr/netmgr.c | 5 ++++ lib/isc/netmgr/tlsstream.c | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 18833bfbde..274aec9a68 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1570,6 +1570,9 @@ isc__nm_tls_cleartimeout(isc_nmhandle_t *handle); void isc__nmsocket_tls_reset(isc_nmsocket_t *sock); +void +isc__nmhandle_tls_set_manual_timer(isc_nmhandle_t *handle, const bool manual); + const char * isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 350e3ba963..4c3c3107f3 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -2910,6 +2910,11 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { case isc_nm_tcpsocket: isc__nmhandle_tcp_set_manual_timer(handle, manual); return; +#if HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + isc__nmhandle_tls_set_manual_timer(handle, manual); + return; +#endif /* HAVE_LIBNGHTTP2 */ default: break; }; diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 485c70410a..e99b03b4bb 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -458,6 +458,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, sock->tlsstream.state = TLS_HANDSHAKE; rv = tls_try_handshake(sock, NULL); INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 0); + isc__nmsocket_timer_restart(sock); } else if (sock->tlsstream.state == TLS_CLOSED) { return; } else { /* initialised and doing I/O */ @@ -494,6 +495,11 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, 1); INSIST(!atomic_load(&sock->client)); finish = true; + } else if (sock->tlsstream.state == TLS_IO && + hs_result == ISC_R_SUCCESS && + !sock->tlsstream.server) + { + INSIST(atomic_load(&sock->client)); } } } else if (send_data != NULL) { @@ -523,6 +529,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, if (sock->tlsstream.state >= TLS_IO && sock->recv_cb != NULL && was_reading && sock->statichandle != NULL && !finish) { + bool was_new_data = false; uint8_t recv_buf[TLS_BUF_SIZE]; INSIST(sock->tlsstream.state > TLS_HANDSHAKE); while ((rv = SSL_read_ex(sock->tlsstream.tls, recv_buf, @@ -532,6 +539,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, region = (isc_region_t){ .base = &recv_buf[0], .length = len }; + was_new_data = true; INSIST(VALID_NMHANDLE(sock->statichandle)); sock->recv_cb(sock->statichandle, ISC_R_SUCCESS, ®ion, sock->recv_cbarg); @@ -570,8 +578,29 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, break; } } + + if (was_new_data && !sock->manual_read_timer) { + /* + * Some data has been decrypted, it is the right + * time to stop the read timer as it will be + * restarted on the next read attempt. + */ + isc__nmsocket_timer_stop(sock); + } } } + + /* + * Setting 'finish' to 'true' means that we are about to close the + * TLS stream (we intend to send TLS shutdown message to the + * remote side). After that no new data can be received, so we + * should stop the timer regardless of the + * 'sock->manual_read_timer' value. + */ + if (finish) { + isc__nmsocket_timer_stop(sock); + } + errno = 0; tls_status = SSL_get_error(sock->tlsstream.tls, rv); saved_errno = errno; @@ -633,6 +662,9 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, INSIST(VALID_NMHANDLE(sock->outerhandle)); isc_nm_read(sock->outerhandle, tls_readcb, sock); + if (!sock->manual_read_timer) { + isc__nmsocket_timer_start(sock); + } return; default: result = tls_error_to_result(tls_status, sock->tlsstream.state, @@ -764,6 +796,7 @@ tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { RUNTIME_CHECK(result == ISC_R_SUCCESS); /* TODO: catch failure code, detach tlssock, and log the error */ + isc__nmhandle_set_manual_timer(tlssock->outerhandle, true); tls_do_bio(tlssock, NULL, NULL, false); return (result); } @@ -1081,6 +1114,7 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { */ handle->sock->tlsstream.tlssocket = tlssock; + isc__nmhandle_set_manual_timer(tlssock->outerhandle, true); tls_do_bio(tlssock, NULL, NULL, false); return; error: @@ -1355,3 +1389,16 @@ tls_try_shutdown(isc_tls_t *tls, const bool force) { (void)SSL_shutdown(tls); } } + +void +isc__nmhandle_tls_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + REQUIRE(sock->tid == isc_tid()); + + sock->manual_read_timer = manual; +} From c0c59b55ab0776fe6a2f99e6b5fe585030018cc6 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 3 Aug 2022 14:46:33 +0300 Subject: [PATCH 06/33] TLS: add an internal function isc__nmhandle_get_selected_alpn() The added function provides the interface for getting an ALPN tag negotiated during TLS connection establishment. The new function can be used by higher level transports. --- lib/isc/netmgr/http.c | 3 +-- lib/isc/netmgr/netmgr-int.h | 17 +++++++++++++++++ lib/isc/netmgr/netmgr.c | 21 +++++++++++++++++++++ lib/isc/netmgr/tlsstream.c | 15 +++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 7cb69b5f97..962a5b4386 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -1399,8 +1399,7 @@ transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { INSIST(transp_sock->type == isc_nm_tlssocket); - isc_tls_get_selected_alpn(transp_sock->tlsstream.tls, &alpn, - &alpnlen); + isc__nmhandle_get_selected_alpn(handle, &alpn, &alpnlen); if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN || memcmp(NGHTTP2_PROTO_VERSION_ID, alpn, NGHTTP2_PROTO_VERSION_ID_LEN) != 0) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 274aec9a68..fe99e3a125 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1603,6 +1603,11 @@ void isc__nm_tls_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); +void +isc__nmhandle_tls_get_selected_alpn(isc_nmhandle_t *handle, + const unsigned char **alpn, + unsigned int *alpnlen); + void isc__nm_http_stoplistening(isc_nmsocket_t *sock); @@ -2022,3 +2027,15 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual); * Set manual read timer control mode - so that it will not get reset * automatically on read nor get started when read is initiated. */ + +void +isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, + const unsigned char **alpn, + unsigned int *alpnlen); +/* + * Returns a non zero terminated ALPN identifier via 'alpn'. The + * length of the identifier is returned via 'alpnlen'. If after the + * call either 'alpn == NULL' or 'alpnlen == 0', then identifier was + * not negotiated of the underlying protocol of the connection + * represented via the given handle does not support ALPN. + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 4c3c3107f3..bca6f3c76b 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -2922,6 +2922,27 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { UNREACHABLE(); } +void +isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, + const unsigned char **alpn, + unsigned int *alpnlen) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + + switch (sock->type) { +#if HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + isc__nmhandle_tls_get_selected_alpn(handle, alpn, alpnlen); + return; +#endif /* HAVE_LIBNGHTTP2 */ + default: + break; + }; +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index e99b03b4bb..834a386ce6 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -1402,3 +1402,18 @@ isc__nmhandle_tls_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { sock->manual_read_timer = manual; } + +void +isc__nmhandle_tls_get_selected_alpn(isc_nmhandle_t *handle, + const unsigned char **alpn, + unsigned int *alpnlen) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_tlssocket); + REQUIRE(sock->tid == isc_tid()); + + isc_tls_get_selected_alpn(sock->tlsstream.tls, alpn, alpnlen); +} From cbb758abd48d27748049537c15beb31436b465a0 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 7 Jun 2022 13:14:29 +0300 Subject: [PATCH 07/33] Add isc_dnsbuffer_t implementation This commit adds "isc_dnsbuffer_t" object implementation, a thin wrapper on top of "isc_buffer_t" which has the following characteristics: * provides interface specifically atuned for handling/generating DNS messages, especially in the format used for DNS messages over TCP; * avoids allocating dynamic memory when handling small DNS messages, while transparently switching to using dynamic memory when handling larger messages. This approach significantly reduces pressure on the memory allocator, as most of the DNS messages are small. --- .gitlab-ci.yml | 1 + lib/isc/Makefile.am | 1 + lib/isc/include/isc/dnsbuffer.h | 446 ++++++++++++++++++++++++++++++++ 3 files changed, 448 insertions(+) create mode 100644 lib/isc/include/isc/dnsbuffer.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a1ddc9b67..6e14d6d634 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1521,6 +1521,7 @@ gcov: - for SRC in dns isc; do for DST in dns isc ns; do cp -f "lib/${SRC}/include/${SRC}"/*.h "lib/${DST}/"; done; done - find bin lib -maxdepth 1 -mindepth 1 -type d -exec cp -f lib/isc/include/isc/buffer.h "{}" \; - cp -f lib/isc/include/isc/buffer.h lib/isc/netmgr/buffer.h + - cp -f lib/isc/include/isc/dnsbuffer.h lib/isc/netmgr/dnsbuffer.h # Help gcovr find dlz_dbi.c file - for DST in ldap mysql mysqldyn sqlite3 wildcard; do cp contrib/dlz/modules/common/dlz_dbi.c "contrib/dlz/modules/${DST}"; done # Generate XML file in the Cobertura XML format suitable for use by GitLab diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index e3ead9b465..3d55874dab 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -25,6 +25,7 @@ libisc_la_HEADERS = \ include/isc/crc64.h \ include/isc/deprecated.h \ include/isc/dir.h \ + include/isc/dnsbuffer.h \ include/isc/endian.h \ include/isc/entropy.h \ include/isc/errno.h \ diff --git a/lib/isc/include/isc/dnsbuffer.h b/lib/isc/include/isc/dnsbuffer.h new file mode 100644 index 0000000000..c79c7adb44 --- /dev/null +++ b/lib/isc/include/isc/dnsbuffer.h @@ -0,0 +1,446 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#define ISC_DNSBUFFER_STATIC_BUFFER_SIZE (512) +#define ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE (ISC_BUFFER_INCR * 2) + +typedef struct isc_dnsbuffer { + isc_buffer_t *current; /* pointer to the currently used buffer */ + isc_buffer_t stbuf; /* static memory buffer */ + uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE]; /* storage for the static + buffer */ + isc_buffer_t *dynbuf; /* resizeable dynamic memory buffer */ + isc_mem_t *mctx; +} isc_dnsbuffer_t; +/* + * The 'isc_dnsbuffer_t' object implementation is a thin wrapper on + * top of 'isc_buffer_t' which has the following characteristics: + * + * - provides interface specifically atuned for handling/generating + * DNS messages, especially in the format used for DNS messages over + * TCP; + * + * - avoids allocating dynamic memory when handling small DNS + * messages, while transparently switching to using dynamic memory + * when handling larger messages. This approach significantly + * reduces pressure on the memory allocator, as most of the DNS + * messages are small. + */ + +static inline void +isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx); +/*!< + * \brief Initialise the 'isc_dnsbuffer_t' object, keep a reference to + * 'memctx' inside the object. + * + * Requires: + *\li 'dnsbuf' is not NULL; + *\li 'memctx' is not NULL. + */ + +static inline void +isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Un-initialise the 'isc_dnsbuffer_t' object, de-allocate any + *dynamically allocated memory, detach from an internal memory context + * reference. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline isc_dnsbuffer_t * +isc_dnsbuffer_new(isc_mem_t *memctx); +/*!< + * \brief Allocate and initialise a new 'isc_dnsbuffer_t' object, keep a + * reference to 'memctx' inside the object. + * + * Requires: + *\li 'dnsbuf' is not NULL; + *\li 'memctx' is not NULL. + */ + +static inline void +isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf); +/*!< + * \brief Un-initialise and de-allocate the given 'isc_dnsbuffer_t' object. + * + * Requires: + *\li 'pdnsbuf' is not NULL; + *\li 'pdnsbuf' does not point to NULL. + */ + +static inline void +isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Clear the given 'isc_dnsbuffer_t' object (make it empty). + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline unsigned int +isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Return the total length of the internal memory buffer of + * the given 'isc_dnsbuffer_t' object. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline unsigned int +isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Return the total number of used bytes from the internal + * memory buffer of the given 'isc_dnsbuffer_t' object. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline unsigned int +isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Return the total number of remaining (unprocessed data) + * bytes from the internal memory buffer of the given + * 'isc_dnsbuffer_t' object. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf, + isc_region_t *region); +/*!< + * \brief Make the given 'isc_region_t' object reference remaining + * (unprocessed) data from the internal memory buffer of the given + * 'isc_dnsbuffer_t' object. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Compact the internal used memory region of the internal + * memory buffer of the given 'isc_dnsbuffer_t' object so that it + * occurs at the start of the memory buffer. Then used region is + * shrunk by the size of the processed (consumed) region, and the + * consumed region is then made empty. This way the previously + * processed (consumed) amount of memory can be used again without + * resizing/reallocating the buffer. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline bool +isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Compact the internal used memory region of the internal + * memory buffer of the given 'isc_dnsbuffer_t' object in the case + * when the processed (consumed) region is larger or equal in size to + * the unprocessed one. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n); +/*!< + * \brief Consume the given number of bytes from the beginning of + * the unprocessed data region of the given 'isc_dnsbuffer_t' object. + * The call moves the 'current' unprocessed data region pointer by the + * given number of bytes. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf, + const unsigned int buf_size); +/*!< + * \brief Copy 'buf_size' bytes from the location pointed to by + * 'buf' pointer to the end of the unprocessed data region of the + * given 'isc_dnsbuffer_t' object. Resize/reallocate the internal + * memory buffer if it is too small. + * + * Requires: + *\li 'dnsbuf' is not NULL; + *\li 'buf' is not NULL; + *\li 'buf_size' is greater than '0'. + */ + +static inline uint8_t * +isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Return the pointer to the beginning of unprocessed data + * region of the given 'isc_dnsbuffer_t' object ("current pointer"). + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline uint16_t +isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Lookup an unsigned short (16-bit) value in + * big-endian/network byte order at the beginning of unprocessed data + * region of the given 'isc_dnsbuffer_t' object. + * + * If there is not enough data available in the region, '0' will be + * returned. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline uint16_t +isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf); +/*!< + * \brief Read an unsigned short (16-bit) value in + * big-endian/network byte order at the beginning of unprocessed data + * region of the given 'isc_dnsbuffer_t' object. + * + * If there is not enough data available in the region, '0' will be + * returned. + * + * In the case, when the data has been read successfully, the start of + * the unprocessed data region is advanced by two bytes. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf, + const uint16_t v); +/*!< + * \brief Append a given unsigned short (16-bit) value 'v' converted + * into big-endian/network byte order at the end of unprocessed data + * region of the given 'isc_dnsbuffer_t' object. Resize/reallocate the + * internal memory buffer if it is too small to hold the appended data. + * + * Requires: + *\li 'dnsbuf' is not NULL. + */ + +static inline void +isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx) { + REQUIRE(dnsbuf != NULL); + REQUIRE(memctx != NULL); + *dnsbuf = (isc_dnsbuffer_t){ .current = &dnsbuf->stbuf }; + isc_buffer_init(&dnsbuf->stbuf, &dnsbuf->buf[0], sizeof(dnsbuf->buf)); + isc_mem_attach(memctx, &dnsbuf->mctx); +} + +static inline void +isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + isc_buffer_clear(&dnsbuf->stbuf); + if (dnsbuf->dynbuf != NULL) { + isc_buffer_free(&dnsbuf->dynbuf); + } + + if (dnsbuf->mctx != NULL) { + isc_mem_detach(&dnsbuf->mctx); + } +} + +static inline isc_dnsbuffer_t * +isc_dnsbuffer_new(isc_mem_t *memctx) { + isc_dnsbuffer_t *newbuf; + + REQUIRE(memctx != NULL); + + newbuf = isc_mem_get(memctx, sizeof(*newbuf)); + isc_dnsbuffer_init(newbuf, memctx); + + return (newbuf); +} + +static inline void +isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf) { + isc_dnsbuffer_t *restrict buf = NULL; + isc_mem_t *memctx = NULL; + REQUIRE(pdnsbuf != NULL && *pdnsbuf != NULL); + + buf = *pdnsbuf; + + isc_mem_attach(buf->mctx, &memctx); + isc_dnsbuffer_uninit(buf); + isc_mem_putanddetach(&memctx, buf, sizeof(*buf)); + + *pdnsbuf = NULL; +} + +static inline void +isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + isc_buffer_clear(dnsbuf->current); +} + +static inline unsigned int +isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + return (isc_buffer_length(dnsbuf->current)); +} + +static inline unsigned int +isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + return (isc_buffer_usedlength(dnsbuf->current)); +} + +static inline unsigned int +isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + return (isc_buffer_remaininglength(dnsbuf->current)); +} + +static inline void +isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf, + isc_region_t *region) { + REQUIRE(dnsbuf != NULL); + REQUIRE(region != NULL); + isc_buffer_remainingregion(dnsbuf->current, region); +} + +static inline void +isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + isc_buffer_compact(dnsbuf->current); +} + +static inline bool +isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + if (isc_buffer_consumedlength(dnsbuf->current) >= + isc_dnsbuffer_remaininglength(dnsbuf)) + { + isc_dnsbuffer_compact(dnsbuf); + return (true); + } + + return (false); +} + +static inline void +isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n) { + REQUIRE(dnsbuf != NULL); + isc_buffer_forward(dnsbuf->current, n); +} + +static inline void +isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf, + const unsigned int buf_size) { + REQUIRE(dnsbuf != NULL); + REQUIRE(buf != NULL); + REQUIRE(buf_size > 0); + if (!(dnsbuf->current == &dnsbuf->stbuf && + isc_buffer_availablelength(dnsbuf->current) >= buf_size) && + dnsbuf->dynbuf == NULL) + { + isc_region_t remaining = { 0 }; + unsigned int total_size = 0; + + isc_buffer_remainingregion(&dnsbuf->stbuf, &remaining); + total_size = remaining.length + buf_size; + if (total_size < ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE) { + total_size = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE; + } + isc_buffer_allocate(dnsbuf->mctx, &dnsbuf->dynbuf, total_size); + isc_buffer_setautorealloc(dnsbuf->dynbuf, true); + if (remaining.length > 0) { + isc_buffer_putmem(dnsbuf->dynbuf, remaining.base, + remaining.length); + } + + dnsbuf->current = dnsbuf->dynbuf; + } + + isc_buffer_putmem(dnsbuf->current, buf, buf_size); +} + +static inline uint8_t * +isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + return (isc_buffer_current(dnsbuf->current)); +} + +static inline uint16_t +isc__dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) { + uint16_t v; + uint8_t *p = (uint8_t *)isc_dnsbuffer_current(dnsbuf); + + v = p[0] << 8; + v |= p[1] & 0xFF; + + return (v); +} + +static inline uint16_t +isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) { + REQUIRE(dnsbuf != NULL); + if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) { + return (0); + } + + return (isc__dnsbuffer_peek_uint16be(dnsbuf)); +} + +static inline uint16_t +isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf) { + uint16_t v; + + REQUIRE(dnsbuf != NULL); + + if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) { + return (0); + } + + v = isc__dnsbuffer_peek_uint16be(dnsbuf); + + isc_dnsbuffer_consume(dnsbuf, sizeof(uint16_t)); + + return (v); +} + +static inline void +isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf, + const uint16_t v) { + uint8_t b[2] = { 0 }; + + REQUIRE(dnsbuf != NULL); + + b[0] = v >> 8; + b[1] = v & 0xFF; + + isc_dnsbuffer_putmem(dnsbuf, b, sizeof(b)); +} From 338cf3e4677e1ad793c5ff789b0b3a4d7e453426 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 13 Jun 2022 19:20:08 +0300 Subject: [PATCH 08/33] Add isc_dnsstream_assembler_t implementation This commit adds the implementation for an "isc_dnsstream_assembler_t" object. The object is built on top of "isc_dnsbuffer_t" and is intended to encapsulate the state machine used for handling DNS messages received in the format used for messages transmitted over TCP. The idea is that the object accepts the input data received from a socket, tries to assemble DNS messages from the incoming data and calls the callback which contains the status of the incoming data as well as a pointer to the memory region referencing the data of the assembled message. It is capable of assembling DNS messages no matter how torn apart they are when sent over network. The following statuses might be passed to the callback: * ISC_R_SUCCESS - a message has been successfully assembled; * ISC_R_NOMORE - not enough data has been processed to assemble a message; * ISC_R_RANGE - there was an attempt to process a zero-sized DNS message (someone attempts to send us junk data). One could say that the object replaces the implementation of "isc__nm_*_processbuffer()" functions used by the old TCP DNS and TLS DNS transports with a better defined state machine completely decoupled from the networking code itself. Such a design makes it trivial to write unit tests for it, leading to better verification of its correctness. Another important difference is directly related to the fact that it is built on top of "isc_dnsbuffer_t", which tries to manage memory in a smart way. In particular: * It tries to use a static buffer for smaller messages, reducing pressure on the memory manager (hot path); * When allocating dynamic memory for larger messages, it tries to allocate memory conservatively (generic path). These characteristics is a significant upgrade over the older logic where a 64KB(+2 bytes) buffer was allocated from dynamic memory regardless of the fact if we need a buffer this large or not. That is, lesser memory usage is expected in a generic case for DNS transports built on top of "isc_dnsstream_assembler_t." --- .gitlab-ci.yml | 1 + lib/isc/Makefile.am | 1 + lib/isc/include/isc/dnsstream.h | 378 ++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) create mode 100644 lib/isc/include/isc/dnsstream.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6e14d6d634..dd97506400 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1522,6 +1522,7 @@ gcov: - find bin lib -maxdepth 1 -mindepth 1 -type d -exec cp -f lib/isc/include/isc/buffer.h "{}" \; - cp -f lib/isc/include/isc/buffer.h lib/isc/netmgr/buffer.h - cp -f lib/isc/include/isc/dnsbuffer.h lib/isc/netmgr/dnsbuffer.h + - cp -f lib/isc/include/isc/dnsstream.h lib/isc/netmgr/dnsstream.h # Help gcovr find dlz_dbi.c file - for DST in ldap mysql mysqldyn sqlite3 wildcard; do cp contrib/dlz/modules/common/dlz_dbi.c "contrib/dlz/modules/${DST}"; done # Generate XML file in the Cobertura XML format suitable for use by GitLab diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index 3d55874dab..50d94e40d8 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -26,6 +26,7 @@ libisc_la_HEADERS = \ include/isc/deprecated.h \ include/isc/dir.h \ include/isc/dnsbuffer.h \ + include/isc/dnsstream.h \ include/isc/endian.h \ include/isc/entropy.h \ include/isc/errno.h \ diff --git a/lib/isc/include/isc/dnsstream.h b/lib/isc/include/isc/dnsstream.h new file mode 100644 index 0000000000..6e4ede334a --- /dev/null +++ b/lib/isc/include/isc/dnsstream.h @@ -0,0 +1,378 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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. + */ +#pragma once + +#include + +typedef struct isc_dnsstream_assembler isc_dnsstream_assembler_t; +/*!< + * \brief The 'isc_dnsstream_assembler_t' object is built on top of + * 'isc_dnsbuffer_t' and intended to encapsulate the state machine + * used for handling DNS messages received in the format used for + * messages transmitted over TCP. + * + * The idea is that the object accepts the input data received from a + * socket (or anywhere else, for that matter), tries to assemble DNS + * messages from the incoming data and calls the callback passing it + * the status of the incoming data as well as a pointer to the memory + * region referencing the data of the assembled message (in the case + * there is enough data to assemble the message). It is capable of + * assembling DNS messages no matter how "torn apart" they are when + * sent over network. + * + * The implementation is completely decoupled from the networking code + * itself makes it trivial to write unit tests for it, leading to + * better verification of its correctness. Another important aspect + * of its functioning is directly related to the fact that it is built + * on top of 'isc_dnsbuffer_t', which tries to manage memory in a + * smart way. In particular: + * + *\li It tries to use a static buffer for smaller messages, reducing + * pressure on the memory manager (hot path); + * + *\li When allocating dynamic memory for larger messages, it tries to + * allocate memory conservatively (generic path). + * + * That is, when using 'isc_dnsstream_assembler_t', we allocate memory + * conservatively, avoiding any allocations whatsoever for small DNS + * messages (whose size is lesser of equal to 512 bytes). The last + * characteristic is important in the context of DNS, as most of DNS + * messages are small. + */ + +typedef bool (*isc_dnsstream_assembler_cb_t)(isc_dnsstream_assembler_t *dnsasm, + const isc_result_t result, + isc_region_t *restrict region, + void *cbarg, void *userarg); +/*!< + * /brief The type of callback called when processing the data passed to a + * 'isc_dnsstream_assembler_t' type. + * + * The callback accepts the following arguments: + * + *\li 'isc_dnsstream_assembler_t *dnsasm' - a pointer to the + * 'isc_dnsstream_assembler_t' object in use; + *\li 'isc_result_t result' - processing status; + *\li 'isc_region_t *region' - the region referencing the DNS message if + * assembled, empty otherwise; + *\li 'void *cbarg' - the callback argument, set during the object + * initialisation or when setting the callback; + *\li 'void *userarg' - the callback argument passed to it when processing the + * current chunk of data; + * + * Return values: + * + *\li 'true' - continue processing data, if there is any non-processed data + * left; + *\li 'false' - stop processing data regardless of non-processed data + * availability. + * + * Processing status values: + * + *\li 'ISC_R_SUCCESS' - a message has been successfully assembled; + *\li 'ISC_R_NOMORE' - not enough data to assemble a DNS message, need to get +more; + *\li 'ISC_R_RANGE' - there was an attempt to process a zero-sized DNS +message (i.e. someone attempts to send us junk data). + */ + +struct isc_dnsstream_assembler { + isc_dnsbuffer_t dnsbuf; /*!< Internal buffer for assembling DNS + messages. */ + isc_dnsstream_assembler_cb_t onmsg_cb; /*!< Data processing callback. */ + void *cbarg; /*!< Callback argument. */ + bool calling_cb; /*mctx); + isc_dnsbuffer_init(&dnsasm->dnsbuf, memctx); +} + +static inline void +isc_dnsstream_assembler_uninit(isc_dnsstream_assembler_t *restrict dnsasm) { + REQUIRE(dnsasm != NULL); + /* + * Uninitialising the object from withing the callback does not + * make any sense. + */ + INSIST(dnsasm->calling_cb == false); + isc_dnsbuffer_uninit(&dnsasm->dnsbuf); + if (dnsasm->mctx != NULL) { + isc_mem_detach(&dnsasm->mctx); + } +} + +static inline isc_dnsstream_assembler_t * +isc_dnsstream_assembler_new(isc_mem_t *memctx, isc_dnsstream_assembler_cb_t cb, + void *cbarg) { + isc_dnsstream_assembler_t *newasm; + + REQUIRE(memctx != NULL); + REQUIRE(cb != NULL); + + newasm = isc_mem_get(memctx, sizeof(*newasm)); + isc_dnsstream_assembler_init(newasm, memctx, cb, cbarg); + + return (newasm); +} + +static inline void +isc_dnsstream_assembler_free(isc_dnsstream_assembler_t **restrict dnsasm) { + isc_dnsstream_assembler_t *restrict oldasm = NULL; + isc_mem_t *memctx = NULL; + REQUIRE(dnsasm != NULL && *dnsasm != NULL); + + oldasm = *dnsasm; + + isc_mem_attach(oldasm->mctx, &memctx); + isc_dnsstream_assembler_uninit(oldasm); + isc_mem_putanddetach(&memctx, oldasm, sizeof(*oldasm)); + + *dnsasm = NULL; +} + +static inline void +isc_dnsstream_assembler_setcb(isc_dnsstream_assembler_t *restrict dnsasm, + isc_dnsstream_assembler_cb_t cb, void *cbarg) { + REQUIRE(dnsasm != NULL); + REQUIRE(cb != NULL); + dnsasm->onmsg_cb = cb; + dnsasm->cbarg = cbarg; +} + +static inline bool +isc__dnsstream_assembler_handle_message( + isc_dnsstream_assembler_t *restrict dnsasm, void *userarg) { + bool cont = false; + isc_region_t region = { 0 }; + isc_result_t result; + uint16_t dnslen = isc_dnsbuffer_peek_uint16be(&dnsasm->dnsbuf); + + INSIST(dnsasm->calling_cb == false); + + if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) < sizeof(uint16_t)) { + result = ISC_R_NOMORE; + } else if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) >= + sizeof(uint16_t) && + dnslen == 0) + { + /* + * Someone seems to send us binary junk or output from /dev/zero + */ + result = ISC_R_RANGE; + isc_dnsbuffer_clear(&dnsasm->dnsbuf); + } else if (dnslen <= (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) - + sizeof(uint16_t))) + { + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOMORE; + } + + dnsasm->result = result; + dnsasm->calling_cb = true; + if (result == ISC_R_SUCCESS) { + (void)isc_dnsbuffer_consume_uint16be(&dnsasm->dnsbuf); + isc_dnsbuffer_remainingregion(&dnsasm->dnsbuf, ®ion); + region.length = dnslen; + cont = dnsasm->onmsg_cb(dnsasm, ISC_R_SUCCESS, ®ion, + dnsasm->cbarg, userarg); + if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) >= dnslen) { + isc_dnsbuffer_consume(&dnsasm->dnsbuf, dnslen); + } + } else { + cont = false; + (void)dnsasm->onmsg_cb(dnsasm, result, NULL, dnsasm->cbarg, + userarg); + } + dnsasm->calling_cb = false; + + return (cont); +} + +static inline void +isc_dnsstream_assembler_incoming(isc_dnsstream_assembler_t *restrict dnsasm, + void *userarg, void *restrict buf, + const unsigned int buf_size) { + REQUIRE(dnsasm != NULL); + INSIST(!dnsasm->calling_cb); + + if (buf_size == 0) { + INSIST(buf == NULL); + } else { + INSIST(buf != NULL); + isc_dnsbuffer_putmem(&dnsasm->dnsbuf, buf, buf_size); + } + + while (isc__dnsstream_assembler_handle_message(dnsasm, userarg)) { + if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) == 0) { + break; + } + } + isc_dnsbuffer_trycompact(&dnsasm->dnsbuf); +} + +static inline isc_result_t +isc_dnsstream_assembler_result( + const isc_dnsstream_assembler_t *restrict dnsasm) { + REQUIRE(dnsasm != NULL); + + return (dnsasm->result); +} + +static inline size_t +isc_dnsstream_assembler_remaininglength( + const isc_dnsstream_assembler_t *restrict dnsasm) { + REQUIRE(dnsasm != NULL); + + return (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf)); +} + +static inline void +isc_dnsstream_assembler_clear(isc_dnsstream_assembler_t *restrict dnsasm) { + REQUIRE(dnsasm != NULL); + + isc_dnsbuffer_clear(&dnsasm->dnsbuf); + dnsasm->result = ISC_R_UNSET; +} From ae63471e502c08ce221e9adfedd397130cb12406 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 2 Jun 2022 14:19:12 +0300 Subject: [PATCH 09/33] Add a set of unit tests for dnsbuffer_t and dnsstream_assembler_t This commit adds a set of unit tests for isc_dnsbuffer_t and isc_dnsstream_assembler_t which help to verify that they work as expected. --- tests/isc/Makefile.am | 10 + tests/isc/dnsstream_utils_test.c | 654 ++++++++++++++++++++++++++ tests/isc/dnsstream_utils_test_data.h | 602 ++++++++++++++++++++++++ 3 files changed, 1266 insertions(+) create mode 100644 tests/isc/dnsstream_utils_test.c create mode 100644 tests/isc/dnsstream_utils_test_data.h diff --git a/tests/isc/Makefile.am b/tests/isc/Makefile.am index 4a55ec2aba..ea4a4b8286 100644 --- a/tests/isc/Makefile.am +++ b/tests/isc/Makefile.am @@ -16,6 +16,7 @@ check_PROGRAMS = \ buffer_test \ counter_test \ crc64_test \ + dnsstream_utils_test \ errno_test \ file_test \ hash_test \ @@ -70,6 +71,15 @@ doh_test_SOURCES = \ endif HAVE_LIBNGHTTP2 +dnsstream_utils_test_CPPFLAGS = \ + $(AM_CPPFLAGS) + +dnsstream_utils_test_LDADD = \ + $(LDADD) + +dnsstream_utils_test_SOURCES = \ + dnsstream_utils_test.c + hmac_test_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(OPENSSL_CFLAGS) diff --git a/tests/isc/dnsstream_utils_test.c b/tests/isc/dnsstream_utils_test.c new file mode 100644 index 0000000000..17ff422836 --- /dev/null +++ b/tests/isc/dnsstream_utils_test.c @@ -0,0 +1,654 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 /* IWYU pragma: keep */ +#include +#include +#include +#include +#include +#include +#include + +#define UNIT_TESTING +#include + +#include +#include +#include +#include +#include + +#include "dnsstream_utils_test_data.h" + +#include + +static int +setup_test_dnsbuf(void **state) { + isc_dnsbuffer_t **pdnsbuf = (isc_dnsbuffer_t **)state; + *pdnsbuf = isc_dnsbuffer_new(mctx); + + return (0); +} + +static int +teardown_test_dnsbuf(void **state) { + isc_dnsbuffer_free((isc_dnsbuffer_t **)state); + + return (0); +} + +static bool +dnsasm_dummy(isc_dnsstream_assembler_t *dnsasm, const isc_result_t result, + isc_region_t *restrict region, void *cbarg, void *userarg) { + UNUSED(dnsasm); + UNUSED(result); + UNUSED(region); + UNUSED(cbarg); + UNUSED(userarg); + return (true); +} + +static int +setup_test_dnsasm(void **state) { + isc_dnsstream_assembler_t **pdnsasm = + (isc_dnsstream_assembler_t **)state; + *pdnsasm = isc_dnsstream_assembler_new(mctx, dnsasm_dummy, NULL); + + return (0); +} + +static int +teardown_test_dnsasm(void **state) { + isc_dnsstream_assembler_free((isc_dnsstream_assembler_t **)state); + + return (0); +} + +ISC_RUN_TEST_IMPL(dnsbuffer_generic_test) { + uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE / 2] = { 0 }; + isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + isc_region_t reg = { 0 }; + size_t n = 0; + + for (size_t i = 0; i < sizeof(buf); i++) { + buf[i] = (uint8_t)i; + } + + /* sanity checks */ + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == 0); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + + assert_true(isc_dnsbuffer_current(dnsbuf) == dnsbuf->buf); + assert_true(dnsbuf->current == &dnsbuf->stbuf); + + isc_dnsbuffer_clear(dnsbuf); + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == 0); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + + assert_true(dnsbuf->current == &dnsbuf->stbuf); + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + + assert_true(isc_dnsbuffer_current(dnsbuf) == dnsbuf->buf); + assert_true(dnsbuf->current == &dnsbuf->stbuf); + + for (size_t i = 0; i < sizeof(buf); + i++, isc_dnsbuffer_consume(dnsbuf, 1)) + { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf) * 2); + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + + assert_true(dnsbuf->current == &dnsbuf->stbuf); + + for (size_t i = 0; i < sizeof(buf); + i++, isc_dnsbuffer_consume(dnsbuf, 1)) + { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == i); + } + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + + for (size_t i = 0; i < sizeof(buf); + i++, isc_dnsbuffer_consume(dnsbuf, 1)) + { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_dnsbuffer_trycompact(dnsbuf)); + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + isc_dnsbuffer_remainingregion(dnsbuf, ®); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == reg.length); + assert_true(reg.length == sizeof(buf)); + + for (size_t i = 0; i < reg.length; i++) { + uint8_t d = (uint8_t)reg.base[i]; + + assert_true(d == i); + } + + isc_dnsbuffer_consume(dnsbuf, reg.length); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + + isc_dnsbuffer_clear(dnsbuf); + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + + n = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + for (size_t i = 0; i < n; i++) { + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + } + + assert_true(isc_dnsbuffer_length(dnsbuf) > + ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + assert_true(isc_dnsbuffer_length(dnsbuf) >= n * sizeof(buf)); + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == n * sizeof(buf)); + + for (size_t i = 0; i < n; i++) { + for (size_t k = 0; k < sizeof(buf); + k++, isc_dnsbuffer_consume(dnsbuf, 1)) + { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == k); + } + } +} + +ISC_RUN_TEST_IMPL(dnsbuffer_resize_alloc_test) { + uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE / 2] = { 0 }; + isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + size_t i = 0, n = 0; + + for (i = 0; i < sizeof(buf); i++) { + buf[i] = (uint8_t)i; + } + + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + + for (i = 0; i < (sizeof(buf) / 3) * 2; + i++, isc_dnsbuffer_consume(dnsbuf, 1)) + { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_dnsbuffer_length(dnsbuf) == + ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + + n = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + for (size_t k = 0; k < n; k++) { + isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + } + + assert_true(isc_dnsbuffer_length(dnsbuf) >= + ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + + for (; i < sizeof(buf); i++, isc_dnsbuffer_consume(dnsbuf, 1)) { + uint8_t *p = isc_dnsbuffer_current(dnsbuf); + + assert_true(*p == i); + } +} + +ISC_RUN_TEST_IMPL(dnsbuffer_be_test) { + isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + const uint16_t u16 = 0xBEEF; + uint16_t *pu16; + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + + assert_true(isc_dnsbuffer_peek_uint16be(dnsbuf) == 0); + assert_true(isc_dnsbuffer_consume_uint16be(dnsbuf) == 0); + + isc_dnsbuffer_putmem_uint16be(dnsbuf, u16); + + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(u16)); + + pu16 = (uint16_t *)isc_dnsbuffer_current(dnsbuf); + assert_true(*pu16 == htons(u16)); + + assert_true(isc_dnsbuffer_peek_uint16be(dnsbuf) == u16); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(u16)); + + assert_true(isc_dnsbuffer_consume_uint16be(dnsbuf) == u16); + assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); +} + +typedef struct verify_cbdata { + uint8_t *verify_message; + bool cont_on_success; + bool clear_on_success; +} verify_cbdata_t; + +static bool +verify_dnsmsg(isc_dnsstream_assembler_t *dnsasm, const isc_result_t result, + isc_region_t *restrict region, void *cbarg, void *userarg) { + size_t *processed = (size_t *)userarg; + verify_cbdata_t *vdata = (verify_cbdata_t *)cbarg; + uint8_t *message = (uint8_t *)vdata->verify_message; + + UNUSED(dnsasm); + + assert_true(result == isc_dnsstream_assembler_result(dnsasm)); + + if (vdata->verify_message != NULL) { + message += sizeof(uint16_t); + } + + if (result != ISC_R_SUCCESS) { + return (true); + } + + if (vdata->verify_message != NULL && + memcmp(message, region->base, region->length) == 0) + { + *processed += 1; + } else { + *processed += 1; + } + + if (vdata->clear_on_success) { + isc_dnsstream_assembler_clear( + (isc_dnsstream_assembler_t *)dnsasm); + } + + return (vdata->cont_on_success); +} + +typedef struct verify_regions_cbdata { + isc_region_t *packets; + bool cont_on_success; +} verify_regions_cbdata_t; + +static bool +verify_dnsmsg_regions(isc_dnsstream_assembler_t *dnsasm, + const isc_result_t result, isc_region_t *restrict region, + void *cbarg, void *userarg) { + size_t *processed = (size_t *)userarg; + verify_regions_cbdata_t *vdata = (verify_regions_cbdata_t *)cbarg; + uint8_t *message = (uint8_t *)vdata->packets[0].base; + + UNUSED(dnsasm); + + assert_true(result == isc_dnsstream_assembler_result(dnsasm)); + + if (vdata->packets != NULL) { + message += sizeof(uint16_t); + } + + if (result != ISC_R_SUCCESS) { + return (true); + } + + if (vdata->packets != NULL && + memcmp(message, region->base, region->length) == 0) + { + *processed += 1; + } else { + *processed += 1; + } + + vdata->packets++; + + return (vdata->cont_on_success); +} + +ISC_RUN_TEST_IMPL(dnsasm_sequence_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + + cbdata.cont_on_success = true; + + cbdata.verify_message = (uint8_t *)request; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming(dnsasm, &verified, (void *)request, + sizeof(request)); + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + cbdata.verify_message = (uint8_t *)response; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming(dnsasm, &verified, (void *)response, + sizeof(response)); + assert_true(verified == 2); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + cbdata.verify_message = (uint8_t *)request_large; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming(dnsasm, &verified, + (void *)request_large, + sizeof(request_large)); + assert_true(verified == 3); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + cbdata.verify_message = (uint8_t *)response_large; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming(dnsasm, &verified, + (void *)response_large, + sizeof(response_large)); + assert_true(verified == 4); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); +} + +ISC_RUN_TEST_IMPL(dnsasm_multiple_messages_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + isc_dnsbuffer_t dnsbuf; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + + isc_dnsbuffer_init(&dnsbuf, mctx); + isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)response, sizeof(response)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)request_large, + sizeof(request_large)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)response_large, + sizeof(response_large)); + + cbdata.cont_on_success = false; + + /* + * feed the data to the message assembler and handle the first message + */ + cbdata.verify_message = (uint8_t *)request; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming( + dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), + isc_dnsbuffer_remaininglength(&dnsbuf)); + + isc_dnsbuffer_uninit(&dnsbuf); + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + /* + * handle the next message (and so on) + */ + cbdata.verify_message = (uint8_t *)response; + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + assert_true(verified == 2); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + cbdata.verify_message = (uint8_t *)request_large; + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + assert_true(verified == 3); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + cbdata.verify_message = (uint8_t *)response_large; + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + assert_true(verified == 4); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + /* + * no unprocessed data left + */ + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + assert_true(verified == 4); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_NOMORE); +} + +ISC_RUN_TEST_IMPL(dnsasm_torn_apart_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + size_t left = 0; + + cbdata.verify_message = (uint8_t *)response_large; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming(dnsasm, &verified, response_large, + sizeof(response_large) / 3 * 2); + + assert_true(verified == 0); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_NOMORE); + + left = sizeof(response_large) - + isc_dnsstream_assembler_remaininglength(dnsasm); + isc_dnsstream_assembler_incoming( + dnsasm, &verified, + &response_large[isc_dnsstream_assembler_remaininglength(dnsasm)], + left); + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); +} + +ISC_RUN_TEST_IMPL(dnsasm_error_data_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + isc_dnsbuffer_t dnsbuf; + uint16_t bad_data = 0; + + isc_dnsbuffer_init(&dnsbuf, mctx); + + isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)response_large, + sizeof(response_large)); + + cbdata.cont_on_success = false; + + cbdata.verify_message = (uint8_t *)request; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming( + dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), + isc_dnsbuffer_remaininglength(&dnsbuf)); + + isc_dnsbuffer_uninit(&dnsbuf); + + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); + + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) > 0); + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_RANGE); + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); + + isc_dnsstream_assembler_incoming(dnsasm, &verified, NULL, 0); + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_NOMORE); +} + +ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + verify_cbdata_t cbdata = { 0 }; + verify_regions_cbdata_t cbdata_regions = { 0 }; + isc_dnsbuffer_t dnsbuf; + size_t packetno; + isc_region_t packets[] = { + { (void *)request, sizeof(request) }, + { (void *)response, sizeof(response) }, + { (void *)request_large, sizeof(request_large) }, + { (void *)response_large, sizeof(response_large) }, + { (void *)request, sizeof(request) }, + { (void *)response_large, sizeof(response_large) }, + { (void *)request_large, sizeof(request_large) }, + { (void *)response_large, sizeof(response_large) }, + { (void *)request, sizeof(request) }, + }; + const size_t npackets = sizeof(packets) / sizeof(packets[0]); + + isc_dnsbuffer_init(&dnsbuf, mctx); + for (size_t i = 0; i < npackets; i++) { + isc_dnsbuffer_putmem(&dnsbuf, packets[i].base, + packets[i].length); + } + + /* process packet by packet */ + packetno = 0; + cbdata.cont_on_success = false; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + + /* process random amount of data */ + for (; isc_dnsbuffer_remaininglength(&dnsbuf) > 0;) { + size_t sz = 1 + isc_random_uniform( + isc_dnsbuffer_remaininglength(&dnsbuf)); + + for (bool start = true; packetno < npackets; start = false) { + cbdata.verify_message = + (uint8_t *)packets[packetno].base; + + if (start) { + isc_dnsstream_assembler_incoming( + dnsasm, &packetno, + isc_dnsbuffer_current(&dnsbuf), sz); + } else { + isc_dnsstream_assembler_incoming( + dnsasm, &packetno, NULL, 0); + } + + if (isc_dnsstream_assembler_result(dnsasm) == + ISC_R_NOMORE) + { + break; + } + } + + isc_dnsbuffer_consume(&dnsbuf, sz); + } + + assert_true(packetno == npackets); + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); + assert_true(isc_dnsbuffer_remaininglength(&dnsbuf) == 0); + + for (size_t i = 0; i < npackets; i++) { + isc_dnsbuffer_putmem(&dnsbuf, packets[i].base, + packets[i].length); + } + + /* try to process multiple packets at once, when possible */ + packetno = 0; + cbdata_regions.cont_on_success = true; + cbdata_regions.packets = packets; + + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg_regions, + (void *)&cbdata_regions); + + /* process random amount of data */ + for (; isc_dnsbuffer_remaininglength(&dnsbuf) > 0;) { + size_t sz = 1 + isc_random_uniform( + isc_dnsbuffer_remaininglength(&dnsbuf)); + + isc_dnsstream_assembler_incoming( + dnsasm, &packetno, isc_dnsbuffer_current(&dnsbuf), sz); + + isc_dnsbuffer_consume(&dnsbuf, sz); + } + + assert_true(packetno == npackets); + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); + assert_true(isc_dnsbuffer_remaininglength(&dnsbuf) == 0); + + isc_dnsbuffer_uninit(&dnsbuf); +} + +ISC_RUN_TEST_IMPL(dnsasm_clear_buffer_within_cb_test) { + isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + isc_dnsbuffer_t dnsbuf; + + isc_dnsbuffer_init(&dnsbuf, mctx); + + isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)&response, sizeof(response)); + isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + cbdata.cont_on_success = true; + cbdata.clear_on_success = true; + + cbdata.verify_message = (uint8_t *)request; + isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); + isc_dnsstream_assembler_incoming( + dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), + isc_dnsbuffer_remaininglength(&dnsbuf)); + + isc_dnsbuffer_uninit(&dnsbuf); + + assert_true(verified == 1); + assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_UNSET); + + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); +} + +ISC_TEST_LIST_START +ISC_TEST_ENTRY_CUSTOM(dnsbuffer_generic_test, setup_test_dnsbuf, + teardown_test_dnsbuf) +ISC_TEST_ENTRY_CUSTOM(dnsbuffer_resize_alloc_test, setup_test_dnsbuf, + teardown_test_dnsbuf) +ISC_TEST_ENTRY_CUSTOM(dnsbuffer_be_test, setup_test_dnsbuf, + teardown_test_dnsbuf) +ISC_TEST_ENTRY_CUSTOM(dnsasm_sequence_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_ENTRY_CUSTOM(dnsasm_multiple_messages_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_ENTRY_CUSTOM(dnsasm_torn_apart_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_ENTRY_CUSTOM(dnsasm_error_data_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_ENTRY_CUSTOM(dnsasm_torn_randomly_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_ENTRY_CUSTOM(dnsasm_clear_buffer_within_cb_test, setup_test_dnsasm, + teardown_test_dnsasm) +ISC_TEST_LIST_END + +ISC_TEST_MAIN diff --git a/tests/isc/dnsstream_utils_test_data.h b/tests/isc/dnsstream_utils_test_data.h new file mode 100644 index 0000000000..e9e0ba060c --- /dev/null +++ b/tests/isc/dnsstream_utils_test_data.h @@ -0,0 +1,602 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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. + */ +#pragma once + +static const char request[] = { /* Packet 12 */ + 0x00, 0x1c, 0x28, 0x0d, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, + 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const char response[] = { + /* Packet 14 */ + 0x00, 0x37, 0x28, 0x0d, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, + 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x8e, 0xfb, 0x27, 0x6e, 0x00, 0x00, + 0x29, 0x04, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static char request_large[] = { /* Packet 4 */ + 0x00, 0x2a, 0x15, 0x45, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, + 0x6d, 0x74, 0x73, 0x31, 0x2d, 0x64, 0x68, 0x63, + 0x70, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x03, 0x63, 0x6f, 0x6d, 0x00, + 0x00, 0x01, 0x00, 0x01 +}; +static char response_large + [] = { /* Packet 6 */ + 0x18, 0x1a, 0x15, 0x45, 0x81, 0x80, 0x00, 0x01, 0x01, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6d, 0x74, 0x73, 0x31, 0x2d, 0x64, + 0x68, 0x63, 0x70, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x69, 0x6e, + 0x65, 0x73, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x1c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x46, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x52, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x15, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x56, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe5, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x1d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc9, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x57, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x0d, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x63, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xa9, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xde, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x6f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc1, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x59, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xa2, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x2b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x65, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x6c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x2e, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x5a, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x61, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x79, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x3b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x4a, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xa6, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x35, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x06, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x49, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x54, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x39, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x5e, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x83, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xef, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x91, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x3e, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x08, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb5, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x89, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xe2, 0x84, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xd3, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc8, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x51, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x69, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x03, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x17, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x15, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xc4, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x5b, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x94, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xbd, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x2c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x2b, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x09, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x10, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x13, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x48, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x2d, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x0b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x4c, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x11, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x13, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x5c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x25, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x0d, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x5b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x55, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x1c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x97, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xe2, 0x82, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x31, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x3f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe7, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x9d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x42, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x32, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x3d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe2, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0xf9, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x76, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xb3, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb4, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x28, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xfa, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xa5, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x4f, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb9, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xbf, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x4d, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x43, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x14, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x04, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x66, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xd9, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x19, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x98, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x37, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x0c, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x4d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x27, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x58, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x76, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x38, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x4c, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x0a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc2, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x55, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x9c, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xa1, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x56, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x60, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x8f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x61, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x68, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x81, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x8e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xad, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x74, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x44, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb7, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x41, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x3a, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x3c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x0e, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xdf, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x67, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x99, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x03, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xd1, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x70, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x07, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x77, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x7d, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x6a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x7b, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x64, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x39, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x60, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x6b, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xaf, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xa7, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xca, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x73, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x02, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x2f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x45, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x1f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x2e, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x07, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x05, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x79, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x16, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x6d, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x7b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x18, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0xfd, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x57, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x1f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x3f, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x46, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x25, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x04, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x5d, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf7, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x3a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x8a, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x0e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x33, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xe9, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x34, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x1e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x40, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x3d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x14, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x59, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x26, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x0f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x71, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x17, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xdb, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x4e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x21, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x7a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x2d, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x33, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x27, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x38, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x53, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb8, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x32, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x82, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x1a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x02, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x40, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x2c, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x2a, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x31, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x1e, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x50, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x9f, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x8c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x26, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x37, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x4b, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x70, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x28, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x23, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x7c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x67, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x29, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x24, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xfb, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xac, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x50, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x21, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x35, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x53, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x08, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x68, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x0b, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x65, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x4a, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x44, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf3, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x47, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x85, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x6a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x06, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xe2, 0x85, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x51, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x3c, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x18, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x0a, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x16, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x64, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x23, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x78, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x22, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x5f, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x34, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x9a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x42, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x69, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x0f, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x6f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x3e, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x6e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x75, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x2f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x6b, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x5f, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x45, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x3b, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x2a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xe2, 0x86, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x41, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xce, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x19, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x5a, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x73, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x0c, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf1, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x22, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x30, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x7c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x4b, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x6d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x5e, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x43, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x72, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x29, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x12, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x62, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x80, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x4e, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x1b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x87, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x05, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x30, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, + 0xdb, 0x52, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x10, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x8b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x90, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x09, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xe2, 0x83, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x20, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x9b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x74, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, + 0xac, 0xdb, 0x72, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x71, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x5d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x48, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x63, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x93, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe8, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x7d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x54, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x92, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x58, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x4f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x5c, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xa0, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x6e, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xa4, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x42, 0xac, 0xdb, 0x7e, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x66, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x12, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x96, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x6c, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x11, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x7e, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xae, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xa3, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x9e, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x1d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xb0, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xbc, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xba, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xaa, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x84, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x47, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x86, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, 0x20, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xcb, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xab, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x42, 0xac, 0xdb, + 0x36, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc5, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x88, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xb1, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xed, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0xbe, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc0, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0x77, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xbb, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xf8, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe3, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xc7, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x95, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xa8, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0x8d, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xea, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x49, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc3, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xb2, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xc6, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xd4, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xec, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x75, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf4, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xd8, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xcc, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xd6, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x62, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xda, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xb6, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x36, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0x7f, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe0, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xd7, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xd2, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xd0, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x4a, 0xdd, 0x2f, 0xe6, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0x78, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x4a, 0xdd, 0x2f, 0x1a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xdc, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xeb, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xd5, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xcd, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe1, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0x1b, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, + 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xdd, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xe4, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, + 0x04, 0x42, 0xac, 0xdb, 0x7a, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xee, 0xc0, + 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, + 0x42, 0xac, 0xdb, 0x24, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf2, 0xc0, 0x0c, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, + 0xdd, 0x2f, 0xf0, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xcf, 0xc0, 0x0c, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, + 0x2f, 0xf5, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0d, + 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, 0xf6, 0xc0, 0x0c, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x0d, 0xf1, 0x00, 0x04, 0x4a, 0xdd, 0x2f, + 0xfc + }; From f395cd4b3e2ac770e6b9cc087d19733e71afd361 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 20 Jun 2022 20:30:12 +0300 Subject: [PATCH 10/33] Add isc_nm_streamdnssocket (aka Stream DNS) This commit adds an initial implementation of isc_nm_streamdnssocket transport: a unified transport for DNS over stream protocols messages, which is capable of replacing both TCP DNS and TLS DNS transports. Currently, the interface it provides is a unified set of interfaces provided by both of the transports it attempts to replace. The transport is built around "isc_dnsbuffer_t" and "isc_dnsstream_assembler_t" objects and attempts to minimise both the number of memory allocations during network transfers as well as memory usage. --- lib/isc/Makefile.am | 1 + lib/isc/include/isc/netmgr.h | 12 + lib/isc/include/isc/types.h | 4 +- lib/isc/netmgr/netmgr-int.h | 100 +++ lib/isc/netmgr/netmgr.c | 76 ++- lib/isc/netmgr/streamdns.c | 1197 ++++++++++++++++++++++++++++++++++ 6 files changed, 1388 insertions(+), 2 deletions(-) create mode 100644 lib/isc/netmgr/streamdns.c diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index 50d94e40d8..fb09d10145 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -115,6 +115,7 @@ libisc_la_SOURCES = \ netmgr/netmgr-int.h \ netmgr/netmgr.c \ netmgr/socket.c \ + netmgr/streamdns.c \ netmgr/tcp.c \ netmgr/tcpdns.c \ netmgr/timer.c \ diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index c9ce9907fe..729012ac63 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -410,6 +410,13 @@ isc_nm_listentlsdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, * Same as isc_nm_listentcpdns but for an SSL (DoT) socket. */ +isc_result_t +isc_nm_listenstreamdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, + isc_nm_recv_cb_t recv_cb, void *recv_cbarg, + isc_nm_accept_cb_t accept_cb, void *accept_cbarg, + int backlog, isc_quota_t *quota, isc_tlsctx_t *sslctx, + isc_nmsocket_t **sockp); + void isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle, uint32_t keepalive, uint32_t advertised); @@ -496,6 +503,11 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, unsigned int timeout, isc_tlsctx_t *sslctx, isc_tlsctx_client_session_cache_t *client_sess_cache); +void +isc_nm_streamdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, + isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, + unsigned int timeout, isc_tlsctx_t *sslctx, + isc_tlsctx_client_session_cache_t *client_sess_cache); /*%< * Establish a DNS client connection via a TCP or TLS connection, bound to * the address 'local' and connected to the address 'peer'. diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index 51a246dfab..353a37f5c3 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -110,6 +110,7 @@ typedef enum isc_nmsocket_type { isc_nm_tlssocket = 1 << 4, isc_nm_tlsdnssocket = 1 << 5, isc_nm_httpsocket = 1 << 6, + isc_nm_streamdnssocket = 1 << 7, isc_nm_maxsocket, isc_nm_udplistener, /* Aggregate of nm_udpsocks */ @@ -117,7 +118,8 @@ typedef enum isc_nmsocket_type { isc_nm_tlslistener, isc_nm_tcpdnslistener, isc_nm_tlsdnslistener, - isc_nm_httplistener + isc_nm_httplistener, + isc_nm_streamdnslistener } isc_nmsocket_type; typedef isc_nmsocket_type isc_nmsocket_type_t; diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index fe99e3a125..807bd151a8 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -281,6 +282,11 @@ typedef enum isc__netievent_type { netievent_httpsend, netievent_httpendpoints, + netievent_streamdnsclose, + netievent_streamdnssend, + netievent_streamdnsread, + netievent_streamdnscancel, + netievent_connectcb, netievent_readcb, netievent_sendcb, @@ -922,6 +928,17 @@ struct isc_nmsocket { isc_nmsocket_h2_t h2; #endif /* HAVE_LIBNGHTTP2 */ + + struct { + isc_dnsstream_assembler_t *input; + bool reading; + isc_nmsocket_t *listener; + isc_nmsocket_t *sock; + size_t nsending; + void *send_req; + bool dot_alpn_negotiated; + const char *tls_verify_error; + } streamdns; /*% * quota is the TCP client, attached when a TCP connection * is established. pquota is a non-attached pointer to the @@ -1702,6 +1719,79 @@ isc__nm_http_set_max_streams(isc_nmsocket_t *listener, #endif +void +isc__nm_async_streamdnsread(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_streamdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, + void *cbarg); + +void +isc__nm_async_streamdnssend(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_streamdns_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); + +void +isc__nm_async_streamdnsclose(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_streamdns_close(isc_nmsocket_t *sock); + +void +isc__nm_streamdns_stoplistening(isc_nmsocket_t *sock); + +void +isc__nm_streamdns_cleanup_data(isc_nmsocket_t *sock); + +void +isc__nm_async_streamdnscancel(isc__networker_t *worker, isc__netievent_t *ev0); + +void +isc__nm_streamdns_cancelread(isc_nmhandle_t *handle); + +void +isc__nmhandle_streamdns_cleartimeout(isc_nmhandle_t *handle); + +void +isc__nmhandle_streamdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout); + +void +isc__nmhandle_streamdns_keepalive(isc_nmhandle_t *handle, bool value); + +void +isc__nmhandle_streamdns_setwritetimeout(isc_nmhandle_t *handle, + uint32_t timeout); + +bool +isc__nm_streamdns_has_encryption(const isc_nmhandle_t *handle); + +const char * +isc__nm_streamdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle); + +void +isc__nm_streamdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx); + +bool +isc__nm_streamdns_xfr_allowed(isc_nmsocket_t *sock); + +void +isc__nmsocket_streamdns_reset(isc_nmsocket_t *sock); + +bool +isc__nmsocket_streamdns_timer_running(isc_nmsocket_t *sock); + +void +isc__nmsocket_streamdns_timer_stop(isc_nmsocket_t *sock); + +void +isc__nmsocket_streamdns_timer_restart(isc_nmsocket_t *sock); + +void +isc__nm_streamdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, + bool async); + void isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0); @@ -1865,6 +1955,11 @@ NETIEVENT_SOCKET_HANDLE_TYPE(udpcancel); NETIEVENT_SOCKET_QUOTA_TYPE(tcpaccept); +NETIEVENT_SOCKET_TYPE(streamdnsclose); +NETIEVENT_SOCKET_REQ_TYPE(streamdnssend); +NETIEVENT_SOCKET_TYPE(streamdnsread); +NETIEVENT_SOCKET_HANDLE_TYPE(streamdnscancel); + NETIEVENT_SOCKET_TLSCTX_TYPE(settlsctx); NETIEVENT_SOCKET_TYPE(sockstop); @@ -1915,6 +2010,11 @@ NETIEVENT_SOCKET_DECL(detach); NETIEVENT_SOCKET_QUOTA_DECL(tcpaccept); +NETIEVENT_SOCKET_DECL(streamdnsclose); +NETIEVENT_SOCKET_REQ_DECL(streamdnssend); +NETIEVENT_SOCKET_DECL(streamdnsread); +NETIEVENT_SOCKET_HANDLE_DECL(streamdnscancel); + NETIEVENT_SOCKET_TLSCTX_DECL(settlsctx); NETIEVENT_SOCKET_DECL(sockstop); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index bca6f3c76b..9fdf77a96a 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -354,6 +354,9 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) { isc__nmhandle_tls_setwritetimeout(handle, write_timeout); break; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + isc__nmhandle_streamdns_setwritetimeout(handle, write_timeout); + break; default: UNREACHABLE(); break; @@ -480,6 +483,11 @@ process_netievent(void *arg) { NETIEVENT_CASE(httpclose); NETIEVENT_CASE(httpendpoints); #endif + NETIEVENT_CASE(streamdnsread); + NETIEVENT_CASE(streamdnssend); + NETIEVENT_CASE(streamdnsclose); + NETIEVENT_CASE(streamdnscancel); + NETIEVENT_CASE(settlsctx); NETIEVENT_CASE(sockstop); @@ -556,6 +564,11 @@ NETIEVENT_SOCKET_DEF(detach); NETIEVENT_SOCKET_QUOTA_DEF(tcpaccept); +NETIEVENT_SOCKET_DEF(streamdnsclose); +NETIEVENT_SOCKET_REQ_DEF(streamdnssend); +NETIEVENT_SOCKET_DEF(streamdnsread); +NETIEVENT_SOCKET_HANDLE_DEF(streamdnscancel); + NETIEVENT_SOCKET_TLSCTX_DEF(settlsctx); NETIEVENT_SOCKET_DEF(sockstop); @@ -715,6 +728,7 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { isc__nm_tls_cleanup_data(sock); isc__nm_http_cleanup_data(sock); #endif + isc__nm_streamdns_cleanup_data(sock); if (sock->barrier_initialised) { isc_barrier_destroy(&sock->barrier); @@ -844,6 +858,9 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_close(sock); return; + case isc_nm_streamdnssocket: + isc__nm_streamdns_close(sock); + return; #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_close(sock); @@ -896,6 +913,7 @@ 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_streamdnslistener || (*sockp)->type == isc_nm_tlslistener || (*sockp)->type == isc_nm_httplistener); @@ -1159,7 +1177,8 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) { handle->sock->type == isc_nm_tcpdnssocket || handle->sock->type == isc_nm_tlssocket || handle->sock->type == isc_nm_tlsdnssocket || - handle->sock->type == isc_nm_httpsocket); + handle->sock->type == isc_nm_httpsocket || + handle->sock->type == isc_nm_streamdnssocket); } static void @@ -1406,6 +1425,9 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) { isc__nm_tls_failed_read_cb(sock, result, async); return; #endif + case isc_nm_streamdnssocket: + isc__nm_streamdns_failed_read_cb(sock, result, async); + return; default: UNREACHABLE(); } @@ -1517,6 +1539,9 @@ isc__nmsocket_timer_restart(isc_nmsocket_t *sock) { isc__nmsocket_tls_timer_restart(sock); return; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_timer_restart(sock); + return; default: break; } @@ -1560,6 +1585,8 @@ isc__nmsocket_timer_running(isc_nmsocket_t *sock) { case isc_nm_tlssocket: return (isc__nmsocket_tls_timer_running(sock)); #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + return (isc__nmsocket_streamdns_timer_running(sock)); default: break; } @@ -1590,6 +1617,9 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) { isc__nmsocket_tls_timer_stop(sock); return; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_timer_stop(sock); + return; default: break; } @@ -1613,6 +1643,9 @@ isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr) { case isc_nm_tlssocket: isc_nmhandle_attach(sock->statichandle, &req->handle); break; + case isc_nm_streamdnssocket: + isc_nmhandle_attach(sock->recv_handle, &req->handle); + break; default: if (atomic_load(&sock->client) && sock->statichandle != NULL) { isc_nmhandle_attach(sock->statichandle, &req->handle); @@ -1842,6 +1875,9 @@ isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) { isc__nm_tls_cleartimeout(handle); return; #endif + case isc_nm_streamdnssocket: + isc__nmhandle_streamdns_cleartimeout(handle); + return; default: handle->sock->read_timeout = 0; @@ -1865,6 +1901,9 @@ isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { isc__nm_tls_settimeout(handle, timeout); return; #endif + case isc_nm_streamdnssocket: + isc__nmhandle_streamdns_settimeout(handle, timeout); + return; default: handle->sock->read_timeout = timeout; isc__nmsocket_timer_restart(handle->sock); @@ -1892,6 +1931,9 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) { sock->write_timeout = value ? atomic_load(&netmgr->keepalive) : atomic_load(&netmgr->idle); break; + case isc_nm_streamdnssocket: + isc__nmhandle_streamdns_keepalive(handle, value); + break; #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmhandle_tls_keepalive(handle, value); @@ -2025,6 +2067,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_streamdnssocket: + isc__nm_streamdns_send(handle, region, cb, cbarg); + break; #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_send(handle, region, cb, cbarg); @@ -2055,6 +2100,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_read(handle, cb, cbarg); break; + case isc_nm_streamdnssocket: + isc__nm_streamdns_read(handle, cb, cbarg); + break; #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_read(handle, cb, cbarg); @@ -2082,6 +2130,9 @@ isc_nm_cancelread(isc_nmhandle_t *handle) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_cancelread(handle); break; + case isc_nm_streamdnssocket: + isc__nm_streamdns_cancelread(handle); + break; default: UNREACHABLE(); } @@ -2124,6 +2175,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { case isc_nm_tlsdnslistener: isc__nm_tlsdns_stoplistening(sock); break; + case isc_nm_streamdnslistener: + isc__nm_streamdns_stoplistening(sock); + break; #if HAVE_LIBNGHTTP2 case isc_nm_tlslistener: isc__nm_tls_stoplistening(sock); @@ -2364,6 +2418,9 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) { isc__nmsocket_tls_reset(sock); return; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_reset(sock); + return; default: UNREACHABLE(); break; @@ -2583,6 +2640,7 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: case isc_nm_tcpsocket: + case isc_nm_streamdnssocket: #if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: #endif /* HAVE_LIBNGHTTP2 */ @@ -2614,6 +2672,8 @@ isc_nm_xfr_allowed(isc_nmhandle_t *handle) { return (true); case isc_nm_tlsdnssocket: return (isc__nm_tlsdns_xfr_allowed(sock)); + case isc_nm_streamdnssocket: + return (isc__nm_streamdns_xfr_allowed(sock)); default: return (false); } @@ -2653,6 +2713,7 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { case isc_nm_udpsocket: case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: + case isc_nm_streamdnssocket: return; break; @@ -2689,6 +2750,8 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle) { case isc_nm_httpsocket: return (isc__nm_http_has_encryption(handle)); #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + return (isc__nm_streamdns_has_encryption(handle)); default: return (false); }; @@ -2716,6 +2779,10 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { return (isc__nm_http_verify_tls_peer_result_string(handle)); break; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + return (isc__nm_streamdns_verify_tls_peer_result_string( + handle)); + break; default: break; } @@ -2785,6 +2852,9 @@ isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { case isc_nm_tlsdnslistener: set_tlsctx_workers(listener, tlsctx); break; + case isc_nm_streamdnslistener: + isc__nm_streamdns_set_tlsctx(listener, tlsctx); + break; default: UNREACHABLE(); break; @@ -2976,6 +3046,10 @@ nmsocket_type_totext(isc_nmsocket_type type) { return ("isc_nm_httplistener"); case isc_nm_httpsocket: return ("isc_nm_httpsocket"); + case isc_nm_streamdnslistener: + return ("isc_nm_streamdnslistener"); + case isc_nm_streamdnssocket: + return ("isc_nm_streamdnssocket"); default: UNREACHABLE(); } diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c new file mode 100644 index 0000000000..6e3a5bfe5b --- /dev/null +++ b/lib/isc/netmgr/streamdns.c @@ -0,0 +1,1197 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 "netmgr-int.h" + +/* + * Stream DNS is a unified transport capable of serving both DNS over + * TCP and DNS over TLS. It is built on top of + * 'isc_dnsstream_assembler_t' and 'isc_dnsbuffer_t'. The first one + * is used for assembling DNS messages in the format used for DNS over + * TCP out of incoming data and is built on top of + * 'isc_dnsbuffer_t'. The 'isc_dnbuffer_t' is a thin wrapper on top of + * 'isc_buffer_t' and is optimised for small (>= 512 bytes) DNS + * messages. For small messages it uses a small static memory buffer, + * but it can automatically switch to a dynamically allocated memory + * buffer for larger ones. This way we avoid unnecessary memory + * allocation requests in most cases, as most DNS messages are small. + * + * The use of 'isc_dnsstream_assembler_t' allows decoupling DNS + * message assembling code from networking code itself, making it + * easier to test. + * + * To understand how the part responsible for reading of data works, + * start by looking at 'streamdns_on_dnsmessage_data_cb()' (the DNS + * message data processing callback) and + * 'streamdns_handle_incoming_data()' which passes incoming data to + * the 'isc_dnsstream_assembler_t' object within the socket. + * + * The writing is done in a simpler manner due to the fact that we + * have full control over the data. For each write request we attempt + * to allocate a 'streamdns_send_req_t' structure, whose main purpose + * is to keep the data required for the send request processing. An + * 'isc_dnsbuffer_t' object is used as a data storage for the reasons + * described above. + * + * When processing write requests there is an important optimisation: + * we attempt to reuse 'streamdns_send_req_t' objects again, in order + * to avoid memory allocations when: + * + * a) requesting memory for the new 'streamdns_send_req_t' object; + * + * b) resizing the 'isc_dnsbuffer_t' to fit large messages, should it + * be required. + * + * The last characteristic is important as it allows gradually growing + * the reused send buffer in a lazy manner when transmitting multiple + * DNS messages (e.g. during zone transfers). + * + * To understand how sending is done, start by looking at + * 'isc__nm_async_streamdnssend()'. Additionally also take a look at + * 'streamdns_get_send_req()' and 'streamdns_put_send_req()' which are + * responsible for send requests allocation/reuse and initialisation. + * + * The rest of the code is mostly wrapping code to expose the + * functionality of the underlying transport, which at the moment + * could be either TCP or TLS. + */ + +typedef struct streamdns_send_req { + isc_nm_cb_t cb; /* send callback */ + void *cbarg; /* send callback argument */ + isc_nmhandle_t *dnshandle; /* Stream DNS socket handle */ + isc_dnsbuffer_t data; /* buffer that contains the DNS message to send */ +} streamdns_send_req_t; + +static streamdns_send_req_t * +streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, + isc__nm_uvreq_t *req, isc_region_t *data); + +static void +streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, + const bool force_destroy); + +static void +streamdns_readcb(isc_nmhandle_t *handle, isc_result_t result, + isc_region_t *region, void *cbarg); + +static void +streamdns_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result, + const bool async); + +static void +streamdns_try_close_unused(isc_nmsocket_t *sock); + +static bool +streamdns_closing(isc_nmsocket_t *sock); + +static void +streamdns_resumeread(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { + if (!sock->streamdns.reading) { + sock->streamdns.reading = true; + isc_nm_read(transphandle, streamdns_readcb, (void *)sock); + } +} + +static void +streamdns_readmore(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { + streamdns_resumeread(sock, transphandle); + if (sock->streamdns.reading && atomic_load(&sock->ah) == 1) { + isc__nmsocket_timer_start(sock); + } +} + +static void +streamdns_pauseread(isc_nmsocket_t *sock, isc_nmhandle_t *transphandle) { + if (sock->streamdns.reading) { + sock->streamdns.reading = false; + isc_nm_read_stop(transphandle); + } +} + +static bool +streamdns_on_complete_dnsmessage(isc_dnsstream_assembler_t *dnsasm, + isc_region_t *restrict region, + isc_nmsocket_t *sock, + isc_nmhandle_t *transphandle) { + const bool client = atomic_load(&sock->client); + const bool last_datum = isc_dnsstream_assembler_remaininglength( + dnsasm) == region->length; + /* + * Stop after one message if a client + * connection. + */ + bool stop = client; + + sock->recv_read = false; + if (sock->recv_cb != NULL) { + if (!client) { + /* + * We must allocate a new handle object, as we + * need to ensure that after processing of this + * message has been completed and the handle + * gets destroyed, 'nsock->closehandle_cb' + * (streamdns_resume_processing()) is invoked. + * That is required for pipelining support. + */ + isc_nmhandle_t *handle = isc__nmhandle_get( + sock, &sock->peer, &sock->iface); + sock->recv_cb(handle, ISC_R_SUCCESS, region, + sock->recv_cbarg); + isc_nmhandle_detach(&handle); + } else { + /* + * As on the client side we are supposed to stop + * reading/processing after receiving one + * message, we can use the 'sock->recv_handle' + * from which we would need to detach before + * calling the read callback anyway. + */ + isc_nmhandle_t *recv_handle = sock->recv_handle; + sock->recv_handle = NULL; + sock->recv_cb(recv_handle, ISC_R_SUCCESS, region, + sock->recv_cbarg); + isc_nmhandle_detach(&recv_handle); + } + + if (streamdns_closing(sock)) { + stop = true; + } + } else { + stop = true; + } + + isc__nmsocket_timer_stop(sock); + if (!stop && last_datum) { + /* + * We have processed all data, need to read more. + * The call also restarts the timer. + */ + streamdns_readmore(sock, transphandle); + } else if (stop) { + streamdns_pauseread(sock, transphandle); + } + + return (!stop); +} + +/* + * This function, alongside 'streamdns_handle_incoming_data()', + * connects networking code to the 'isc_dnsstream_assembler_t'. It is + * responsible for making decisions regarding reading from the + * underlying transport socket as well as controlling the read timer. + */ +static bool +streamdns_on_dnsmessage_data_cb(isc_dnsstream_assembler_t *dnsasm, + const isc_result_t result, + isc_region_t *restrict region, void *cbarg, + void *userarg) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; + isc_nmhandle_t *transphandle = (isc_nmhandle_t *)userarg; + + switch (result) { + case ISC_R_SUCCESS: + /* + * A complete DNS message has been assembled from the incoming + * data. Let's process it. + */ + return (streamdns_on_complete_dnsmessage(dnsasm, region, sock, + transphandle)); + case ISC_R_RANGE: + /* + * It seems that someone attempts to send us some binary junk + * over the socket, as the beginning of the next message tells + * us the there is an empty (0-sized) DNS message to receive. + * We should treat it as a hard error. + */ + streamdns_failed_read_cb(sock, result, false); + return (false); + case ISC_R_NOMORE: + /* + * We do not have enough data to process the next message and + * thus we need to resume reading from the socket. + */ + if (sock->recv_handle != NULL) { + streamdns_readmore(sock, transphandle); + } + return (false); + default: + UNREACHABLE(); + }; +} + +static void +streamdns_handle_incoming_data(isc_nmsocket_t *sock, + isc_nmhandle_t *transphandle, + void *restrict data, size_t len) { + isc_dnsstream_assembler_t *dnsasm = sock->streamdns.input; + + /* + * Try to process the received data or, when 'data == NULL' and + * 'len == 0', try to resume processing of the data within the + * internal buffers or resume reading, if there is no any. + */ + isc_dnsstream_assembler_incoming(dnsasm, transphandle, data, len); + streamdns_try_close_unused(sock); +} + +static isc_nmsocket_t * +streamdns_sock_new(isc__networker_t *worker, const isc_nmsocket_type_t type, + isc_sockaddr_t *addr, const bool is_server) { + isc_nmsocket_t *sock; + INSIST(type == isc_nm_streamdnssocket || + type == isc_nm_streamdnslistener); + + sock = isc_mem_get(worker->mctx, sizeof(*sock)); + isc__nmsocket_init(sock, worker, type, addr); + sock->result = ISC_R_UNSET; + if (type == isc_nm_streamdnssocket) { + uint32_t initial = 0; + isc_nm_gettimeouts(worker->netmgr, &initial, NULL, NULL, NULL); + sock->read_timeout = initial; + atomic_init(&sock->client, !is_server); + atomic_init(&sock->connecting, !is_server); + sock->streamdns.input = isc_dnsstream_assembler_new( + sock->worker->mctx, streamdns_on_dnsmessage_data_cb, + sock); + } + + return (sock); +} + +static void +streamdns_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle, + const isc_result_t result) { + atomic_store(&sock->connecting, false); + if (sock->connect_cb == NULL) { + return; + } + sock->connect_cb(handle, result, sock->connect_cbarg); + if (result != ISC_R_SUCCESS) { + isc__nmsocket_clearcb(handle->sock); + } else { + atomic_store(&sock->connected, true); + } + streamdns_try_close_unused(sock); +} + +static void +streamdns_save_alpn_status(isc_nmsocket_t *dnssock, + isc_nmhandle_t *transp_handle) { + const unsigned char *alpn = NULL; + unsigned int alpnlen = 0; + + isc__nmhandle_get_selected_alpn(transp_handle, &alpn, &alpnlen); + if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN && + memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn, + ISC_TLS_DOT_PROTO_ALPN_ID_LEN) == 0) + { + dnssock->streamdns.dot_alpn_negotiated = true; + } +} + +static void +streamdns_transport_connected(isc_nmhandle_t *handle, isc_result_t result, + void *cbarg) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; + isc_nmhandle_t *streamhandle = NULL; + + REQUIRE(VALID_NMSOCK(sock)); + + sock->tid = isc_tid(); + if (result == ISC_R_EOF) { + /* + * The transport layer (probably TLS) has returned EOF during + * connection establishment. That means that connection has + * been "cancelled" (for compatibility with old transport + * behaviour). + */ + result = ISC_R_CANCELED; + goto error; + } else if (result == ISC_R_TLSERROR) { + /* + * In some of the cases when the old code would return + * ISC_R_CANCELLED, the new code could return generic + * ISC_R_TLSERROR code. However, the old code does not expect + * that. + */ + result = ISC_R_CANCELED; + goto error; + } else if (result != ISC_R_SUCCESS) { + goto error; + } + + INSIST(VALID_NMHANDLE(handle)); + + sock->iface = isc_nmhandle_localaddr(handle); + sock->peer = isc_nmhandle_peeraddr(handle); + if (isc__nmsocket_closing(handle->sock)) { + result = ISC_R_SHUTTINGDOWN; + goto error; + } + + isc_nmhandle_attach(handle, &sock->outerhandle); + atomic_store(&sock->active, true); + + handle->sock->streamdns.sock = sock; + + streamdns_save_alpn_status(sock, handle); + isc__nmhandle_set_manual_timer(sock->outerhandle, true); + streamhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); + streamdns_call_connect_cb(sock, streamhandle, result); + isc_nmhandle_detach(&streamhandle); + + return; +error: + if (handle != NULL) { + /* + * Let's save the error description (if any) so that + * e.g. 'dig' could produce a usable error message. + */ + INSIST(VALID_NMHANDLE(handle)); + sock->streamdns.tls_verify_error = + isc_nm_verify_tls_peer_result_string(handle); + } + streamhandle = isc__nmhandle_get(sock, NULL, NULL); + atomic_store(&sock->closed, true); + streamdns_call_connect_cb(sock, streamhandle, result); + isc_nmhandle_detach(&streamhandle); + isc__nmsocket_detach(&sock); +} + +void +isc_nm_streamdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, + isc_sockaddr_t *peer, isc_nm_cb_t cb, void *cbarg, + unsigned int timeout, isc_tlsctx_t *ctx, + isc_tlsctx_client_session_cache_t *client_sess_cache) { + isc_nmsocket_t *nsock = NULL; + isc__networker_t *worker = &mgr->workers[isc_tid()]; + + REQUIRE(VALID_NM(mgr)); + + if (isc__nm_closing(worker)) { + cb(NULL, ISC_R_SHUTTINGDOWN, cbarg); + return; + } + + nsock = streamdns_sock_new(worker, isc_nm_streamdnssocket, local, + false); + nsock->connect_cb = cb; + nsock->connect_cbarg = cbarg; + nsock->connect_timeout = timeout; + + if (ctx == NULL) { + INSIST(client_sess_cache == NULL); + isc_nm_tcpconnect(mgr, local, peer, + streamdns_transport_connected, nsock, + nsock->connect_timeout); + } else { + isc_nm_tlsconnect(mgr, local, peer, + streamdns_transport_connected, nsock, ctx, + client_sess_cache, nsock->connect_timeout); + } +} + +static bool +streamdns_waiting_for_msg(isc_nmsocket_t *sock) { + /* There is an unsatisfied read operation pending */ + return (sock->recv_read); +} + +bool +isc__nmsocket_streamdns_timer_running(isc_nmsocket_t *sock) { + isc_nmsocket_t *transp_sock; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (sock->outerhandle == NULL) { + return (false); + } + + INSIST(VALID_NMHANDLE(sock->outerhandle)); + transp_sock = sock->outerhandle->sock; + INSIST(VALID_NMSOCK(transp_sock)); + + return (isc__nmsocket_timer_running(transp_sock)); +} + +void +isc__nmsocket_streamdns_timer_stop(isc_nmsocket_t *sock) { + isc_nmsocket_t *transp_sock; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (sock->outerhandle == NULL) { + return; + } + + INSIST(VALID_NMHANDLE(sock->outerhandle)); + transp_sock = sock->outerhandle->sock; + INSIST(VALID_NMSOCK(transp_sock)); + + isc__nmsocket_timer_stop(transp_sock); +} + +void +isc__nmsocket_streamdns_timer_restart(isc_nmsocket_t *sock) { + isc_nmsocket_t *transp_sock; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (sock->outerhandle == NULL) { + return; + } + + INSIST(VALID_NMHANDLE(sock->outerhandle)); + transp_sock = sock->outerhandle->sock; + INSIST(VALID_NMSOCK(transp_sock)); + + isc__nmsocket_timer_restart(transp_sock); +} + +static void +streamdns_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result, + const bool async) { + bool destroy = true; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(result != ISC_R_SUCCESS); + + if (sock->recv_cb != NULL && sock->recv_handle != NULL && + (streamdns_waiting_for_msg(sock) || result == ISC_R_TIMEDOUT)) + { + isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); + + INSIST(VALID_NMHANDLE(sock->recv_handle)); + + if (result != ISC_R_TIMEDOUT) { + sock->recv_read = false; + isc_dnsstream_assembler_clear(sock->streamdns.input); + isc__nmsocket_clearcb(sock); + } else if (atomic_load(&sock->client)) { + sock->recv_read = false; + } + isc__nm_readcb(sock, req, result, async); + if (result == ISC_R_TIMEDOUT && + isc__nmsocket_streamdns_timer_running(sock)) + { + destroy = false; + } + } + + if (destroy) { + isc__nmsocket_prep_destroy(sock); + } +} + +void +isc__nm_streamdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, + const bool async) { + REQUIRE(result != ISC_R_SUCCESS); + REQUIRE(sock->type == isc_nm_streamdnssocket); + sock->streamdns.reading = false; + streamdns_failed_read_cb(sock, result, async); +} + +static void +streamdns_readcb(isc_nmhandle_t *handle, isc_result_t result, + isc_region_t *region, void *cbarg) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_tid()); + + if (result != ISC_R_SUCCESS) { + streamdns_failed_read_cb(sock, result, false); + return; + } else if (streamdns_closing(sock)) { + streamdns_failed_read_cb(sock, ISC_R_CANCELED, false); + return; + } + + streamdns_handle_incoming_data(sock, handle, region->base, + region->length); +} + +static void +streamdns_try_close_unused(isc_nmsocket_t *sock) { + if (sock->recv_handle == NULL && sock->streamdns.nsending == 0) { + /* + * The socket is unused after calling the callback. Let's close + * the underlying connection. + */ + isc__nmsocket_prep_destroy(sock); + } +} + +static streamdns_send_req_t * +streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, + isc__nm_uvreq_t *req, isc_region_t *data) { + streamdns_send_req_t *send_req; + + if (sock->streamdns.send_req != NULL) { + /* + * We have a previously allocated object - let's use that. + * That should help reducing stress on the memory allocator. + */ + send_req = (streamdns_send_req_t *)sock->streamdns.send_req; + sock->streamdns.send_req = NULL; + } else { + /* Allocate a new object. */ + send_req = isc_mem_get(mctx, sizeof(*send_req)); + *send_req = (streamdns_send_req_t){ 0 }; + isc_dnsbuffer_init(&send_req->data, mctx); + } + + /* Initialise the send request object */ + send_req->cb = req->cb.send; + send_req->cbarg = req->cbarg; + isc_nmhandle_attach(req->handle, &send_req->dnshandle); + + /* Prepare the message */ + /* 1. Add the message length at the very beginning of the message */ + isc_dnsbuffer_putmem_uint16be(&send_req->data, + (uint16_t)req->uvbuf.len); + /* 2. Append the data itself */ + isc_dnsbuffer_putmem(&send_req->data, req->uvbuf.base, req->uvbuf.len); + isc_dnsbuffer_remainingregion(&send_req->data, data); + + sock->streamdns.nsending++; + + return (send_req); +} + +static void +streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, + const bool force_destroy) { + /* + * Attempt to put the object for reuse later if we are not + * wrapping up. + */ + if (!force_destroy) { + isc_nmsocket_t *sock = send_req->dnshandle->sock; + sock->streamdns.nsending--; + isc_nmhandle_detach(&send_req->dnshandle); + if (sock->streamdns.send_req == NULL) { + isc_dnsbuffer_clear(&send_req->data); + sock->streamdns.send_req = send_req; + /* + * An object has been recycled, + * if not - we are going to destroy it. + */ + return; + } + } + + isc_dnsbuffer_uninit(&send_req->data); + isc_mem_put(mctx, send_req, sizeof(*send_req)); +} + +static void +streamdns_writecb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { + streamdns_send_req_t *send_req = (streamdns_send_req_t *)cbarg; + isc_mem_t *mctx; + isc_nm_cb_t cb; + void *send_cbarg; + isc_nmhandle_t *dnshandle = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMHANDLE(send_req->dnshandle)); + REQUIRE(VALID_NMSOCK(send_req->dnshandle->sock)); + REQUIRE(send_req->dnshandle->sock->tid == isc_tid()); + + mctx = send_req->dnshandle->sock->worker->mctx; + cb = send_req->cb; + send_cbarg = send_req->cbarg; + + isc_nmhandle_attach(send_req->dnshandle, &dnshandle); + /* try to keep the send request object for reuse */ + streamdns_put_send_req(mctx, send_req, false); + cb(dnshandle, result, send_cbarg); + streamdns_try_close_unused(dnshandle->sock); + isc_nmhandle_detach(&dnshandle); +} + +static bool +streamdns_closing(isc_nmsocket_t *sock) { + return (isc__nmsocket_closing(sock) || isc__nm_closing(sock->worker) || + sock->outerhandle == NULL || + (sock->outerhandle != NULL && + isc__nmsocket_closing(sock->outerhandle->sock))); +} + +static void +streamdns_resume_processing(void *arg) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)arg; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_tid()); + REQUIRE(!atomic_load(&sock->client)); + + if (streamdns_closing(sock)) { + return; + } + + streamdns_handle_incoming_data(sock, sock->outerhandle, NULL, 0); +} + +static isc_result_t +streamdns_accept_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { + isc_nmsocket_t *listensock = (isc_nmsocket_t *)cbarg; + isc_nmsocket_t *nsock; + isc_sockaddr_t iface; + int tid; + uint32_t initial = 0; + + if (result != ISC_R_SUCCESS) { + return (result); + } + + INSIST(VALID_NMHANDLE(handle)); + INSIST(VALID_NMSOCK(handle->sock)); + INSIST(VALID_NMSOCK(listensock)); + INSIST(listensock->type == isc_nm_streamdnslistener); + + if (isc__nm_closing(handle->sock->worker)) { + return (ISC_R_SHUTTINGDOWN); + } else if (isc__nmsocket_closing(handle->sock) || + atomic_load(&listensock->closing)) + { + return (ISC_R_CANCELED); + } + + tid = isc_tid(); + iface = isc_nmhandle_localaddr(handle); + nsock = streamdns_sock_new(handle->sock->worker, isc_nm_streamdnssocket, + &iface, true); + nsock->recv_cb = listensock->recv_cb; + nsock->recv_cbarg = listensock->recv_cbarg; + + nsock->peer = isc_nmhandle_peeraddr(handle); + nsock->tid = tid; + isc_nm_gettimeouts(handle->sock->worker->netmgr, &initial, NULL, NULL, + NULL); + nsock->read_timeout = initial; + atomic_init(&nsock->accepting, true); + atomic_store(&nsock->active, true); + + isc__nmsocket_attach(listensock, &nsock->listener); + isc_nmhandle_attach(handle, &nsock->outerhandle); + handle->sock->streamdns.sock = nsock; + + streamdns_save_alpn_status(nsock, handle); + + nsock->recv_handle = isc__nmhandle_get(nsock, NULL, &iface); + INSIST(listensock->accept_cb != NULL); + result = listensock->accept_cb(nsock->recv_handle, result, + listensock->accept_cbarg); + if (result != ISC_R_SUCCESS) { + isc_nmhandle_detach(&nsock->recv_handle); + isc__nmsocket_detach(&nsock->listener); + isc_nmhandle_detach(&nsock->outerhandle); + atomic_store(&nsock->closed, true); + goto exit; + } + + nsock->closehandle_cb = streamdns_resume_processing; + isc__nmhandle_set_manual_timer(nsock->outerhandle, true); + isc_nm_gettimeouts(nsock->worker->netmgr, &initial, NULL, NULL, NULL); + /* settimeout restarts the timer */ + isc_nmhandle_settimeout(nsock->outerhandle, initial); + streamdns_handle_incoming_data(nsock, nsock->outerhandle, NULL, 0); + +exit: + atomic_store(&nsock->accepting, false); + + return (result); +} + +isc_result_t +isc_nm_listenstreamdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, + isc_nm_recv_cb_t recv_cb, void *recv_cbarg, + isc_nm_accept_cb_t accept_cb, void *accept_cbarg, + int backlog, isc_quota_t *quota, isc_tlsctx_t *tlsctx, + isc_nmsocket_t **sockp) { + isc_result_t result; + isc_nmsocket_t *listener = NULL; + isc__networker_t *worker = &mgr->workers[isc_tid()]; + + REQUIRE(VALID_NM(mgr)); + REQUIRE(isc_tid() == 0); + + if (isc__nm_closing(worker)) { + return (ISC_R_SHUTTINGDOWN); + } + + listener = streamdns_sock_new(worker, isc_nm_streamdnslistener, iface, + true); + listener->accept_cb = accept_cb; + listener->accept_cbarg = accept_cbarg; + listener->recv_cb = recv_cb; + listener->recv_cbarg = recv_cbarg; + + if (tlsctx == NULL) { + result = isc_nm_listentcp(mgr, workers, iface, + streamdns_accept_cb, listener, + backlog, quota, &listener->outer); + } else { + result = isc_nm_listentls( + mgr, workers, iface, streamdns_accept_cb, listener, + backlog, quota, tlsctx, &listener->outer); + } + if (result != ISC_R_SUCCESS) { + atomic_store(&listener->closed, true); + isc__nmsocket_detach(&listener); + return (result); + } + + listener->result = result; + atomic_store(&listener->active, true); + atomic_store(&listener->listening, true); + INSIST(listener->outer->streamdns.listener == NULL); + listener->nchildren = listener->outer->nchildren; + isc__nmsocket_barrier_init(listener); + atomic_init(&listener->rchildren, listener->outer->nchildren); + isc__nmsocket_attach(listener, &listener->outer->streamdns.listener); + + *sockp = listener; + + return (result); +} + +void +isc__nm_streamdns_cleanup_data(isc_nmsocket_t *sock) { + switch (sock->type) { + case isc_nm_streamdnssocket: + isc_dnsstream_assembler_free(&sock->streamdns.input); + INSIST(sock->streamdns.nsending == 0); + if (sock->streamdns.send_req != NULL) { + isc_mem_t *mctx = sock->worker->mctx; + streamdns_put_send_req(mctx, + (streamdns_send_req_t *) + sock->streamdns.send_req, + true); + } + break; + case isc_nm_streamdnslistener: + if (sock->outer) { + isc__nmsocket_detach(&sock->outer); + } + break; + case isc_nm_tlslistener: + case isc_nm_tcplistener: + if (sock->streamdns.listener != NULL) { + isc__nmsocket_detach(&sock->streamdns.listener); + } + break; + case isc_nm_tlssocket: + case isc_nm_tcpsocket: + if (sock->streamdns.sock != NULL) { + isc__nmsocket_detach(&sock->streamdns.sock); + } + break; + default: + return; + } +} + +void +isc__nm_async_streamdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_streamdnsread_t *ievent = + (isc__netievent_streamdnsread_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + + REQUIRE(sock->tid == isc_tid()); + UNUSED(worker); + + if (streamdns_closing(sock)) { + streamdns_failed_read_cb(sock, ISC_R_CANCELED, false); + return; + } + + if (sock->streamdns.reading) { + return; + } + + INSIST(VALID_NMHANDLE(sock->outerhandle)); + streamdns_handle_incoming_data(sock, sock->outerhandle, NULL, 0); +} + +void +isc__nm_streamdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, + void *cbarg) { + isc_nmsocket_t *sock = NULL; + bool closing = false; + bool worker_thread; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + REQUIRE(sock->recv_handle == NULL); + + closing = streamdns_closing(sock); + worker_thread = sock->tid == isc_tid(); + + sock->recv_cb = cb; + sock->recv_cbarg = cbarg; + sock->recv_read = true; + isc_nmhandle_attach(handle, &sock->recv_handle); + + /* + * In some cases there is little sense in making the operation + * asynchronous as we just want to start reading from the + * underlying transport. + */ + if (worker_thread && !closing && + isc_dnsstream_assembler_result(sock->streamdns.input) == + ISC_R_UNSET) + { + isc__netievent_streamdnsread_t event = { .sock = sock }; + isc__nm_async_streamdnsread(sock->worker, + (isc__netievent_t *)&event); + } else { + isc__netievent_streamdnsread_t *ievent = NULL; + /* + * We want the read operation to be asynchronous in most cases + * because: + * + * 1. A read operation might be initiated from within the read + * callback itself. + * + * 2. Due to the above, we need to make the operation + * asynchronous to keep the socket state consistent. + */ + ievent = isc__nm_get_netievent_streamdnsread(sock->worker, + sock); + isc__nm_enqueue_ievent(sock->worker, + (isc__netievent_t *)ievent); + } +} + +void +isc__nm_async_streamdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_streamdnssend_t *ievent = + (isc__netievent_streamdnssend_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + isc__nm_uvreq_t *req = ievent->req; + streamdns_send_req_t *send_req; + isc_mem_t *mctx; + isc_region_t data = { 0 }; + + REQUIRE(VALID_UVREQ(req)); + REQUIRE(sock->tid == isc_tid()); + + UNUSED(worker); + + ievent->req = NULL; + + if (streamdns_closing(sock)) { + isc__nm_failed_send_cb(sock, req, ISC_R_CANCELED, true); + return; + } + + mctx = sock->worker->mctx; + + send_req = streamdns_get_send_req(sock, mctx, req, &data); + isc_nm_send(sock->outerhandle, &data, streamdns_writecb, + (void *)send_req); + + isc__nm_uvreq_put(&req, sock); + return; +} + +void +isc__nm_streamdns_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + isc__nm_uvreq_t *uvreq = NULL; + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(region->length <= UINT16_MAX); + + sock = handle->sock; + + REQUIRE(sock->type == isc_nm_streamdnssocket); + + uvreq = isc__nm_uvreq_get(sock->worker, 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; + + /* + * As when sending, we, basically, handing data to the underlying + * transport, we can treat the operation synchronously, as the + * transport code will take care of the asynchronicity if required. + */ + if (sock->tid == isc_tid()) { + isc__netievent_streamdnssend_t event = { .sock = sock, + .req = uvreq }; + isc__nm_async_streamdnssend(sock->worker, + (isc__netievent_t *)&event); + } else { + isc__netievent_streamdnssend_t *ievent = + isc__nm_get_netievent_streamdnssend(sock->worker, sock, + uvreq); + isc__nm_enqueue_ievent(sock->worker, + (isc__netievent_t *)ievent); + } +} + +static void +streamdns_close_direct(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_tid()); + + if (sock->outerhandle != NULL) { + sock->streamdns.reading = false; + isc_nm_read_stop(sock->outerhandle); + isc_nmhandle_close(sock->outerhandle); + isc_nmhandle_detach(&sock->outerhandle); + } + + if (sock->listener != NULL) { + isc__nmsocket_detach(&sock->listener); + } + + if (sock->recv_handle != NULL) { + isc_nmhandle_detach(&sock->recv_handle); + } + + /* Further cleanup performed in isc__nm_streamdns_cleanup_data() */ + isc_dnsstream_assembler_clear(sock->streamdns.input); + atomic_store(&sock->closed, true); + atomic_store(&sock->active, false); +} + +void +isc__nm_async_streamdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_streamdnsclose_t *ievent = + (isc__netievent_streamdnsclose_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + + UNUSED(worker); + + streamdns_close_direct(sock); +} + +void +isc__nm_streamdns_close(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, + true)) + { + return; + } + + if (sock->tid == isc_tid()) { + streamdns_close_direct(sock); + } else { + isc__netievent_streamdnsclose_t *ievent = + isc__nm_get_netievent_streamdnsclose(sock->worker, + sock); + isc__nm_enqueue_ievent(sock->worker, + (isc__netievent_t *)ievent); + } +} + +void +isc__nm_streamdns_stoplistening(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnslistener); + + isc__nmsocket_stop(sock); +} + +void +isc__nm_streamdns_cancelread(isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + + isc__netievent_streamdnscancel_t *ievent = + isc__nm_get_netievent_streamdnscancel(sock->worker, + handle->sock, handle); + isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); +} + +void +isc__nm_async_streamdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_streamdnscancel_t *ievent = + (isc__netievent_streamdnscancel_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; + + UNUSED(worker); + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_tid()); + + streamdns_failed_read_cb(sock, ISC_R_EOF, false); +} + +void +isc__nmhandle_streamdns_cleartimeout(isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nmhandle_cleartimeout(sock->outerhandle); + } +} + +void +isc__nmhandle_streamdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nmhandle_settimeout(sock->outerhandle, timeout); + } +} + +void +isc__nmhandle_streamdns_keepalive(isc_nmhandle_t *handle, bool value) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nmhandle_keepalive(sock->outerhandle, value); + } +} + +void +isc__nmhandle_streamdns_setwritetimeout(isc_nmhandle_t *handle, + uint32_t timeout) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nmhandle_setwritetimeout(sock->outerhandle, timeout); + } +} + +bool +isc__nm_streamdns_has_encryption(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + return (isc_nm_has_encryption(sock->outerhandle)); + } + + return (false); +} + +const char * +isc__nm_streamdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_streamdnssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + return (isc_nm_verify_tls_peer_result_string( + sock->outerhandle)); + } else if (sock->streamdns.tls_verify_error != NULL) { + return (sock->streamdns.tls_verify_error); + } + + return (false); +} + +void +isc__nm_streamdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { + REQUIRE(VALID_NMSOCK(listener)); + REQUIRE(listener->type == isc_nm_streamdnslistener); + + if (listener->outer != NULL) { + INSIST(VALID_NMSOCK(listener->outer)); + isc_nmsocket_set_tlsctx(listener->outer, tlsctx); + } +} + +bool +isc__nm_streamdns_xfr_allowed(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (sock->outerhandle == NULL) { + return (false); + } else if (!isc_nm_has_encryption(sock->outerhandle)) { + return (true); + } + + return (sock->streamdns.dot_alpn_negotiated); +} + +void +isc__nmsocket_streamdns_reset(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->type == isc_nm_streamdnssocket); + + if (sock->outerhandle == NULL) { + return; + } + + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc__nmsocket_reset(sock->outerhandle->sock); +} From 85cefb80f8ea6649b7ff3fab19dc21f9087328b8 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 8 Nov 2022 16:43:01 +0200 Subject: [PATCH 11/33] Re-purpose TLS DNS and TCP DNS unit tests for Stream DNS This commit modifies the existing unit tests for TLS DNS and TCP DNS in such a way that the new Stream DNS transport is used as it is intended to be a drop-in replacement for these two transports. --- bin/tests/test_client.c | 10 ++++++---- tests/isc/tcpdns_test.c | 19 +++++++++++-------- tests/isc/tlsdns_test.c | 30 +++++++++++++++++------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/bin/tests/test_client.c b/bin/tests/test_client.c index 15cf35125d..e4a133c034 100644 --- a/bin/tests/test_client.c +++ b/bin/tests/test_client.c @@ -380,14 +380,16 @@ run(void) { connect_cb, NULL, timeout); break; case TCP: - isc_nm_tcpdnsconnect(netmgr, &sockaddr_local, &sockaddr_remote, - connect_cb, NULL, timeout); + isc_nm_streamdnsconnect(netmgr, &sockaddr_local, + &sockaddr_remote, connect_cb, NULL, + timeout, NULL, NULL); break; case DOT: { isc_tlsctx_createclient(&tls_ctx); - isc_nm_tlsdnsconnect(netmgr, &sockaddr_local, &sockaddr_remote, - connect_cb, NULL, timeout, tls_ctx, NULL); + isc_nm_streamdnsconnect(netmgr, &sockaddr_local, + &sockaddr_remote, connect_cb, NULL, + timeout, tls_ctx, NULL); break; } #if HAVE_LIBNGHTTP2 diff --git a/tests/isc/tcpdns_test.c b/tests/isc/tcpdns_test.c index 43b75a70a3..dabd0e84e4 100644 --- a/tests/isc/tcpdns_test.c +++ b/tests/isc/tcpdns_test.c @@ -50,9 +50,9 @@ stop_listening(void *arg __attribute__((__unused__))) { static void start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb, isc_nm_recv_cb_t recv_cb) { - isc_result_t result = isc_nm_listentcpdns( + isc_result_t result = isc_nm_listenstreamdns( listen_nm, nworkers, &tcp_listen_addr, recv_cb, NULL, accept_cb, - NULL, 128, NULL, &listen_sock); + NULL, 128, NULL, NULL, &listen_sock); assert_int_equal(result, ISC_R_SUCCESS); isc_loop_teardown(mainloop, stop_listening, listen_sock); @@ -60,8 +60,9 @@ start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb, static void tcpdns_connect(isc_nm_t *nm) { - isc_nm_tcpdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, - connect_connect_cb, tcpdns_connect, T_CONNECT); + isc_nm_streamdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, + connect_connect_cb, tcpdns_connect, T_CONNECT, + NULL, NULL); } ISC_LOOP_TEST_IMPL(tcpdns_noop) { @@ -69,16 +70,18 @@ ISC_LOOP_TEST_IMPL(tcpdns_noop) { connect_readcb = NULL; isc_refcount_increment0(&active_cconnects); - isc_nm_tcpdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, - connect_success_cb, tcpdns_connect, T_CONNECT); + isc_nm_streamdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, + connect_success_cb, tcpdns_connect, T_CONNECT, + NULL, NULL); } ISC_LOOP_TEST_IMPL(tcpdns_noresponse) { start_listening(ISC_NM_LISTEN_ALL, noop_accept_cb, noop_recv_cb); isc_refcount_increment0(&active_cconnects); - isc_nm_tcpdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, - connect_connect_cb, tcpdns_connect, T_CONNECT); + isc_nm_streamdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, + connect_connect_cb, tcpdns_connect, T_CONNECT, + NULL, NULL); } ISC_LOOP_TEST_IMPL(tcpdns_timeout_recovery) { diff --git a/tests/isc/tlsdns_test.c b/tests/isc/tlsdns_test.c index a8de6bce4a..b33496ba7d 100644 --- a/tests/isc/tlsdns_test.c +++ b/tests/isc/tlsdns_test.c @@ -49,7 +49,7 @@ stop_listening(void *arg __attribute__((__unused__))) { static void start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb, isc_nm_recv_cb_t recv_cb) { - isc_result_t result = isc_nm_listentlsdns( + isc_result_t result = isc_nm_listenstreamdns( listen_nm, nworkers, &tcp_listen_addr, recv_cb, NULL, accept_cb, NULL, 128, NULL, tcp_listen_tlsctx, &listen_sock); assert_int_equal(result, ISC_R_SUCCESS); @@ -59,9 +59,10 @@ start_listening(uint32_t nworkers, isc_nm_accept_cb_t accept_cb, static void tlsdns_connect(isc_nm_t *nm) { - isc_nm_tlsdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, - connect_connect_cb, tlsdns_connect, T_CONNECT, - tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); + isc_nm_streamdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, + connect_connect_cb, tlsdns_connect, T_CONNECT, + tcp_connect_tlsctx, + tcp_tlsctx_client_sess_cache); } ISC_LOOP_TEST_IMPL(tlsdns_noop) { @@ -69,18 +70,20 @@ ISC_LOOP_TEST_IMPL(tlsdns_noop) { connect_readcb = NULL; isc_refcount_increment0(&active_cconnects); - isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, - connect_success_cb, tlsdns_connect, T_CONNECT, - tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); + isc_nm_streamdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, + connect_success_cb, tlsdns_connect, T_CONNECT, + tcp_connect_tlsctx, + tcp_tlsctx_client_sess_cache); } ISC_LOOP_TEST_IMPL(tlsdns_noresponse) { start_listening(ISC_NM_LISTEN_ALL, noop_accept_cb, noop_recv_cb); isc_refcount_increment0(&active_cconnects); - isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, - connect_connect_cb, tlsdns_connect, T_CONNECT, - tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); + isc_nm_streamdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, + connect_connect_cb, tlsdns_connect, T_CONNECT, + tcp_connect_tlsctx, + tcp_tlsctx_client_sess_cache); } ISC_LOOP_TEST_IMPL(tlsdns_timeout_recovery) { @@ -99,9 +102,10 @@ ISC_LOOP_TEST_IMPL(tlsdns_timeout_recovery) { connect_readcb = timeout_retry_cb; isc_nm_settimeouts(connect_nm, T_SOFT, T_SOFT, T_SOFT, T_SOFT); isc_refcount_increment0(&active_cconnects); - isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, - connect_connect_cb, tlsdns_connect, T_SOFT, - tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache); + isc_nm_streamdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, + connect_connect_cb, tlsdns_connect, T_SOFT, + tcp_connect_tlsctx, + tcp_tlsctx_client_sess_cache); } ISC_LOOP_TEST_IMPL(tlsdns_recv_one) { From 2b7e85591fe87311d084ac49ca84cef97aa63ee2 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 12 Jul 2022 23:08:07 +0300 Subject: [PATCH 12/33] Use Stream DNS in dig for DNS over TLS This commit makes dig use the new Stream DNS transport for DNS over TLS. --- bin/dig/dighost.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index b85142e6b4..7b7913ecd3 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -3037,9 +3037,9 @@ start_tcp(dig_query_t *query) { if (tlsctx == NULL) { goto failure_tls; } - isc_nm_tlsdnsconnect(netmgr, &localaddr, &query->sockaddr, - tcp_connected, connectquery, local_timeout, - tlsctx, sess_cache); + isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, + tcp_connected, connectquery, + local_timeout, tlsctx, sess_cache); #if HAVE_LIBNGHTTP2 } else if (query->lookup->https_mode) { char uri[4096] = { 0 }; @@ -3454,8 +3454,9 @@ launch_next_query(dig_query_t *query) { xfr = query->lookup->rdtype == dns_rdatatype_ixfr || query->lookup->rdtype == dns_rdatatype_axfr; - if (xfr && isc_nm_socket_type(query->handle) == isc_nm_tlsdnssocket && - !isc_nm_xfr_allowed(query->handle)) + if (xfr && + isc_nm_socket_type(query->handle) == isc_nm_streamdnssocket && + query->lookup->tls_mode && !isc_nm_xfr_allowed(query->handle)) { dighost_error("zone transfers over the " "established TLS connection are not allowed"); From 83f399b852bff3b64f3c3fc0378024c1ca442dc9 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 23 Aug 2022 19:18:31 +0300 Subject: [PATCH 13/33] Use Stream DNS in dig for DNS over TCP This commit makes dig use the new Stream DNS transport for DNS over TCP. --- bin/dig/dighost.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 7b7913ecd3..217dd3e6f8 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -3062,9 +3062,9 @@ start_tcp(dig_query_t *query) { local_timeout); #endif } else { - isc_nm_tcpdnsconnect(netmgr, &localaddr, &query->sockaddr, - tcp_connected, connectquery, - local_timeout); + isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, + tcp_connected, connectquery, + local_timeout, NULL, NULL); } /* XXX: set DSCP */ From 03e33a014c88ba81d7b362627f6f99073b57feee Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 29 Nov 2022 18:15:54 +0200 Subject: [PATCH 14/33] BIND: use Stream DNS for DNS over TLS connections This commit makes BIND use the new Stream DNS transport for DNS over TLS. --- bin/tests/test_server.c | 2 +- lib/dns/xfrin.c | 6 +++--- lib/isccfg/aclconf.c | 2 +- lib/ns/interfacemgr.c | 2 +- lib/ns/query.c | 4 +++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/bin/tests/test_server.c b/bin/tests/test_server.c index 05a3c0b13e..3ff507c28c 100644 --- a/bin/tests/test_server.c +++ b/bin/tests/test_server.c @@ -256,7 +256,7 @@ run(void) { case DOT: { isc_tlsctx_createserver(NULL, NULL, &tls_ctx); - result = isc_nm_listentlsdns( + result = isc_nm_listenstreamdns( netmgr, ISC_NM_LISTEN_ALL, &sockaddr, read_cb, NULL, accept_cb, NULL, 0, NULL, tls_ctx, &sock); break; diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index fdf2d03f50..b2c102b479 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -964,9 +964,9 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { goto failure; } INSIST(tlsctx != NULL); - isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr, - &xfr->primaryaddr, xfrin_connect_done, - connect_xfr, 30000, tlsctx, sess_cache); + isc_nm_streamdnsconnect(xfr->netmgr, &xfr->sourceaddr, + &xfr->primaryaddr, xfrin_connect_done, + connect_xfr, 30000, tlsctx, sess_cache); } break; default: UNREACHABLE(); diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index 289c177a8d..c980673267 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -731,7 +731,7 @@ cfg_acl_fromconfig2(const cfg_obj_t *acl_data, const cfg_obj_t *cctx, } else if (strcasecmp(cfg_obj_asstring(obj_transport), "tls") == 0) { - transports = isc_nm_tlsdnssocket; + transports = isc_nm_streamdnssocket; encrypted = true; } else if (strcasecmp(cfg_obj_asstring(obj_transport), "http") == 0) diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index dc86e2e975..22b54cce26 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -543,7 +543,7 @@ static isc_result_t ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) { isc_result_t result; - result = isc_nm_listentlsdns( + result = isc_nm_listenstreamdns( ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr, ns__client_request, ifp, ns__client_tcpconn, ifp, ifp->mgr->backlog, &ifp->mgr->sctx->tcpquota, sslctx, &ifp->tcplistensocket); diff --git a/lib/ns/query.c b/lib/ns/query.c index 71f0db8f9e..5bcbe7bb63 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -11962,7 +11962,9 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) { query_error(client, DNS_R_NOTIMP, __LINE__); return; } - if (isc_nm_socket_type(handle) == isc_nm_tlsdnssocket && + if (isc_nm_socket_type(handle) == + isc_nm_streamdnssocket && + isc_nm_has_encryption(handle) && !isc_nm_xfr_allowed(handle)) { /* From cce52fa4a2518938a30964a607da041f75c99be8 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Fri, 5 Aug 2022 19:43:34 +0300 Subject: [PATCH 15/33] BIND: use Stream DNS for DNS over TCP connections This commit makes BIND use the new Stream DNS transport for DNS over TCP. --- bin/tests/test_server.c | 6 +++--- lib/dns/dispatch.c | 13 +++++++------ lib/dns/xfrin.c | 6 +++--- lib/isccfg/aclconf.c | 4 ++-- lib/ns/interfacemgr.c | 4 ++-- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/bin/tests/test_server.c b/bin/tests/test_server.c index 3ff507c28c..05acf8430a 100644 --- a/bin/tests/test_server.c +++ b/bin/tests/test_server.c @@ -249,9 +249,9 @@ run(void) { read_cb, NULL, &sock); break; case TCP: - result = isc_nm_listentcpdns(netmgr, ISC_NM_LISTEN_ALL, - &sockaddr, read_cb, NULL, - accept_cb, NULL, 0, NULL, &sock); + result = isc_nm_listenstreamdns( + netmgr, ISC_NM_LISTEN_ALL, &sockaddr, read_cb, NULL, + accept_cb, NULL, 0, NULL, NULL, &sock); break; case DOT: { isc_tlsctx_createserver(NULL, NULL, &tls_ctx); diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index fc326970f3..ce315eb922 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -2036,13 +2036,14 @@ tcp_dispatch_connect(dns_dispatch_t *disp, dns_dispentry_t *resp) { peerbuf, resp->timeout); if (transport_type == DNS_TRANSPORT_TLS) { - isc_nm_tlsdnsconnect(disp->mgr->nm, &disp->local, - &disp->peer, tcp_connected, disp, - resp->timeout, tlsctx, sess_cache); + isc_nm_streamdnsconnect(disp->mgr->nm, &disp->local, + &disp->peer, tcp_connected, + disp, resp->timeout, tlsctx, + sess_cache); } else { - isc_nm_tcpdnsconnect(disp->mgr->nm, &disp->local, - &disp->peer, tcp_connected, disp, - resp->timeout); + isc_nm_streamdnsconnect( + disp->mgr->nm, &disp->local, &disp->peer, + tcp_connected, disp, resp->timeout, NULL, NULL); } break; diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index b2c102b479..ff1e524c0f 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -952,9 +952,9 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { */ switch (transport_type) { case DNS_TRANSPORT_TCP: - isc_nm_tcpdnsconnect(xfr->netmgr, &xfr->sourceaddr, - &xfr->primaryaddr, xfrin_connect_done, - connect_xfr, 30000); + isc_nm_streamdnsconnect(xfr->netmgr, &xfr->sourceaddr, + &xfr->primaryaddr, xfrin_connect_done, + connect_xfr, 30000, NULL, NULL); break; case DNS_TRANSPORT_TLS: { result = dns_transport_get_tlsctx( diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index c980673267..e72134c8a7 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -719,13 +719,13 @@ cfg_acl_fromconfig2(const cfg_obj_t *acl_data, const cfg_obj_t *cctx, } else if (strcasecmp(cfg_obj_asstring(obj_transport), "tcp") == 0) { - transports = isc_nm_tcpdnssocket; + transports = isc_nm_streamdnssocket; encrypted = false; } else if (strcasecmp(cfg_obj_asstring(obj_transport), "udp-tcp") == 0) { /* Good ol' DNS over port 53 */ - transports = isc_nm_tcpdnssocket | + transports = isc_nm_streamdnssocket | isc_nm_udpsocket; encrypted = false; } else if (strcasecmp(cfg_obj_asstring(obj_transport), diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index 22b54cce26..d9ccdeced8 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -503,10 +503,10 @@ static isc_result_t ns_interface_listentcp(ns_interface_t *ifp) { isc_result_t result; - result = isc_nm_listentcpdns( + result = isc_nm_listenstreamdns( ifp->mgr->nm, ISC_NM_LISTEN_ALL, &ifp->addr, ns__client_request, ifp, ns__client_tcpconn, ifp, ifp->mgr->backlog, - &ifp->mgr->sctx->tcpquota, &ifp->tcplistensocket); + &ifp->mgr->sctx->tcpquota, NULL, &ifp->tcplistensocket); if (result != ISC_R_SUCCESS) { isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, "creating TCP socket: %s", From 4606384345d299d0b910ac889549a228dcea27c7 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 25 Aug 2022 21:59:23 +0300 Subject: [PATCH 16/33] Extend isc__nm_socket_tcp_nodelay() to accept value This makes it possible to both enable and disable Nagle's algorithm for a TCP socket descriptor, before the change it was possible only to disable it. --- lib/isc/netmgr/http.c | 2 +- lib/isc/netmgr/netmgr-int.h | 5 +++-- lib/isc/netmgr/socket.c | 12 ++++++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 962a5b4386..c6f7eed961 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2434,7 +2434,7 @@ http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle) { (void)uv_fileno((uv_handle_t *)&tcpsock->uv_handle.tcp, &tcp_fd); RUNTIME_CHECK(tcp_fd != (uv_os_fd_t)-1); - (void)isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd); + (void)isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd, true); } static isc_result_t diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 807bd151a8..1c229d314a 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1861,9 +1861,10 @@ isc__nm_socket_connectiontimeout(uv_os_sock_t fd, int timeout_ms); */ isc_result_t -isc__nm_socket_tcp_nodelay(uv_os_sock_t fd); +isc__nm_socket_tcp_nodelay(const uv_os_sock_t fd, bool value); /*%< - * Disables Nagle's algorithm on a TCP socket (sets TCP_NODELAY). + * Disables/Enables Nagle's algorithm on a TCP socket (sets TCP_NODELAY if + * 'value' equals 'true' or vice versa). */ isc_result_t diff --git a/lib/isc/netmgr/socket.c b/lib/isc/netmgr/socket.c index b2dce27805..fc9b3ce880 100644 --- a/lib/isc/netmgr/socket.c +++ b/lib/isc/netmgr/socket.c @@ -327,9 +327,17 @@ isc__nm_socket_connectiontimeout(uv_os_sock_t fd, int timeout_ms) { } isc_result_t -isc__nm_socket_tcp_nodelay(uv_os_sock_t fd) { +isc__nm_socket_tcp_nodelay(uv_os_sock_t fd, bool value) { #ifdef TCP_NODELAY - if (setsockopt_on(fd, IPPROTO_TCP, TCP_NODELAY) == -1) { + int ret; + + if (value) { + ret = setsockopt_on(fd, IPPROTO_TCP, TCP_NODELAY); + } else { + ret = setsockopt_off(fd, IPPROTO_TCP, TCP_NODELAY); + } + + if (ret == -1) { return (ISC_R_FAILURE); } else { return (ISC_R_SUCCESS); From 371b02f37a93502a032097c5959881d2fc858270 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 25 Aug 2022 22:18:15 +0300 Subject: [PATCH 17/33] TCP: make it possible to set Nagle's algorithms state via handle This commit adds ability to turn the Nagle's algorithm on or off via connections handle. It adds the isc_nmhandle_set_tcp_nodelay() function as the public interface for this functionality. --- lib/isc/include/isc/netmgr.h | 12 ++++++++++++ lib/isc/netmgr/netmgr.c | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 729012ac63..3f0e90d808 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -744,3 +744,15 @@ isc_nm_timer_start(isc_nm_timer_t *, uint64_t); void isc_nm_timer_stop(isc_nm_timer_t *); + +isc_result_t +isc_nmhandle_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value); +/*%< + * Disables/Enables Nagle's algorithm on a TCP socket for a + * transport backed by TCP (sets TCP_NODELAY if 'value' equals 'true' + * or vice versa). + * + * Requires: + * + * \li 'handle' is a valid netmgr handle object. + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 9fdf77a96a..1cd09c1c48 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -3013,6 +3013,31 @@ isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, }; } +isc_result_t +isc_nmhandle_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value) { + isc_result_t result = ISC_R_FAILURE; + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + sock = handle->sock; + REQUIRE(VALID_NMSOCK(sock)); + + switch (sock->type) { + case isc_nm_tcpsocket: { + uv_os_fd_t tcp_fd = (uv_os_fd_t)-1; + (void)uv_fileno((uv_handle_t *)&sock->uv_handle.tcp, &tcp_fd); + RUNTIME_CHECK(tcp_fd != (uv_os_fd_t)-1); + result = isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd, + value); + } break; + default: + UNREACHABLE(); + break; + }; + + return (result); +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr From 05cfb27b809f55ecce5f7edef5b706b63738636e Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 25 Aug 2022 22:37:26 +0300 Subject: [PATCH 18/33] Disable Nagle's algorithm for TLS connections by default This commit ensures that Nagle's algorithm is disabled by default for TLS connections on best effort basis, just like other networking software (e.g. NGINX) does, as, in the case of TLS, we are not interested in trading latency for throughput, rather vice versa. We attempt to disable it as early as we can, right after TCP connections establishment, as an attempt to speed up handshake handling. --- lib/isc/netmgr/netmgr-int.h | 4 ++++ lib/isc/netmgr/netmgr.c | 3 +++ lib/isc/netmgr/tlsstream.c | 45 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 1c229d314a..0ce595c113 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -924,6 +924,7 @@ struct isc_nmsocket { TLS_CLOSED } state; /*%< The order of these is significant */ size_t nsending; + bool tcp_nodelay_value; } tlsstream; isc_nmsocket_h2_t h2; @@ -1625,6 +1626,9 @@ isc__nmhandle_tls_get_selected_alpn(isc_nmhandle_t *handle, const unsigned char **alpn, unsigned int *alpnlen); +isc_result_t +isc__nmhandle_tls_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value); + void isc__nm_http_stoplistening(isc_nmsocket_t *sock); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 1cd09c1c48..8577ac0197 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -3030,6 +3030,9 @@ isc_nmhandle_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value) { result = isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd, value); } break; + case isc_nm_tlssocket: + result = isc__nmhandle_tls_set_tcp_nodelay(handle, value); + break; default: UNREACHABLE(); break; diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 834a386ce6..05d18e30bf 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -94,6 +94,9 @@ tls_keep_client_tls_session(isc_nmsocket_t *sock); static void tls_try_shutdown(isc_tls_t *tls, const bool quite); +static void +tls_try_to_enable_tcp_nodelay(isc_nmsocket_t *tlssock); + /* * The socket is closing, outerhandle has been detached, listener is * inactive, or the netmgr is closing: any operation on it should abort @@ -739,6 +742,17 @@ error: return (ISC_R_TLSERROR); } +static void +tls_try_to_enable_tcp_nodelay(isc_nmsocket_t *tlssock) { + /* + * Try to enable TCP_NODELAY for TLS connections by default to speed up + * the handshakes, just like other software (e.g. NGINX) does. + */ + isc_result_t result = isc_nmhandle_set_tcp_nodelay(tlssock->outerhandle, + true); + tlssock->tlsstream.tcp_nodelay_value = (result == 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; @@ -796,6 +810,8 @@ tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { RUNTIME_CHECK(result == ISC_R_SUCCESS); /* TODO: catch failure code, detach tlssock, and log the error */ + tls_try_to_enable_tcp_nodelay(tlssock); + isc__nmhandle_set_manual_timer(tlssock->outerhandle, true); tls_do_bio(tlssock, NULL, NULL, false); return (result); @@ -1114,6 +1130,8 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { */ handle->sock->tlsstream.tlssocket = tlssock; + tls_try_to_enable_tcp_nodelay(tlssock); + isc__nmhandle_set_manual_timer(tlssock->outerhandle, true); tls_do_bio(tlssock, NULL, NULL, false); return; @@ -1417,3 +1435,30 @@ isc__nmhandle_tls_get_selected_alpn(isc_nmhandle_t *handle, isc_tls_get_selected_alpn(sock->tlsstream.tls, alpn, alpnlen); } + +isc_result_t +isc__nmhandle_tls_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value) { + isc_nmsocket_t *sock = NULL; + isc_result_t result = ISC_R_FAILURE; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_tlssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + + if (value == sock->tlsstream.tcp_nodelay_value) { + result = ISC_R_SUCCESS; + } else { + result = isc_nmhandle_set_tcp_nodelay(sock->outerhandle, + value); + if (result == ISC_R_SUCCESS) { + sock->tlsstream.tcp_nodelay_value = value; + } + } + } + + return (result); +} From e89575ddce3e2a794bf411c313829afdfec11b23 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 27 Oct 2022 21:14:35 +0300 Subject: [PATCH 19/33] StreamDNS: opportunistically disable Nagle's algorithm This commit ensures that Stream DNS code attempts to disable Nagle's algorithm regardless of underlying stream transport (TCP or TLS), as we are not interested in trading latency for throughout when dealing with DNS messages. --- lib/isc/netmgr/streamdns.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c index 6e3a5bfe5b..c72697c8f3 100644 --- a/lib/isc/netmgr/streamdns.c +++ b/lib/isc/netmgr/streamdns.c @@ -356,6 +356,7 @@ streamdns_transport_connected(isc_nmhandle_t *handle, isc_result_t result, streamdns_save_alpn_status(sock, handle); isc__nmhandle_set_manual_timer(sock->outerhandle, true); streamhandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); + (void)isc_nmhandle_set_tcp_nodelay(sock->outerhandle, true); streamdns_call_connect_cb(sock, streamhandle, result); isc_nmhandle_detach(&streamhandle); @@ -720,6 +721,7 @@ streamdns_accept_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { isc_nm_gettimeouts(nsock->worker->netmgr, &initial, NULL, NULL, NULL); /* settimeout restarts the timer */ isc_nmhandle_settimeout(nsock->outerhandle, initial); + (void)isc_nmhandle_set_tcp_nodelay(nsock->outerhandle, true); streamdns_handle_incoming_data(nsock, nsock->outerhandle, NULL, 0); exit: From efe4267044fc81247ddef333184e224d33e52bd2 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 25 Aug 2022 22:44:59 +0300 Subject: [PATCH 20/33] DoH: use isc_nmhandle_set_tcp_nodelay() This commit replaces ad-hoc code for disabling Nagle's algorithm with a call to isc_nmhandle_set_tcp_nodelay(). --- lib/isc/netmgr/http.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index c6f7eed961..4eb5a147b6 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2423,18 +2423,7 @@ server_send_connection_header(isc_nm_http_session_t *session) { */ static void http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle) { - isc_nmsocket_t *tcpsock = NULL; - uv_os_fd_t tcp_fd = (uv_os_fd_t)-1; - - if (transphandle->sock->type == isc_nm_tlssocket) { - tcpsock = transphandle->sock->outerhandle->sock; - } else { - tcpsock = transphandle->sock; - } - - (void)uv_fileno((uv_handle_t *)&tcpsock->uv_handle.tcp, &tcp_fd); - RUNTIME_CHECK(tcp_fd != (uv_os_fd_t)-1); - (void)isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd, true); + (void)isc_nmhandle_set_tcp_nodelay(transphandle, true); } static isc_result_t From 4524bf4083e8413a319d7e27ccdd4d03a0ba975b Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 18 Oct 2022 15:36:00 +0300 Subject: [PATCH 21/33] Make isc_nm_tlssocket non-optional This commit unties generic TLS code (isc_nm_tlssocket) from DoH, so that it will be available regardless of the fact if BIND was built with DNS over HTTP support or not. --- lib/isc/Makefile.am | 4 +-- lib/isc/include/isc/netmgr.h | 8 +++--- lib/isc/netmgr/netmgr-int.h | 8 +++--- lib/isc/netmgr/netmgr.c | 52 ++++++++---------------------------- 4 files changed, 22 insertions(+), 50 deletions(-) diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index fb09d10145..ac261ccf91 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -120,6 +120,7 @@ libisc_la_SOURCES = \ netmgr/tcpdns.c \ netmgr/timer.c \ netmgr/tlsdns.c \ + netmgr/tlsstream.c \ netmgr/udp.c \ aes.c \ ascii.c \ @@ -256,8 +257,7 @@ endif HAVE_JSON_C if HAVE_LIBNGHTTP2 libisc_la_SOURCES += \ - netmgr/http.c \ - netmgr/tlsstream.c + netmgr/http.c libisc_la_CPPFLAGS += \ $(LIBNGHTTP2_CFLAGS) diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 3f0e90d808..f672d3e141 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -533,10 +533,6 @@ isc_nm_is_http_handle(isc_nmhandle_t *handle); * 'isc_nm_httpsocket'. */ -#if HAVE_LIBNGHTTP2 - -#define ISC_NM_HTTP_DEFAULT_PATH "/dns-query" - isc_result_t isc_nm_listentls(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, isc_nm_accept_cb_t accept_cb, void *accept_cbarg, int backlog, @@ -549,6 +545,10 @@ isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, isc_tlsctx_client_session_cache_t *client_sess_cache, unsigned int timeout); +#if HAVE_LIBNGHTTP2 + +#define ISC_NM_HTTP_DEFAULT_PATH "/dns-query" + void isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg, diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 0ce595c113..c65bb81b53 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -753,7 +753,6 @@ typedef enum { STATID_MAX = 11, } isc__nm_statid_t; -#if HAVE_LIBNGHTTP2 typedef struct isc_nmsocket_tls_send_req { isc_nmsocket_t *tlssock; isc_region_t data; @@ -764,6 +763,8 @@ typedef struct isc_nmsocket_tls_send_req { uint8_t smallbuf[512]; } isc_nmsocket_tls_send_req_t; +#if HAVE_LIBNGHTTP2 + typedef enum isc_http_request_type { ISC_HTTP_REQ_GET, ISC_HTTP_REQ_POST, @@ -901,7 +902,6 @@ struct isc_nmsocket { const char *tls_verify_errmsg; } tls; -#if HAVE_LIBNGHTTP2 /*% TLS stuff */ struct tlsstream { bool server; @@ -927,6 +927,7 @@ struct isc_nmsocket { bool tcp_nodelay_value; } tlsstream; +#if HAVE_LIBNGHTTP2 isc_nmsocket_h2_t h2; #endif /* HAVE_LIBNGHTTP2 */ @@ -1542,7 +1543,6 @@ isc__nm_tlsdns_xfr_allowed(isc_nmsocket_t *sock); void isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock); -#if HAVE_LIBNGHTTP2 void isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, void *cbarg); @@ -1629,6 +1629,8 @@ isc__nmhandle_tls_get_selected_alpn(isc_nmhandle_t *handle, isc_result_t isc__nmhandle_tls_set_tcp_nodelay(isc_nmhandle_t *handle, const bool value); +#if HAVE_LIBNGHTTP2 + void isc__nm_http_stoplistening(isc_nmsocket_t *sock); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 8577ac0197..1cdcfccd23 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -349,11 +349,9 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) { case isc_nm_tlsdnssocket: handle->sock->write_timeout = write_timeout; break; -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmhandle_tls_setwritetimeout(handle, write_timeout); break; -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_streamdnssocket: isc__nmhandle_streamdns_setwritetimeout(handle, write_timeout); break; @@ -474,11 +472,10 @@ process_netievent(void *arg) { NETIEVENT_CASE(tlsdnsstop); NETIEVENT_CASE(tlsdnsshutdown); -#if HAVE_LIBNGHTTP2 NETIEVENT_CASE(tlssend); NETIEVENT_CASE(tlsclose); NETIEVENT_CASE(tlsdobio); - +#if HAVE_LIBNGHTTP2 NETIEVENT_CASE(httpsend); NETIEVENT_CASE(httpclose); NETIEVENT_CASE(httpendpoints); @@ -724,8 +721,8 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { isc_astack_destroy(sock->inactivereqs); isc__nm_tlsdns_cleanup_data(sock); -#if HAVE_LIBNGHTTP2 isc__nm_tls_cleanup_data(sock); +#if HAVE_LIBNGHTTP2 isc__nm_http_cleanup_data(sock); #endif isc__nm_streamdns_cleanup_data(sock); @@ -861,10 +858,10 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_streamdnssocket: isc__nm_streamdns_close(sock); return; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_close(sock); return; +#if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: isc__nm_http_close(sock); return; @@ -1009,9 +1006,7 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker, isc_refcount_init(&sock->references, 1); -#if HAVE_LIBNGHTTP2 memset(&sock->tlsstream, 0, sizeof(sock->tlsstream)); -#endif /* HAVE_LIBNGHTTP2 */ NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %" PRIuFAST32 "\n", @@ -1420,11 +1415,9 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) { case isc_nm_tlsdnssocket: isc__nm_tlsdns_failed_read_cb(sock, result, async); return; -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_failed_read_cb(sock, result, async); return; -#endif case isc_nm_streamdnssocket: isc__nm_streamdns_failed_read_cb(sock, result, async); return; @@ -1534,11 +1527,9 @@ isc__nmsocket_timer_restart(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); switch (sock->type) { -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmsocket_tls_timer_restart(sock); return; -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_streamdnssocket: isc__nmsocket_streamdns_timer_restart(sock); return; @@ -1581,10 +1572,8 @@ isc__nmsocket_timer_running(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); switch (sock->type) { -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: return (isc__nmsocket_tls_timer_running(sock)); -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_streamdnssocket: return (isc__nmsocket_streamdns_timer_running(sock)); default: @@ -1612,11 +1601,9 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); switch (sock->type) { -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmsocket_tls_timer_stop(sock); return; -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_streamdnssocket: isc__nmsocket_streamdns_timer_stop(sock); return; @@ -1871,10 +1858,10 @@ isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) { case isc_nm_httpsocket: isc__nm_http_cleartimeout(handle); return; +#endif case isc_nm_tlssocket: isc__nm_tls_cleartimeout(handle); return; -#endif case isc_nm_streamdnssocket: isc__nmhandle_streamdns_cleartimeout(handle); return; @@ -1897,10 +1884,10 @@ isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { case isc_nm_httpsocket: isc__nm_http_settimeout(handle, timeout); return; +#endif case isc_nm_tlssocket: isc__nm_tls_settimeout(handle, timeout); return; -#endif case isc_nm_streamdnssocket: isc__nmhandle_streamdns_settimeout(handle, timeout); return; @@ -1934,10 +1921,10 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) { case isc_nm_streamdnssocket: isc__nmhandle_streamdns_keepalive(handle, value); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmhandle_tls_keepalive(handle, value); break; +#if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: isc__nmhandle_http_keepalive(handle, value); break; @@ -2070,10 +2057,10 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_streamdnssocket: isc__nm_streamdns_send(handle, region, cb, cbarg); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_send(handle, region, cb, cbarg); break; +#if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: isc__nm_http_send(handle, region, cb, cbarg); break; @@ -2103,10 +2090,10 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { case isc_nm_streamdnssocket: isc__nm_streamdns_read(handle, cb, cbarg); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_read(handle, cb, cbarg); break; +#if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: isc__nm_http_read(handle, cb, cbarg); break; @@ -2148,11 +2135,9 @@ isc_nm_read_stop(isc_nmhandle_t *handle) { case isc_nm_tcpsocket: isc__nm_tcp_read_stop(handle); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nm_tls_read_stop(handle); break; -#endif default: UNREACHABLE(); } @@ -2178,10 +2163,10 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { case isc_nm_streamdnslistener: isc__nm_streamdns_stoplistening(sock); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlslistener: isc__nm_tls_stoplistening(sock); break; +#if HAVE_LIBNGHTTP2 case isc_nm_httplistener: isc__nm_http_stoplistening(sock); break; @@ -2413,11 +2398,9 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) { */ REQUIRE(sock->parent == NULL); break; -#ifdef HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmsocket_tls_reset(sock); return; -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_streamdnssocket: isc__nmsocket_streamdns_reset(sock); return; @@ -2641,9 +2624,7 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { case isc_nm_tlsdnssocket: case isc_nm_tcpsocket: case isc_nm_streamdnssocket: -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: -#endif /* HAVE_LIBNGHTTP2 */ REQUIRE(sock->parent == NULL); isc__nmsocket_reset(sock); return; @@ -2716,11 +2697,8 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { case isc_nm_streamdnssocket: return; break; - case isc_nm_tcpsocket: -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: -#endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); break; @@ -2742,9 +2720,7 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle) { switch (handle->sock->type) { case isc_nm_tlsdnssocket: -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: -#endif /* HAVE_LIBNGHTTP2 */ return (true); #if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: @@ -2771,10 +2747,10 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { case isc_nm_tlsdnssocket: return (isc__nm_tlsdns_verify_tls_peer_result_string(handle)); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: return (isc__nm_tls_verify_tls_peer_result_string(handle)); break; +#if HAVE_LIBNGHTTP2 case isc_nm_httpsocket: return (isc__nm_http_verify_tls_peer_result_string(handle)); break; @@ -2803,11 +2779,9 @@ isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0) { case isc_nm_tlsdnslistener: isc__nm_async_tlsdns_set_tlsctx(listener, tlsctx, tid); break; -#if HAVE_LIBNGHTTP2 case isc_nm_tlslistener: isc__nm_async_tls_set_tlsctx(listener, tlsctx, tid); break; -#endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); break; @@ -2845,10 +2819,10 @@ isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { */ isc__nm_http_set_tlsctx(listener, tlsctx); break; +#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_tlslistener: set_tlsctx_workers(listener, tlsctx); break; -#endif /* HAVE_LIBNGHTTP2 */ case isc_nm_tlsdnslistener: set_tlsctx_workers(listener, tlsctx); break; @@ -2980,11 +2954,9 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) { case isc_nm_tcpsocket: isc__nmhandle_tcp_set_manual_timer(handle, manual); return; -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmhandle_tls_set_manual_timer(handle, manual); return; -#endif /* HAVE_LIBNGHTTP2 */ default: break; }; @@ -3003,11 +2975,9 @@ isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, REQUIRE(VALID_NMSOCK(sock)); switch (sock->type) { -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: isc__nmhandle_tls_get_selected_alpn(handle, alpn, alpnlen); return; -#endif /* HAVE_LIBNGHTTP2 */ default: break; }; From 4860b66fba429aa9bac68ea9dffeb48a3df64e9e Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 23 Aug 2022 22:15:54 +0300 Subject: [PATCH 22/33] Make isc_nm_tlssocket unit tests available in non DoH-enabled builds This commit ensures that generic TLS unit tests are available in non DoH-enabled builds, as isc_nm_tlssocket is not tied exclusively to the DoH implementation anymore. --- tests/isc/netmgr_common.c | 9 +-------- tests/isc/tls_test.c | 4 ---- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/isc/netmgr_common.c b/tests/isc/netmgr_common.c index cf3351e21e..cd4e8f28c0 100644 --- a/tests/isc/netmgr_common.c +++ b/tests/isc/netmgr_common.c @@ -560,22 +560,18 @@ tcp_connect(isc_nm_t *nm) { connect_connect_cb, NULL, T_CONNECT); } -#if HAVE_LIBNGHTTP2 static void tls_connect(isc_nm_t *nm) { isc_nm_tlsconnect(nm, &tcp_connect_addr, &tcp_listen_addr, connect_connect_cb, NULL, tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache, T_CONNECT); } -#endif stream_connect_function get_stream_connect_function(void) { -#if HAVE_LIBNGHTTP2 if (stream_use_TLS) { return (tls_connect); } -#endif return (tcp_connect); } @@ -584,7 +580,6 @@ stream_listen(isc_nm_accept_cb_t accept_cb, void *accept_cbarg, int backlog, isc_quota_t *quota, isc_nmsocket_t **sockp) { isc_result_t result = ISC_R_SUCCESS; -#if HAVE_LIBNGHTTP2 if (stream_use_TLS) { result = isc_nm_listentls(listen_nm, ISC_NM_LISTEN_ALL, &tcp_listen_addr, accept_cb, @@ -592,7 +587,6 @@ stream_listen(isc_nm_accept_cb_t accept_cb, void *accept_cbarg, int backlog, tcp_listen_tlsctx, sockp); return (result); } -#endif result = isc_nm_listentcp(listen_nm, ISC_NM_LISTEN_ALL, &tcp_listen_addr, accept_cb, accept_cbarg, backlog, quota, sockp); @@ -603,7 +597,7 @@ stream_listen(isc_nm_accept_cb_t accept_cb, void *accept_cbarg, int backlog, void stream_connect(isc_nm_cb_t cb, void *cbarg, unsigned int timeout) { isc_refcount_increment0(&active_cconnects); -#if HAVE_LIBNGHTTP2 + if (stream_use_TLS) { isc_nm_tlsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, cb, cbarg, @@ -611,7 +605,6 @@ stream_connect(isc_nm_cb_t cb, void *cbarg, unsigned int timeout) { tcp_tlsctx_client_sess_cache, timeout); return; } -#endif isc_nm_tcpconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr, cb, cbarg, timeout); } diff --git a/tests/isc/tls_test.c b/tests/isc/tls_test.c index e4d7f86ea4..200fc2adfd 100644 --- a/tests/isc/tls_test.c +++ b/tests/isc/tls_test.c @@ -38,7 +38,6 @@ #include -#if HAVE_LIBNGHTTP2 ISC_LOOP_TEST_IMPL(tls_noop) { stream_noop(arg); return; @@ -96,11 +95,9 @@ ISC_LOOP_TEST_IMPL(tls_recv_send_quota_sendback) { atomic_store(&check_listener_quota, true); stream_recv_send(arg); } -#endif ISC_TEST_LIST_START -#if HAVE_LIBNGHTTP2 /* TLS */ ISC_TEST_ENTRY_CUSTOM(tls_noop, stream_noop_setup, stream_noop_teardown) ISC_TEST_ENTRY_CUSTOM(tls_noresponse, stream_noresponse_setup, @@ -125,7 +122,6 @@ ISC_TEST_ENTRY_CUSTOM(tls_recv_send_quota, stream_recv_send_setup, stream_recv_send_teardown) ISC_TEST_ENTRY_CUSTOM(tls_recv_send_quota_sendback, stream_recv_send_setup, stream_recv_send_teardown) -#endif ISC_TEST_LIST_END From a07e798df6b79302f5556239fe8baecf5aef7a72 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Fri, 7 Oct 2022 22:23:06 +0300 Subject: [PATCH 23/33] Make dispatch unit tests use Stream DNS This commit replaces usage of TCP DNS and TLS DNS in the dispatch unit tests with Stream DNS transport. --- tests/dns/dispatch_test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/dns/dispatch_test.c b/tests/dns/dispatch_test.c index 4e3ef2fd63..649166bf25 100644 --- a/tests/dns/dispatch_test.c +++ b/tests/dns/dispatch_test.c @@ -485,9 +485,9 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_response) { uint16_t id; /* Server */ - result = isc_nm_listentcpdns(netmgr, ISC_NM_LISTEN_ONE, - &tcp_server_addr, noop_nameserver, NULL, - accept_cb, NULL, 0, NULL, &sock); + result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE, + &tcp_server_addr, noop_nameserver, NULL, + accept_cb, NULL, 0, NULL, NULL, &sock); assert_int_equal(result, ISC_R_SUCCESS); /* ensure we stop listening after the test is done */ @@ -517,9 +517,9 @@ ISC_LOOP_TEST_IMPL(dispatch_tcp_response) { uint16_t id; /* Server */ - result = isc_nm_listentcpdns(netmgr, ISC_NM_LISTEN_ONE, - &tcp_server_addr, nameserver, NULL, - accept_cb, NULL, 0, NULL, &sock); + result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE, + &tcp_server_addr, nameserver, NULL, + accept_cb, NULL, 0, NULL, NULL, &sock); assert_int_equal(result, ISC_R_SUCCESS); isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock); @@ -554,7 +554,7 @@ ISC_LOOP_TEST_IMPL(dispatch_tls_response) { uint16_t id; /* Server */ - result = isc_nm_listentlsdns( + result = isc_nm_listenstreamdns( netmgr, ISC_NM_LISTEN_ONE, &tls_server_addr, nameserver, NULL, accept_cb, NULL, 0, NULL, tls_listen_tlsctx, &sock); assert_int_equal(result, ISC_R_SUCCESS); From e5649710d3caff9b7d88c7bc368ddbf3ee267f86 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 22 Nov 2022 20:43:05 +0200 Subject: [PATCH 24/33] Remove TCP DNS transport This commit removes TCP DNS transport superseded by Stream DNS. --- lib/isc/Makefile.am | 1 - lib/isc/include/isc/types.h | 10 +- lib/isc/netmgr/netmgr-int.h | 97 +-- lib/isc/netmgr/netmgr.c | 65 -- lib/isc/netmgr/tcpdns.c | 1460 ----------------------------------- 5 files changed, 5 insertions(+), 1628 deletions(-) delete mode 100644 lib/isc/netmgr/tcpdns.c diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index ac261ccf91..ad9fc80d2c 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -117,7 +117,6 @@ libisc_la_SOURCES = \ netmgr/socket.c \ netmgr/streamdns.c \ netmgr/tcp.c \ - netmgr/tcpdns.c \ netmgr/timer.c \ netmgr/tlsdns.c \ netmgr/tlsstream.c \ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index 353a37f5c3..135f7439d8 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -106,17 +106,15 @@ typedef enum isc_nmsocket_type { isc_nm_nonesocket = 0, isc_nm_udpsocket = 1 << 1, isc_nm_tcpsocket = 1 << 2, - isc_nm_tcpdnssocket = 1 << 3, - isc_nm_tlssocket = 1 << 4, - isc_nm_tlsdnssocket = 1 << 5, - isc_nm_httpsocket = 1 << 6, - isc_nm_streamdnssocket = 1 << 7, + isc_nm_tlssocket = 1 << 3, + isc_nm_tlsdnssocket = 1 << 4, + isc_nm_httpsocket = 1 << 5, + isc_nm_streamdnssocket = 1 << 6, isc_nm_maxsocket, isc_nm_udplistener, /* Aggregate of nm_udpsocks */ isc_nm_tcplistener, isc_nm_tlslistener, - isc_nm_tcpdnslistener, isc_nm_tlsdnslistener, isc_nm_httplistener, isc_nm_streamdnslistener diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index c65bb81b53..c33d859a8d 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -257,13 +257,6 @@ typedef enum isc__netievent_type { netievent_tcpaccept, - netievent_tcpdnsaccept, - netievent_tcpdnsconnect, - netievent_tcpdnsclose, - netievent_tcpdnssend, - netievent_tcpdnsread, - netievent_tcpdnscancel, - netievent_tlsclose, netievent_tlssend, netievent_tlsconnect, @@ -299,8 +292,6 @@ typedef enum isc__netievent_type { netievent_tcplisten, netievent_tcpstop, - netievent_tcpdnslisten, - netievent_tcpdnsstop, netievent_tlsdnslisten, netievent_tlsdnsstop, @@ -1401,67 +1392,6 @@ isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0); * Callback handlers for asynchronous TLS events. */ -void -isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region, - isc_nm_cb_t cb, void *cbarg); -/*%< - * Back-end implementation of isc_nm_send() for TCPDNS handles. - */ - -void -isc__nm_tcpdns_shutdown(isc_nmsocket_t *sock); - -void -isc__nm_tcpdns_close(isc_nmsocket_t *sock); -/*%< - * Close a TCPDNS socket. - */ - -void -isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock); -/*%< - * Stop listening on 'sock'. - */ - -void -isc__nm_tcpdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout); -/*%< - * Set the read timeout and reset the timer for the TCPDNS socket - * associated with 'handle', and the TCP socket it wraps around. - */ - -void -isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0); -void -isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0); -/*%< - * Callback handlers for asynchronous TCPDNS events. - */ - -void -isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg); -/* - * Back-end implementation of isc_nm_read() for TCPDNS handles. - */ - -void -isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle); -/*%< - * Stop reading on a connected TCPDNS handle. - */ - void isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, void *cbarg); @@ -1925,15 +1855,6 @@ NETIEVENT_SOCKET_TYPE(tlsdobio); NETIEVENT_SOCKET_TYPE(udplisten); NETIEVENT_SOCKET_TYPE(udpstop); -NETIEVENT_SOCKET_TYPE(tcpdnsclose); -NETIEVENT_SOCKET_TYPE(tcpdnsread); -NETIEVENT_SOCKET_TYPE(tcpdnsstop); -NETIEVENT_SOCKET_TYPE(tcpdnslisten); -NETIEVENT_SOCKET_REQ_TYPE(tcpdnsconnect); -NETIEVENT_SOCKET_REQ_TYPE(tcpdnssend); -NETIEVENT_SOCKET_HANDLE_TYPE(tcpdnscancel); -NETIEVENT_SOCKET_QUOTA_TYPE(tcpdnsaccept); - NETIEVENT_SOCKET_TYPE(tlsdnsclose); NETIEVENT_SOCKET_TYPE(tlsdnsread); NETIEVENT_SOCKET_TYPE(tlsdnsstop); @@ -1980,15 +1901,6 @@ NETIEVENT_SOCKET_DECL(tlsdobio); NETIEVENT_SOCKET_DECL(udplisten); NETIEVENT_SOCKET_DECL(udpstop); -NETIEVENT_SOCKET_DECL(tcpdnsclose); -NETIEVENT_SOCKET_DECL(tcpdnsread); -NETIEVENT_SOCKET_DECL(tcpdnsstop); -NETIEVENT_SOCKET_DECL(tcpdnslisten); -NETIEVENT_SOCKET_REQ_DECL(tcpdnsconnect); -NETIEVENT_SOCKET_REQ_DECL(tcpdnssend); -NETIEVENT_SOCKET_HANDLE_DECL(tcpdnscancel); -NETIEVENT_SOCKET_QUOTA_DECL(tcpdnsaccept); - NETIEVENT_SOCKET_DECL(tlsdnsclose); NETIEVENT_SOCKET_DECL(tlsdnsread); NETIEVENT_SOCKET_DECL(tlsdnsstop); @@ -2031,15 +1943,10 @@ isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, void isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); -void -isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, - bool async); -void + isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); -isc_result_t -isc__nm_tcpdns_processbuffer(isc_nmsocket_t *sock); isc_result_t isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock); @@ -2055,8 +1962,6 @@ isc__nm_udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf, void isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); void -isc__nm_tcpdns_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); -void isc__nm_tlsdns_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); isc_result_t diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 1cdcfccd23..fda7625952 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -345,7 +345,6 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) { switch (handle->sock->type) { case isc_nm_tcpsocket: case isc_nm_udpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: handle->sock->write_timeout = write_timeout; break; @@ -452,15 +451,6 @@ process_netievent(void *arg) { NETIEVENT_CASE(tcplisten); NETIEVENT_CASE(tcpstop); - NETIEVENT_CASE(tcpdnsaccept); - NETIEVENT_CASE(tcpdnslisten); - NETIEVENT_CASE(tcpdnsconnect); - NETIEVENT_CASE(tcpdnssend); - NETIEVENT_CASE(tcpdnscancel); - NETIEVENT_CASE(tcpdnsclose); - NETIEVENT_CASE(tcpdnsread); - NETIEVENT_CASE(tcpdnsstop); - NETIEVENT_CASE(tlsdnscycle); NETIEVENT_CASE(tlsdnsaccept); NETIEVENT_CASE(tlsdnslisten); @@ -526,15 +516,6 @@ NETIEVENT_SOCKET_DEF(udplisten); NETIEVENT_SOCKET_DEF(udpstop); NETIEVENT_SOCKET_HANDLE_DEF(udpcancel); -NETIEVENT_SOCKET_DEF(tcpdnsclose); -NETIEVENT_SOCKET_DEF(tcpdnsread); -NETIEVENT_SOCKET_DEF(tcpdnsstop); -NETIEVENT_SOCKET_DEF(tcpdnslisten); -NETIEVENT_SOCKET_REQ_DEF(tcpdnsconnect); -NETIEVENT_SOCKET_REQ_DEF(tcpdnssend); -NETIEVENT_SOCKET_HANDLE_DEF(tcpdnscancel); -NETIEVENT_SOCKET_QUOTA_DEF(tcpdnsaccept); - NETIEVENT_SOCKET_DEF(tlsdnsclose); NETIEVENT_SOCKET_DEF(tlsdnsread); NETIEVENT_SOCKET_DEF(tlsdnsstop); @@ -849,9 +830,6 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_tcpsocket: isc__nm_tcp_close(sock); return; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_close(sock); - return; case isc_nm_tlsdnssocket: isc__nm_tlsdns_close(sock); return; @@ -908,7 +886,6 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) { REQUIRE(VALID_NMSOCK(*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_streamdnslistener || (*sockp)->type == isc_nm_tlslistener || @@ -983,8 +960,6 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker, break; case isc_nm_tcpsocket: case isc_nm_tcplistener: - case isc_nm_tcpdnssocket: - case isc_nm_tcpdnslistener: case isc_nm_tlsdnssocket: case isc_nm_tlsdnslistener: case isc_nm_httpsocket: @@ -1119,7 +1094,6 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer, switch (sock->type) { case isc_nm_udpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: if (!atomic_load(&sock->client)) { break; @@ -1169,7 +1143,6 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) { REQUIRE(VALID_NMHANDLE(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 || handle->sock->type == isc_nm_httpsocket || @@ -1409,9 +1382,6 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) { case isc_nm_tcpsocket: isc__nm_tcp_failed_read_cb(sock, result, async); return; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_failed_read_cb(sock, result, async); - return; case isc_nm_tlsdnssocket: isc__nm_tlsdns_failed_read_cb(sock, result, async); return; @@ -1673,7 +1643,6 @@ isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { buf->len = ISC_NETMGR_UDP_RECVBUF_SIZE; break; case isc_nm_tcpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: buf->len = ISC_NETMGR_TCP_RECVBUF_SIZE; break; @@ -1705,10 +1674,6 @@ isc__nm_start_reading(isc_nmsocket_t *sock) { r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, isc__nm_tcp_read_cb); break; - case isc_nm_tcpdnssocket: - r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, - isc__nm_tcpdns_read_cb); - break; case isc_nm_tlsdnssocket: r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, isc__nm_tlsdns_read_cb); @@ -1739,7 +1704,6 @@ isc__nm_stop_reading(isc_nmsocket_t *sock) { UV_RUNTIME_CHECK(uv_udp_recv_stop, r); break; case isc_nm_tcpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: r = uv_read_stop(&sock->uv_handle.stream); UV_RUNTIME_CHECK(uv_read_stop, r); @@ -1765,8 +1729,6 @@ isc__nmsocket_closing(isc_nmsocket_t *sock) { static isc_result_t processbuffer(isc_nmsocket_t *sock) { switch (sock->type) { - case isc_nm_tcpdnssocket: - return (isc__nm_tcpdns_processbuffer(sock)); case isc_nm_tlsdnssocket: return (isc__nm_tlsdns_processbuffer(sock)); default: @@ -1910,7 +1872,6 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) { switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: atomic_store(&sock->keepalive, value); sock->read_timeout = value ? atomic_load(&netmgr->keepalive) @@ -2048,9 +2009,6 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_tcpsocket: isc__nm_tcp_send(handle, region, cb, cbarg); break; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_send(handle, region, cb, cbarg); - break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_send(handle, region, cb, cbarg); break; @@ -2081,9 +2039,6 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { case isc_nm_tcpsocket: isc__nm_tcp_read(handle, cb, cbarg); break; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_read(handle, cb, cbarg); - break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_read(handle, cb, cbarg); break; @@ -2111,9 +2066,6 @@ isc_nm_cancelread(isc_nmhandle_t *handle) { case isc_nm_udpsocket: isc__nm_udp_cancelread(handle); break; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_cancelread(handle); - break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_cancelread(handle); break; @@ -2151,9 +2103,6 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { case isc_nm_udplistener: isc__nm_udp_stoplistening(sock); break; - case isc_nm_tcpdnslistener: - isc__nm_tcpdns_stoplistening(sock); - break; case isc_nm_tcplistener: isc__nm_tcp_stoplistening(sock); break; @@ -2390,7 +2339,6 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) { switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: /* * This can be called from the TCP write timeout, or @@ -2435,15 +2383,11 @@ isc__nmsocket_shutdown(isc_nmsocket_t *sock) { case isc_nm_tcpsocket: isc__nm_tcp_shutdown(sock); break; - case isc_nm_tcpdnssocket: - isc__nm_tcpdns_shutdown(sock); - break; case isc_nm_tlsdnssocket: isc__nm_tlsdns_shutdown(sock); break; case isc_nm_udplistener: case isc_nm_tcplistener: - case isc_nm_tcpdnslistener: case isc_nm_tlsdnslistener: return; default: @@ -2469,7 +2413,6 @@ shutdown_walk_cb(uv_handle_t *handle, void *arg) { case UV_TCP: switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: if (sock->parent == NULL) { /* Reset the TCP connections on shutdown */ @@ -2620,7 +2563,6 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { switch (sock->type) { case isc_nm_udpsocket: return; - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: case isc_nm_tcpsocket: case isc_nm_streamdnssocket: @@ -2649,8 +2591,6 @@ isc_nm_xfr_allowed(isc_nmhandle_t *handle) { sock = handle->sock; switch (sock->type) { - case isc_nm_tcpdnssocket: - return (true); case isc_nm_tlsdnssocket: return (isc__nm_tlsdns_xfr_allowed(sock)); case isc_nm_streamdnssocket: @@ -2692,7 +2632,6 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { break; #endif /* HAVE_LIBNGHTTP2 */ case isc_nm_udpsocket: - case isc_nm_tcpdnssocket: case isc_nm_tlsdnssocket: case isc_nm_streamdnssocket: return; @@ -3028,10 +2967,6 @@ nmsocket_type_totext(isc_nmsocket_type type) { return ("isc_nm_tcpsocket"); case isc_nm_tcplistener: return ("isc_nm_tcplistener"); - case isc_nm_tcpdnslistener: - return ("isc_nm_tcpdnslistener"); - case isc_nm_tcpdnssocket: - return ("isc_nm_tcpdnssocket"); case isc_nm_tlssocket: return ("isc_nm_tlssocket"); case isc_nm_tlslistener: diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c deleted file mode 100644 index 9024d9422c..0000000000 --- a/lib/isc/netmgr/tcpdns.c +++ /dev/null @@ -1,1460 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * 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 "netmgr-int.h" - -static atomic_uint_fast32_t last_tcpdnsquota_log = 0; - -static bool -can_log_tcpdns_quota(void) { - isc_stdtime_t now, last; - - isc_stdtime_get(&now); - last = atomic_exchange_relaxed(&last_tcpdnsquota_log, now); - if (now != last) { - return (true); - } - - return (false); -} - -static isc_result_t -tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req); - -static void -tcpdns_close_direct(isc_nmsocket_t *sock); - -static void -tcpdns_connect_cb(uv_connect_t *uvreq, int status); - -static void -tcpdns_connection_cb(uv_stream_t *server, int status); - -static void -tcpdns_stop_cb(uv_handle_t *handle); - -static void -tcpdns_close_cb(uv_handle_t *uvhandle); - -static isc_result_t -accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota); - -static void -quota_accept_cb(isc_quota_t *quota, void *sock0); - -static isc_result_t -tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { - isc__networker_t *worker = NULL; - int r; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(VALID_UVREQ(req)); - - REQUIRE(sock->tid == isc_tid()); - - worker = sock->worker; - - atomic_store(&sock->connecting, true); - - r = uv_tcp_init(&worker->loop->loop, &sock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&sock->uv_handle.handle, sock); - - r = uv_timer_init(&worker->loop->loop, &sock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - - if (isc__nm_closing(worker)) { - return (ISC_R_SHUTTINGDOWN); - } - - r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); - if (r != 0) { - isc__nm_closesocket(sock->fd); - isc__nm_incstats(sock, STATID_OPENFAIL); - return (isc_uverr2result(r)); - } - isc__nm_incstats(sock, STATID_OPEN); - - if (req->local.length != 0) { - r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0); - /* - * In case of shared socket UV_EINVAL will be returned and needs - * to be ignored - */ - if (r != 0 && r != UV_EINVAL) { - isc__nm_incstats(sock, STATID_BINDFAIL); - return (isc_uverr2result(r)); - } - } - - isc__nm_set_network_buffers(sock->worker->netmgr, - &sock->uv_handle.handle); - - uv_handle_set_data(&req->uv_req.handle, req); - r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp, - &req->peer.type.sa, tcpdns_connect_cb); - if (r != 0) { - isc__nm_incstats(sock, STATID_CONNECTFAIL); - return (isc_uverr2result(r)); - } - - uv_handle_set_data((uv_handle_t *)&sock->read_timer, - &req->uv_req.connect); - isc__nmsocket_timer_start(sock); - - atomic_store(&sock->connected, true); - - return (ISC_R_SUCCESS); -} - -void -isc__nm_async_tcpdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnsconnect_t *ievent = - (isc__netievent_tcpdnsconnect_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - isc__nm_uvreq_t *req = ievent->req; - isc_result_t result = ISC_R_SUCCESS; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - REQUIRE(sock->parent == NULL); - REQUIRE(sock->tid == isc_tid()); - - result = tcpdns_connect_direct(sock, req); - if (result != ISC_R_SUCCESS) { - isc__nmsocket_clearcb(sock); - isc__nm_connectcb(sock, req, result, true); - atomic_store(&sock->active, false); - isc__nm_tcpdns_close(sock); - } - - /* - * The sock is now attached to the handle. - */ - isc__nmsocket_detach(&sock); -} - -static void -tcpdns_connect_cb(uv_connect_t *uvreq, int status) { - isc_result_t result = ISC_R_UNSET; - isc__nm_uvreq_t *req = NULL; - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle); - struct sockaddr_storage ss; - isc__networker_t *worker = NULL; - int r; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - worker = sock->worker; - - req = uv_handle_get_data((uv_handle_t *)uvreq); - - REQUIRE(VALID_UVREQ(req)); - REQUIRE(VALID_NMHANDLE(req->handle)); - - if (atomic_load(&sock->timedout)) { - result = ISC_R_TIMEDOUT; - goto error; - } else if (isc__nm_closing(worker)) { - /* Network manager shutting down */ - result = ISC_R_SHUTTINGDOWN; - goto error; - } else if (isc__nmsocket_closing(sock)) { - /* Connection canceled */ - result = ISC_R_CANCELED; - goto error; - } else if (status == UV_ETIMEDOUT) { - /* Timeout status code here indicates hard error */ - result = ISC_R_TIMEDOUT; - goto error; - } else if (status == UV_EADDRINUSE) { - /* - * On FreeBSD the TCP connect() call sometimes results in a - * spurious transient EADDRINUSE. Try a few more times before - * giving up. - */ - if (--req->connect_tries > 0) { - r = uv_tcp_connect( - &req->uv_req.connect, &sock->uv_handle.tcp, - &req->peer.type.sa, tcpdns_connect_cb); - if (r != 0) { - result = isc_uverr2result(r); - goto error; - } - return; - } - result = isc_uverr2result(status); - goto error; - } else if (status != 0) { - result = isc_uverr2result(status); - goto error; - } - - isc__nmsocket_timer_stop(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - - isc__nm_incstats(sock, STATID_CONNECT); - r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss, - &(int){ sizeof(ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto error; - } - - atomic_store(&sock->connecting, false); - - result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - isc__nm_connectcb(sock, req, ISC_R_SUCCESS, false); - - return; -error: - isc__nm_failed_connect_cb(sock, req, result, false); -} - -void -isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, - isc_nm_cb_t cb, void *cbarg, unsigned int timeout) { - isc_result_t result = ISC_R_SUCCESS; - isc_nmsocket_t *sock = NULL; - isc__netievent_tcpdnsconnect_t *ievent = NULL; - isc__nm_uvreq_t *req = NULL; - sa_family_t sa_family; - isc__networker_t *worker = &mgr->workers[isc_tid()]; - - REQUIRE(VALID_NM(mgr)); - REQUIRE(local != NULL); - REQUIRE(peer != NULL); - - sa_family = peer->type.sa.sa_family; - - sock = isc_mem_get(worker->mctx, sizeof(*sock)); - isc__nmsocket_init(sock, worker, isc_nm_tcpdnssocket, local); - - sock->connect_timeout = timeout; - atomic_init(&sock->client, true); - - req = isc__nm_uvreq_get(worker, sock); - req->cb.connect = cb; - req->cbarg = cbarg; - req->peer = *peer; - req->local = *local; - req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface); - - result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd); - if (result != ISC_R_SUCCESS) { - isc__nmsocket_clearcb(sock); - isc__nm_connectcb(sock, req, result, true); - atomic_store(&sock->closed, true); - isc__nmsocket_detach(&sock); - return; - } - - (void)isc__nm_socket_min_mtu(sock->fd, sa_family); - (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG); - - /* 2 minute timeout */ - result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - ievent = isc__nm_get_netievent_tcpdnsconnect(sock->worker, sock, req); - - atomic_store(&sock->active, true); - isc__nm_async_tcpdnsconnect(sock->worker, (isc__netievent_t *)ievent); - isc__nm_put_netievent_tcpdnsconnect(sock->worker, ievent); - - atomic_store(&sock->active, true); -} - -static uv_os_sock_t -isc__nm_tcpdns_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) { - isc_result_t result; - uv_os_sock_t sock = -1; - - result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - (void)isc__nm_socket_incoming_cpu(sock); - (void)isc__nm_socket_v6only(sock, sa_family); - - /* FIXME: set mss */ - - result = isc__nm_socket_reuse(sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - if (mgr->load_balance_sockets) { - result = isc__nm_socket_reuse_lb(sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - } - - return (sock); -} - -static void -start_tcpdns_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock, - uv_os_sock_t fd, int tid) { - isc__netievent_tcpdnslisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[tid]; - isc__networker_t *worker = &mgr->workers[tid]; - - isc__nmsocket_init(csock, worker, isc_nm_tcpdnssocket, iface); - csock->parent = sock; - csock->accept_cb = sock->accept_cb; - csock->accept_cbarg = sock->accept_cbarg; - csock->recv_cb = sock->recv_cb; - csock->recv_cbarg = sock->recv_cbarg; - csock->backlog = sock->backlog; - /* - * We don't attach to quota, just assign - to avoid - * increasing quota unnecessarily. - */ - csock->pquota = sock->pquota; - isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); - - if (mgr->load_balance_sockets) { - REQUIRE(fd == -1); - csock->fd = isc__nm_tcpdns_lb_socket(mgr, - iface->type.sa.sa_family); - } else { - csock->fd = dup(fd); - } - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_tcpdnslisten(csock->worker, csock); - - if (tid == 0) { - isc__nm_process_ievent(csock->worker, - (isc__netievent_t *)ievent); - } else { - isc__nm_enqueue_ievent(csock->worker, - (isc__netievent_t *)ievent); - } -} - -isc_result_t -isc_nm_listentcpdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, - isc_nm_recv_cb_t recv_cb, void *recv_cbarg, - isc_nm_accept_cb_t accept_cb, void *accept_cbarg, - int backlog, isc_quota_t *quota, isc_nmsocket_t **sockp) { - isc_nmsocket_t *sock = NULL; - size_t children_size = 0; - uv_os_sock_t fd = -1; - isc_result_t result = ISC_R_UNSET; - isc__networker_t *worker = &mgr->workers[0]; - - REQUIRE(VALID_NM(mgr)); - REQUIRE(isc_tid() == 0); - - if (workers == 0) { - workers = mgr->nloops; - } - REQUIRE(workers <= mgr->nloops); - - sock = isc_mem_get(worker->mctx, sizeof(*sock)); - isc__nmsocket_init(sock, worker, isc_nm_tcpdnslistener, iface); - - atomic_init(&sock->rchildren, 0); - sock->nchildren = (workers == ISC_NM_LISTEN_ALL) ? (uint32_t)mgr->nloops - : workers; - children_size = sock->nchildren * sizeof(sock->children[0]); - sock->children = isc_mem_getx(worker->mctx, children_size, - ISC_MEM_ZERO); - - isc__nmsocket_barrier_init(sock); - - sock->accept_cb = accept_cb; - sock->accept_cbarg = accept_cbarg; - sock->recv_cb = recv_cb; - sock->recv_cbarg = recv_cbarg; - sock->backlog = backlog; - sock->pquota = quota; - - if (!mgr->load_balance_sockets) { - fd = isc__nm_tcpdns_lb_socket(mgr, iface->type.sa.sa_family); - } - - for (size_t i = 1; i < sock->nchildren; i++) { - start_tcpdns_child(mgr, iface, sock, fd, i); - } - - start_tcpdns_child(mgr, iface, sock, fd, 0); - - if (!mgr->load_balance_sockets) { - isc__nm_closesocket(fd); - } - - LOCK(&sock->lock); - result = sock->result; - UNLOCK(&sock->lock); - INSIST(result != ISC_R_UNSET); - - atomic_store(&sock->active, true); - - if (result != ISC_R_SUCCESS) { - atomic_store(&sock->active, false); - isc__nm_tcpdns_stoplistening(sock); - isc_nmsocket_close(&sock); - - return (result); - } - - REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); - *sockp = sock; - return (ISC_R_SUCCESS); -} - -void -isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnslisten_t *ievent = - (isc__netievent_tcpdnslisten_t *)ev0; - sa_family_t sa_family; - int r; - int flags = 0; - isc_nmsocket_t *sock = NULL; - isc_result_t result = ISC_R_UNSET; - isc_nm_t *mgr = NULL; - - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->tid == isc_tid()); - REQUIRE(VALID_NMSOCK(ievent->sock->parent)); - - sock = ievent->sock; - sa_family = sock->iface.type.sa.sa_family; - mgr = sock->worker->netmgr; - - REQUIRE(sock->type == isc_nm_tcpdnssocket); - REQUIRE(sock->parent != NULL); - REQUIRE(sock->tid == isc_tid()); - - (void)isc__nm_socket_min_mtu(sock->fd, sa_family); - (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG); - - r = uv_tcp_init(&worker->loop->loop, &sock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&sock->uv_handle.handle, sock); - /* This keeps the socket alive after everything else is gone */ - isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL }); - - r = uv_timer_init(&worker->loop->loop, &sock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - - r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); - if (r < 0) { - isc__nm_closesocket(sock->fd); - isc__nm_incstats(sock, STATID_OPENFAIL); - goto done; - } - isc__nm_incstats(sock, STATID_OPEN); - - if (sa_family == AF_INET6) { - flags = UV_TCP_IPV6ONLY; - } - - if (mgr->load_balance_sockets) { - r = isc__nm_tcp_freebind(&sock->uv_handle.tcp, - &sock->iface.type.sa, flags); - if (r < 0) { - isc__nm_incstats(sock, STATID_BINDFAIL); - goto done; - } - } else { - LOCK(&sock->parent->lock); - if (sock->parent->fd == -1) { - r = isc__nm_tcp_freebind(&sock->uv_handle.tcp, - &sock->iface.type.sa, flags); - if (r < 0) { - isc__nm_incstats(sock, STATID_BINDFAIL); - UNLOCK(&sock->parent->lock); - goto done; - } - sock->parent->uv_handle.tcp.flags = - sock->uv_handle.tcp.flags; - sock->parent->fd = sock->fd; - } else { - /* The socket is already bound, just copy the flags */ - sock->uv_handle.tcp.flags = - sock->parent->uv_handle.tcp.flags; - } - UNLOCK(&sock->parent->lock); - } - - isc__nm_set_network_buffers(sock->worker->netmgr, - &sock->uv_handle.handle); - - /* - * The callback will run in the same thread uv_listen() was called - * from, so a race with tcpdns_connection_cb() isn't possible. - */ - r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog, - tcpdns_connection_cb); - if (r != 0) { - isc__nmsocket_log(sock, ISC_LOG_ERROR, "uv_listen failed: %s", - isc_result_totext(isc_uverr2result(r))); - isc__nm_incstats(sock, STATID_BINDFAIL); - goto done; - } - - atomic_store(&sock->listening, true); - -done: - result = isc_uverr2result(r); - atomic_fetch_add(&sock->parent->rchildren, 1); - - if (result != ISC_R_SUCCESS) { - sock->pquota = NULL; - } - - LOCK(&sock->parent->lock); - if (sock->parent->result == ISC_R_UNSET) { - sock->parent->result = result; - } else { - REQUIRE(sock->parent->result == result); - } - UNLOCK(&sock->parent->lock); - - REQUIRE(!worker->loop->paused); - isc_barrier_wait(&sock->parent->barrier); -} - -static void -tcpdns_connection_cb(uv_stream_t *server, int status) { - isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server); - isc_result_t result; - isc_quota_t *quota = NULL; - - if (status != 0) { - result = isc_uverr2result(status); - goto done; - } - - REQUIRE(VALID_NMSOCK(ssock)); - REQUIRE(ssock->tid == isc_tid()); - - if (isc__nmsocket_closing(ssock)) { - result = ISC_R_CANCELED; - goto done; - } - - if (ssock->pquota != NULL) { - result = isc_quota_attach_cb(ssock->pquota, "a, - &ssock->quotacb); - if (result == ISC_R_QUOTA) { - isc__nm_incstats(ssock, STATID_ACCEPTFAIL); - goto done; - } - } - - result = accept_connection(ssock, quota); -done: - isc__nm_accept_connection_log(ssock, result, can_log_tcpdns_quota()); -} - -static void -stop_tcpdns_child(isc_nmsocket_t *sock, uint32_t tid) { - isc_nmsocket_t *csock = NULL; - isc__netievent_tcpstop_t *ievent = NULL; - - csock = &sock->children[tid]; - REQUIRE(VALID_NMSOCK(csock)); - - atomic_store(&csock->active, false); - ievent = isc__nm_get_netievent_tcpdnsstop(csock->worker, csock); - - if (tid == 0) { - isc__nm_process_ievent(csock->worker, - (isc__netievent_t *)ievent); - } else { - isc__nm_enqueue_ievent(csock->worker, - (isc__netievent_t *)ievent); - } -} - -static void -stop_tcpdns_parent(isc_nmsocket_t *sock) { - /* Stop the parent */ - atomic_store(&sock->closed, true); - isc__nmsocket_prep_destroy(sock); -} - -void -isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tcpdnslistener); - - RUNTIME_CHECK(atomic_compare_exchange_strong(&sock->closing, - &(bool){ false }, true)); - - for (size_t i = 1; i < sock->nchildren; i++) { - stop_tcpdns_child(sock, i); - } - - stop_tcpdns_child(sock, 0); - - stop_tcpdns_parent(sock); -} - -void -isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnsstop_t *ievent = - (isc__netievent_tcpdnsstop_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->parent != NULL); - - RUNTIME_CHECK(atomic_compare_exchange_strong(&sock->closing, - &(bool){ false }, true)); - - /* - * The order of the close operation is important here, the uv_close() - * gets scheduled in the reverse order, so we need to close the timer - * last, so its gone by the time we destroy the socket - */ - - /* 2. close the listening socket */ - isc__nmsocket_clearcb(sock); - isc__nm_stop_reading(sock); - uv_close(&sock->uv_handle.handle, tcpdns_stop_cb); - - /* 1. close the read timer */ - isc__nmsocket_timer_stop(sock); - uv_close(&sock->read_timer, NULL); - - (void)atomic_fetch_sub(&sock->parent->rchildren, 1); - - REQUIRE(!worker->loop->paused); - isc_barrier_wait(&sock->parent->barrier); -} - -void -isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, - bool async) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(result != ISC_R_SUCCESS); - - isc__nmsocket_timer_stop(sock); - isc__nm_stop_reading(sock); - - if (!sock->recv_read) { - goto destroy; - } - sock->recv_read = false; - - if (sock->recv_cb != NULL) { - isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); - isc__nmsocket_clearcb(sock); - isc__nm_readcb(sock, req, result, async); - } - -destroy: - isc__nmsocket_prep_destroy(sock); - - /* - * We need to detach from quota after the read callback function had a - * chance to be executed. - */ - if (sock->quota != NULL) { - isc_quota_detach(&sock->quota); - } -} - -void -isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - isc_nmsocket_t *sock = handle->sock; - isc__netievent_tcpdnsread_t *ievent = NULL; - isc_nm_t *netmgr = sock->worker->netmgr; - - REQUIRE(sock->type == isc_nm_tcpdnssocket); - REQUIRE(sock->statichandle == handle); - - sock->recv_cb = cb; - sock->recv_cbarg = cbarg; - sock->recv_read = true; - if (sock->read_timeout == 0) { - sock->read_timeout = (atomic_load(&sock->keepalive) - ? atomic_load(&netmgr->keepalive) - : atomic_load(&netmgr->idle)); - } - - ievent = isc__nm_get_netievent_tcpdnsread(sock->worker, sock); - - /* - * FIXME: This MUST be done asynchronously, ~~no matter which thread - * we're in.~~ ,only when there's existing data on the socket. - - * The callback function for isc_nm_read() often calls - * isc_nm_read() again; if we tried to do that synchronously - * we'd clash in processbuffer() and grow the stack indefinitely. - */ - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); - - return; -} - -void -isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnsread_t *ievent = - (isc__netievent_tcpdnsread_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - isc_result_t result; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - if (isc__nmsocket_closing(sock)) { - result = ISC_R_CANCELED; - } else { - result = isc__nm_process_sock_buffer(sock); - } - - if (result != ISC_R_SUCCESS) { - sock->reading = true; - isc__nm_failed_read_cb(sock, result, false); - } -} - -/* - * Process a single packet from the incoming buffer. - * - * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something - * was processed; return ISC_R_NOMORE if there isn't a full message - * to be processed. - * - * The caller will need to unreference the handle. - */ -isc_result_t -isc__nm_tcpdns_processbuffer(isc_nmsocket_t *sock) { - size_t len; - isc__nm_uvreq_t *req = NULL; - isc_nmhandle_t *handle = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - if (isc__nmsocket_closing(sock)) { - return (ISC_R_CANCELED); - } - - /* - * If we don't even have the length yet, we can't do - * anything. - */ - if (sock->buf_len < 2) { - return (ISC_R_NOMORE); - } - - /* - * Process the first packet from the buffer, leaving - * the rest (if any) for later. - */ - len = ntohs(*(uint16_t *)sock->buf); - if (len > sock->buf_len - 2) { - return (ISC_R_NOMORE); - } - - if (sock->recv_cb == NULL) { - /* - * recv_cb has been cleared - there is - * nothing to do - */ - return (ISC_R_CANCELED); - } else if (sock->statichandle == NULL && - atomic_load(&sock->connected) && - !atomic_load(&sock->connecting)) - { - /* - * It seems that some unexpected data (a DNS message) has - * arrived while we are wrapping up. - */ - return (ISC_R_CANCELED); - } - - req = isc__nm_get_read_req(sock, NULL); - REQUIRE(VALID_UVREQ(req)); - - /* - * We need to launch isc__nm_resume_processing() after the buffer - * has been consumed, thus we must delay detaching the handle. - */ - isc_nmhandle_attach(req->handle, &handle); - - /* - * The callback will be called synchronously because the - * result is ISC_R_SUCCESS, so we don't need to have - * the buffer on the heap - */ - req->uvbuf.base = (char *)sock->buf + 2; - req->uvbuf.len = len; - - /* - * If isc__nm_tcpdns_read() was called, it will be satisfied by single - * DNS message in the next call. - */ - sock->recv_read = false; - - /* - * An assertion failure here means that there's an erroneous - * extra nmhandle detach happening in the callback and - * isc__nm_resume_processing() is called while we're - * processing the buffer. - */ - REQUIRE(sock->processing == false); - sock->processing = true; - isc__nm_readcb(sock, req, ISC_R_SUCCESS, false); - sock->processing = false; - - len += 2; - sock->buf_len -= len; - if (sock->buf_len > 0) { - memmove(sock->buf, sock->buf + len, sock->buf_len); - } - - isc_nmhandle_detach(&handle); - - return (ISC_R_SUCCESS); -} - -void -isc__nm_tcpdns_read_cb(uv_stream_t *stream, ssize_t nread, - const uv_buf_t *buf) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream); - uint8_t *base = NULL; - size_t len; - isc_result_t result; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->reading); - REQUIRE(buf != NULL); - - if (isc__nmsocket_closing(sock)) { - isc__nm_failed_read_cb(sock, ISC_R_CANCELED, true); - goto free; - } - - if (nread < 0) { - if (nread != UV_EOF) { - isc__nm_incstats(sock, STATID_RECVFAIL); - } - - isc__nm_failed_read_cb(sock, isc_uverr2result(nread), true); - goto free; - } - - base = (uint8_t *)buf->base; - len = nread; - - /* - * FIXME: We can avoid the memmove here if we know we have received full - * packet; e.g. we should be smarter, a.s. there are just few situations - * - * The tcp_alloc_buf should be smarter and point the uv_read_start to - * the position where previous read has ended in the sock->buf, that way - * the data could be read directly into sock->buf. - */ - - if (sock->buf_len + len > sock->buf_size) { - isc__nm_alloc_dnsbuf(sock, sock->buf_len + len); - } - memmove(sock->buf + sock->buf_len, base, len); - sock->buf_len += len; - - if (!atomic_load(&sock->client)) { - sock->read_timeout = atomic_load(&sock->worker->netmgr->idle); - } - - result = isc__nm_process_sock_buffer(sock); - if (result != ISC_R_SUCCESS) { - isc__nm_failed_read_cb(sock, result, true); - } -free: - if (nread < 0) { - /* - * The buffer may be a null buffer on error. - */ - if (buf->base == NULL && buf->len == 0) { - return; - } - } - - isc__nm_free_uvbuf(sock, buf); -} - -static void -quota_accept_cb(isc_quota_t *quota, void *sock0) { - isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0; - - REQUIRE(VALID_NMSOCK(sock)); - - /* - * Create a tcpdnsaccept event and pass it using the async channel. - */ - - isc__netievent_tcpdnsaccept_t *ievent = - isc__nm_get_netievent_tcpdnsaccept(sock->worker, sock, quota); - isc__nm_maybe_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -/* - * This is called after we get a quota_accept_cb() callback. - */ -void -isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnsaccept_t *ievent = - (isc__netievent_tcpdnsaccept_t *)ev0; - isc_result_t result; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->tid == isc_tid()); - - result = accept_connection(ievent->sock, ievent->quota); - isc__nm_accept_connection_log(ievent->sock, result, - can_log_tcpdns_quota()); -} - -static isc_result_t -accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { - isc_nmsocket_t *csock = NULL; - isc__networker_t *worker = NULL; - int r; - isc_result_t result; - struct sockaddr_storage peer_ss; - struct sockaddr_storage local_ss; - isc_sockaddr_t local; - isc_nmhandle_t *handle = NULL; - - REQUIRE(VALID_NMSOCK(ssock)); - REQUIRE(ssock->tid == isc_tid()); - - if (isc__nmsocket_closing(ssock)) { - if (quota != NULL) { - isc_quota_detach("a); - } - return (ISC_R_CANCELED); - } - - REQUIRE(ssock->accept_cb != NULL); - - csock = isc_mem_get(ssock->worker->mctx, sizeof(isc_nmsocket_t)); - isc__nmsocket_init(csock, ssock->worker, isc_nm_tcpdnssocket, - &ssock->iface); - isc__nmsocket_attach(ssock, &csock->server); - csock->recv_cb = ssock->recv_cb; - csock->recv_cbarg = ssock->recv_cbarg; - csock->quota = quota; - atomic_init(&csock->accepting, true); - - worker = csock->worker; - - r = uv_tcp_init(&worker->loop->loop, &csock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&csock->uv_handle.handle, csock); - - r = uv_timer_init(&worker->loop->loop, &csock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock); - - r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - r = uv_tcp_getpeername(&csock->uv_handle.tcp, - (struct sockaddr *)&peer_ss, - &(int){ sizeof(peer_ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - result = isc_sockaddr_fromsockaddr(&csock->peer, - (struct sockaddr *)&peer_ss); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - r = uv_tcp_getsockname(&csock->uv_handle.tcp, - (struct sockaddr *)&local_ss, - &(int){ sizeof(local_ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - result = isc_sockaddr_fromsockaddr(&local, - (struct sockaddr *)&local_ss); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - /* - * The handle will be either detached on acceptcb failure or in the - * readcb. - */ - handle = isc__nmhandle_get(csock, NULL, &local); - - result = ssock->accept_cb(handle, ISC_R_SUCCESS, ssock->accept_cbarg); - if (result != ISC_R_SUCCESS) { - isc_nmhandle_detach(&handle); - goto failure; - } - - atomic_store(&csock->accepting, false); - - isc__nm_incstats(csock, STATID_ACCEPT); - - csock->read_timeout = atomic_load(&csock->worker->netmgr->init); - - csock->closehandle_cb = isc__nm_resume_processing; - - /* - * We need to keep the handle alive until we fail to read or connection - * is closed by the other side, it will be detached via - * prep_destroy()->tcpdns_close_direct(). - */ - isc_nmhandle_attach(handle, &csock->recv_handle); - result = isc__nm_process_sock_buffer(csock); - if (result != ISC_R_SUCCESS) { - isc_nmhandle_detach(&csock->recv_handle); - isc_nmhandle_detach(&handle); - goto failure; - } - - /* - * The initial timer has been set, update the read timeout for the next - * reads. - */ - csock->read_timeout = - (atomic_load(&csock->keepalive) - ? atomic_load(&csock->worker->netmgr->keepalive) - : atomic_load(&csock->worker->netmgr->idle)); - - isc_nmhandle_detach(&handle); - - /* - * sock is now attached to the handle. - */ - isc__nmsocket_detach(&csock); - - return (ISC_R_SUCCESS); - -failure: - - atomic_store(&csock->active, false); - - isc__nm_failed_accept_cb(csock, result); - - isc__nmsocket_prep_destroy(csock); - - isc__nmsocket_detach(&csock); - - return (result); -} - -void -isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region, - isc_nm_cb_t cb, void *cbarg) { - isc__netievent_tcpdnssend_t *ievent = NULL; - isc__nm_uvreq_t *uvreq = NULL; - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - - sock = handle->sock; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - - uvreq = isc__nm_uvreq_get(sock->worker, sock); - *(uint16_t *)uvreq->tcplen = htons(region->length); - uvreq->uvbuf.base = (char *)region->base; - uvreq->uvbuf.len = region->length; - - isc_nmhandle_attach(handle, &uvreq->handle); - - uvreq->cb.send = cb; - uvreq->cbarg = cbarg; - - ievent = isc__nm_get_netievent_tcpdnssend(sock->worker, sock, uvreq); - isc__nm_maybe_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); - - return; -} - -static void -tcpdns_send_cb(uv_write_t *req, int status) { - isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_UVREQ(uvreq)); - REQUIRE(VALID_NMSOCK(uvreq->sock)); - - sock = uvreq->sock; - - isc_nm_timer_stop(uvreq->timer); - isc_nm_timer_detach(&uvreq->timer); - - if (status < 0) { - isc__nm_incstats(sock, STATID_SENDFAIL); - isc__nm_failed_send_cb(sock, uvreq, isc_uverr2result(status), - false); - return; - } - - isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false); -} - -/* - * Handle 'tcpsend' async event - send a packet on the socket - */ -void -isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { - isc_result_t result; - isc__netievent_tcpdnssend_t *ievent = - (isc__netievent_tcpdnssend_t *)ev0; - isc_nmsocket_t *sock = NULL; - isc__nm_uvreq_t *uvreq = NULL; - int r, nbufs = 2; - - UNUSED(worker); - - REQUIRE(VALID_UVREQ(ievent->req)); - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->type == isc_nm_tcpdnssocket); - REQUIRE(ievent->sock->tid == isc_tid()); - - sock = ievent->sock; - uvreq = ievent->req; - - if (sock->write_timeout == 0) { - sock->write_timeout = - (atomic_load(&sock->keepalive) - ? atomic_load(&sock->worker->netmgr->keepalive) - : atomic_load(&sock->worker->netmgr->idle)); - } - - uv_buf_t bufs[2] = { { .base = uvreq->tcplen, .len = 2 }, - { .base = uvreq->uvbuf.base, - .len = uvreq->uvbuf.len } }; - - if (isc__nmsocket_closing(sock)) { - result = ISC_R_CANCELED; - goto fail; - } - - r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); - - if (r == (int)(bufs[0].len + bufs[1].len)) { - /* Wrote everything */ - isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, true); - return; - } - - if (r == 1) { - /* Partial write of DNSMSG length */ - bufs[0].base = uvreq->tcplen + 1; - bufs[0].len = 1; - } else if (r > 0) { - /* Partial write of DNSMSG */ - nbufs = 1; - bufs[0].base = uvreq->uvbuf.base + (r - 2); - bufs[0].len = uvreq->uvbuf.len - (r - 2); - } else if (r == UV_ENOSYS || r == UV_EAGAIN) { - /* uv_try_write not supported, send asynchronously */ - } else { - /* error sending data */ - result = isc_uverr2result(r); - goto fail; - } - - r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, - tcpdns_send_cb); - if (r < 0) { - result = isc_uverr2result(r); - goto fail; - } - - isc_nm_timer_create(uvreq->handle, isc__nmsocket_writetimeout_cb, uvreq, - &uvreq->timer); - if (sock->write_timeout > 0) { - isc_nm_timer_start(uvreq->timer, sock->write_timeout); - } - - return; -fail: - isc__nm_incstats(sock, STATID_SENDFAIL); - isc__nm_failed_send_cb(sock, uvreq, result, true); -} - -static void -tcpdns_stop_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - - uv_handle_set_data(handle, NULL); - - if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false }, - true)) - { - UNREACHABLE(); - } - - isc__nm_incstats(sock, STATID_CLOSE); - - atomic_store(&sock->listening, false); - - isc__nmsocket_detach(&sock); -} - -static void -tcpdns_close_sock(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - - if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false }, - true)) - { - UNREACHABLE(); - } - - isc__nm_incstats(sock, STATID_CLOSE); - - if (sock->server != NULL) { - isc__nmsocket_detach(&sock->server); - } - - atomic_store(&sock->connected, false); - - isc__nmsocket_prep_destroy(sock); -} - -static void -tcpdns_close_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - - uv_handle_set_data(handle, NULL); - - tcpdns_close_sock(sock); -} - -static void -tcpdns_close_direct(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - - if (sock->quota != NULL) { - isc_quota_detach(&sock->quota); - } - - if (sock->recv_handle != NULL) { - isc_nmhandle_detach(&sock->recv_handle); - } - - /* - * The order of the close operation is important here, the uv_close() - * gets scheduled in the reverse order, so we need to close the timer - * last, so its gone by the time we destroy the socket - */ - - if (!uv_is_closing(&sock->uv_handle.handle)) { - /* Normal order of operation */ - - /* 2. close the socket + destroy the socket in callback */ - isc__nmsocket_clearcb(sock); - isc__nm_stop_reading(sock); - uv_close(&sock->uv_handle.handle, tcpdns_close_cb); - - /* 1. close the timer */ - uv_close((uv_handle_t *)&sock->read_timer, NULL); - } else { - /* The socket was already closed elsewhere */ - - /* 1. close the timer + destroy the socket in callback */ - isc__nmsocket_timer_stop(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - uv_close((uv_handle_t *)&sock->read_timer, tcpdns_close_cb); - } -} - -void -isc__nm_tcpdns_close(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - REQUIRE(!isc__nmsocket_active(sock)); - - if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, - true)) - { - return; - } - - if (sock->tid == isc_tid()) { - tcpdns_close_direct(sock); - } else { - /* - * We need to create an event and pass it using async channel - */ - isc__netievent_tcpdnsclose_t *ievent = - isc__nm_get_netievent_tcpdnsclose(sock->worker, sock); - - isc__nm_enqueue_ievent(sock->worker, - (isc__netievent_t *)ievent); - } -} - -void -isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnsclose_t *ievent = - (isc__netievent_tcpdnsclose_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - tcpdns_close_direct(sock); -} - -static void -tcpdns_close_connect_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - - REQUIRE(VALID_NMSOCK(sock)); - - REQUIRE(sock->tid == isc_tid()); - - isc__nmsocket_prep_destroy(sock); - isc__nmsocket_detach(&sock); -} - -void -isc__nm_tcpdns_shutdown(isc_nmsocket_t *sock) { - isc__networker_t *worker = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - - worker = sock->worker; - - /* - * If the socket is active, mark it inactive and - * continue. If it isn't active, stop now. - */ - if (!isc__nmsocket_deactivate(sock)) { - return; - } - - if (atomic_load(&sock->accepting)) { - return; - } - - if (atomic_load(&sock->connecting)) { - isc_nmsocket_t *tsock = NULL; - isc__nmsocket_attach(sock, &tsock); - uv_close(&sock->uv_handle.handle, tcpdns_close_connect_cb); - return; - } - - if (sock->statichandle != NULL) { - if (isc__nm_closing(worker)) { - isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false); - } else { - isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false); - } - return; - } - - /* - * Otherwise, we just send the socket to abyss... - */ - if (sock->parent == NULL) { - isc__nmsocket_prep_destroy(sock); - } -} - -void -isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle) { - isc_nmsocket_t *sock = NULL; - isc__netievent_tcpdnscancel_t *ievent = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - - sock = handle->sock; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - - ievent = isc__nm_get_netievent_tcpdnscancel(sock->worker, sock, handle); - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -void -isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tcpdnscancel_t *ievent = - (isc__netievent_tcpdnscancel_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - isc__nm_failed_read_cb(sock, ISC_R_EOF, false); -} From 4277eeeb9c56c5d1ac43af714f328ac9fec791ef Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 22 Nov 2022 21:03:02 +0200 Subject: [PATCH 25/33] Remove TLS DNS transport (and parts common with TCP DNS) This commit removes TLS DNS transport superseded by Stream DNS. --- lib/isc/Makefile.am | 1 - lib/isc/include/isc/types.h | 6 +- lib/isc/netmgr/netmgr-int.h | 59 - lib/isc/netmgr/netmgr.c | 193 +-- lib/isc/netmgr/tlsdns.c | 2247 ----------------------------------- 5 files changed, 4 insertions(+), 2502 deletions(-) delete mode 100644 lib/isc/netmgr/tlsdns.c diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index ad9fc80d2c..4df3574eb7 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -118,7 +118,6 @@ libisc_la_SOURCES = \ netmgr/streamdns.c \ netmgr/tcp.c \ netmgr/timer.c \ - netmgr/tlsdns.c \ netmgr/tlsstream.c \ netmgr/udp.c \ aes.c \ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index 135f7439d8..cdbe4bc443 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -107,15 +107,13 @@ typedef enum isc_nmsocket_type { isc_nm_udpsocket = 1 << 1, isc_nm_tcpsocket = 1 << 2, isc_nm_tlssocket = 1 << 3, - isc_nm_tlsdnssocket = 1 << 4, - isc_nm_httpsocket = 1 << 5, - isc_nm_streamdnssocket = 1 << 6, + isc_nm_httpsocket = 1 << 4, + isc_nm_streamdnssocket = 1 << 5, isc_nm_maxsocket, isc_nm_udplistener, /* Aggregate of nm_udpsocks */ isc_nm_tcplistener, isc_nm_tlslistener, - isc_nm_tlsdnslistener, isc_nm_httplistener, isc_nm_streamdnslistener } isc_nmsocket_type; diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index c33d859a8d..4fe9e1c0c5 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -262,15 +262,6 @@ typedef enum isc__netievent_type { netievent_tlsconnect, netievent_tlsdobio, - netievent_tlsdnsaccept, - netievent_tlsdnsconnect, - netievent_tlsdnsclose, - netievent_tlsdnssend, - netievent_tlsdnsread, - netievent_tlsdnscancel, - netievent_tlsdnscycle, - netievent_tlsdnsshutdown, - netievent_httpclose, netievent_httpsend, netievent_httpendpoints, @@ -292,8 +283,6 @@ typedef enum isc__netievent_type { netievent_tcplisten, netievent_tcpstop, - netievent_tlsdnslisten, - netievent_tlsdnsstop, netievent_detach, } isc__netievent_type; @@ -866,33 +855,6 @@ struct isc_nmsocket { /*% Self socket */ isc_nmsocket_t *self; - /*% TLS stuff */ - struct tls { - isc_tls_t *tls; - isc_tlsctx_t *ctx; - isc_tlsctx_client_session_cache_t *client_sess_cache; - bool client_session_saved; - BIO *app_rbio; - BIO *app_wbio; - BIO *ssl_rbio; - BIO *ssl_wbio; - enum { - TLS_STATE_NONE, - TLS_STATE_HANDSHAKE, - TLS_STATE_IO, - TLS_STATE_ERROR, - TLS_STATE_CLOSING - } state; - isc_region_t senddata; - ISC_LIST(isc__nm_uvreq_t) sendreqs; - bool cycle; - isc_result_t pending_error; - /* List of active send requests. */ - isc__nm_uvreq_t *pending_req; - bool alpn_negotiated; - const char *tls_verify_errmsg; - } tls; - /*% TLS stuff */ struct tlsstream { bool server; @@ -1046,11 +1008,6 @@ struct isc_nmsocket { */ atomic_int_fast32_t ah; - /*% Buffer for TCPDNS processing */ - size_t buf_size; - size_t buf_len; - unsigned char *buf; - /*% * This function will be called with handle->sock * as the argument whenever a handle's references drop @@ -1470,9 +1427,6 @@ isc__nm_tlsdns_xfr_allowed(isc_nmsocket_t *sock); * \li 'sock' is a valid TLSDNS socket. */ -void -isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock); - void isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, void *cbarg); @@ -1944,12 +1898,6 @@ void isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async); -isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, - bool async); - -isc_result_t -isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock); - isc__nm_uvreq_t * isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr); @@ -1968,18 +1916,11 @@ isc_result_t isc__nm_start_reading(isc_nmsocket_t *sock); void isc__nm_stop_reading(isc_nmsocket_t *sock); -isc_result_t -isc__nm_process_sock_buffer(isc_nmsocket_t *sock); -void -isc__nm_resume_processing(void *arg); bool isc__nmsocket_closing(isc_nmsocket_t *sock); bool isc__nm_closing(isc__networker_t *worker); -void -isc__nm_alloc_dnsbuf(isc_nmsocket_t *sock, size_t len); - void isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult, bool async); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index fda7625952..eb10ce67f2 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -345,7 +345,6 @@ isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) { switch (handle->sock->type) { case isc_nm_tcpsocket: case isc_nm_udpsocket: - case isc_nm_tlsdnssocket: handle->sock->write_timeout = write_timeout; break; case isc_nm_tlssocket: @@ -451,17 +450,6 @@ process_netievent(void *arg) { NETIEVENT_CASE(tcplisten); NETIEVENT_CASE(tcpstop); - NETIEVENT_CASE(tlsdnscycle); - NETIEVENT_CASE(tlsdnsaccept); - NETIEVENT_CASE(tlsdnslisten); - NETIEVENT_CASE(tlsdnsconnect); - NETIEVENT_CASE(tlsdnssend); - NETIEVENT_CASE(tlsdnscancel); - NETIEVENT_CASE(tlsdnsclose); - NETIEVENT_CASE(tlsdnsread); - NETIEVENT_CASE(tlsdnsstop); - NETIEVENT_CASE(tlsdnsshutdown); - NETIEVENT_CASE(tlssend); NETIEVENT_CASE(tlsclose); NETIEVENT_CASE(tlsdobio); @@ -516,17 +504,6 @@ NETIEVENT_SOCKET_DEF(udplisten); NETIEVENT_SOCKET_DEF(udpstop); NETIEVENT_SOCKET_HANDLE_DEF(udpcancel); -NETIEVENT_SOCKET_DEF(tlsdnsclose); -NETIEVENT_SOCKET_DEF(tlsdnsread); -NETIEVENT_SOCKET_DEF(tlsdnsstop); -NETIEVENT_SOCKET_DEF(tlsdnslisten); -NETIEVENT_SOCKET_REQ_DEF(tlsdnsconnect); -NETIEVENT_SOCKET_REQ_DEF(tlsdnssend); -NETIEVENT_SOCKET_HANDLE_DEF(tlsdnscancel); -NETIEVENT_SOCKET_QUOTA_DEF(tlsdnsaccept); -NETIEVENT_SOCKET_DEF(tlsdnscycle); -NETIEVENT_SOCKET_DEF(tlsdnsshutdown); - #ifdef HAVE_LIBNGHTTP2 NETIEVENT_SOCKET_REQ_DEF(httpsend); NETIEVENT_SOCKET_DEF(httpclose); @@ -683,10 +660,6 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { nmhandle_free(sock, handle); } - if (sock->buf != NULL) { - isc_mem_put(sock->worker->mctx, sock->buf, sock->buf_size); - } - if (sock->quota != NULL) { isc_quota_detach(&sock->quota); } @@ -701,7 +674,6 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) { isc_astack_destroy(sock->inactivereqs); - isc__nm_tlsdns_cleanup_data(sock); isc__nm_tls_cleanup_data(sock); #if HAVE_LIBNGHTTP2 isc__nm_http_cleanup_data(sock); @@ -830,9 +802,6 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) { case isc_nm_tcpsocket: isc__nm_tcp_close(sock); return; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_close(sock); - return; case isc_nm_streamdnssocket: isc__nm_streamdns_close(sock); return; @@ -886,7 +855,6 @@ isc_nmsocket_close(isc_nmsocket_t **sockp) { REQUIRE(VALID_NMSOCK(*sockp)); REQUIRE((*sockp)->type == isc_nm_udplistener || (*sockp)->type == isc_nm_tcplistener || - (*sockp)->type == isc_nm_tlsdnslistener || (*sockp)->type == isc_nm_streamdnslistener || (*sockp)->type == isc_nm_tlslistener || (*sockp)->type == isc_nm_httplistener); @@ -913,7 +881,6 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker, .result = ISC_R_UNSET, }; - ISC_LIST_INIT(sock->tls.sendreqs); isc_mutex_init(&sock->lock); if (iface != NULL) { @@ -960,8 +927,6 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker, break; case isc_nm_tcpsocket: case isc_nm_tcplistener: - case isc_nm_tlsdnssocket: - case isc_nm_tlsdnslistener: case isc_nm_httpsocket: case isc_nm_httplistener: switch (family) { @@ -1094,7 +1059,6 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer, switch (sock->type) { case isc_nm_udpsocket: - case isc_nm_tlsdnssocket: if (!atomic_load(&sock->client)) { break; } @@ -1144,7 +1108,6 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) { return (handle->sock->type == isc_nm_tcpsocket || handle->sock->type == isc_nm_tlssocket || - handle->sock->type == isc_nm_tlsdnssocket || handle->sock->type == isc_nm_httpsocket || handle->sock->type == isc_nm_streamdnssocket); } @@ -1291,23 +1254,6 @@ isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg, handle->dofree = dofree; } -void -isc__nm_alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) { - REQUIRE(len <= NM_BIG_BUF); - - if (sock->buf == NULL) { - /* We don't have the buffer at all */ - size_t alloc_len = len < NM_REG_BUF ? NM_REG_BUF : NM_BIG_BUF; - sock->buf = isc_mem_get(sock->worker->mctx, alloc_len); - sock->buf_size = alloc_len; - } else { - /* We have the buffer but it's too small */ - sock->buf = isc_mem_reget(sock->worker->mctx, sock->buf, - sock->buf_size, NM_BIG_BUF); - sock->buf_size = NM_BIG_BUF; - } -} - void isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult, bool async) { @@ -1375,6 +1321,7 @@ isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, void isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) { REQUIRE(VALID_NMSOCK(sock)); + UNUSED(async); switch (sock->type) { case isc_nm_udpsocket: isc__nm_udp_failed_read_cb(sock, result, async); @@ -1382,9 +1329,6 @@ isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) { case isc_nm_tcpsocket: isc__nm_tcp_failed_read_cb(sock, result, async); return; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_failed_read_cb(sock, result, async); - return; case isc_nm_tlssocket: isc__nm_tls_failed_read_cb(sock, result, async); return; @@ -1410,11 +1354,6 @@ isc__nmsocket_connecttimeout_cb(uv_timer_t *timer) { isc__nmsocket_timer_stop(sock); - if (sock->tls.pending_req != NULL) { - REQUIRE(req == sock->tls.pending_req); - sock->tls.pending_req = NULL; - } - /* * Mark the connection as timed out and shutdown the socket. */ @@ -1643,7 +1582,6 @@ isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { buf->len = ISC_NETMGR_UDP_RECVBUF_SIZE; break; case isc_nm_tcpsocket: - case isc_nm_tlsdnssocket: buf->len = ISC_NETMGR_TCP_RECVBUF_SIZE; break; default: @@ -1674,10 +1612,6 @@ isc__nm_start_reading(isc_nmsocket_t *sock) { r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, isc__nm_tcp_read_cb); break; - case isc_nm_tlsdnssocket: - r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, - isc__nm_tlsdns_read_cb); - break; default: UNREACHABLE(); } @@ -1704,7 +1638,6 @@ isc__nm_stop_reading(isc_nmsocket_t *sock) { UV_RUNTIME_CHECK(uv_udp_recv_stop, r); break; case isc_nm_tcpsocket: - case isc_nm_tlsdnssocket: r = uv_read_stop(&sock->uv_handle.stream); UV_RUNTIME_CHECK(uv_read_stop, r); break; @@ -1726,90 +1659,6 @@ isc__nmsocket_closing(isc_nmsocket_t *sock) { (sock->server != NULL && !isc__nmsocket_active(sock->server))); } -static isc_result_t -processbuffer(isc_nmsocket_t *sock) { - switch (sock->type) { - case isc_nm_tlsdnssocket: - return (isc__nm_tlsdns_processbuffer(sock)); - default: - UNREACHABLE(); - } -} - -/* - * Process a DNS message. - * - * If we only have an incomplete DNS message, we don't touch any - * timers. If we do have a full message, reset the timer. - * - * Stop reading if this is a client socket. In this case we'll be - * called again later by isc__nm_resume_processing(). - */ -isc_result_t -isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { - for (;;) { - int_fast32_t ah = atomic_load(&sock->ah); - isc_result_t result = processbuffer(sock); - switch (result) { - case ISC_R_NOMORE: - /* - * Don't reset the timer until we have a - * full DNS message. - */ - result = isc__nm_start_reading(sock); - if (result != ISC_R_SUCCESS) { - return (result); - } - /* - * Start the timer only if there are no externally used - * active handles, there's always one active handle - * attached internally to sock->recv_handle in - * accept_connection() - */ - if (ah == 1) { - isc__nmsocket_timer_start(sock); - } - goto done; - case ISC_R_CANCELED: - isc__nmsocket_timer_stop(sock); - isc__nm_stop_reading(sock); - goto done; - case ISC_R_SUCCESS: - /* - * Stop the timer on the successful message read, this - * also allows to restart the timer when we have no more - * data. - */ - isc__nmsocket_timer_stop(sock); - - if (atomic_load(&sock->client)) { - isc__nm_stop_reading(sock); - goto done; - } - break; - default: - UNREACHABLE(); - } - } -done: - return (ISC_R_SUCCESS); -} - -void -isc__nm_resume_processing(void *arg) { - isc_nmsocket_t *sock = (isc_nmsocket_t *)arg; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(!atomic_load(&sock->client)); - - if (isc__nmsocket_closing(sock)) { - return; - } - - isc__nm_process_sock_buffer(sock); -} - void isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) { REQUIRE(VALID_NMHANDLE(handle)); @@ -1872,7 +1721,6 @@ isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) { switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tlsdnssocket: atomic_store(&sock->keepalive, value); sock->read_timeout = value ? atomic_load(&netmgr->keepalive) : atomic_load(&netmgr->idle); @@ -2009,9 +1857,6 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_tcpsocket: isc__nm_tcp_send(handle, region, cb, cbarg); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_send(handle, region, cb, cbarg); - break; case isc_nm_streamdnssocket: isc__nm_streamdns_send(handle, region, cb, cbarg); break; @@ -2039,9 +1884,6 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { case isc_nm_tcpsocket: isc__nm_tcp_read(handle, cb, cbarg); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_read(handle, cb, cbarg); - break; case isc_nm_streamdnssocket: isc__nm_streamdns_read(handle, cb, cbarg); break; @@ -2066,9 +1908,6 @@ isc_nm_cancelread(isc_nmhandle_t *handle) { case isc_nm_udpsocket: isc__nm_udp_cancelread(handle); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_cancelread(handle); - break; case isc_nm_streamdnssocket: isc__nm_streamdns_cancelread(handle); break; @@ -2106,9 +1945,6 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) { case isc_nm_tcplistener: isc__nm_tcp_stoplistening(sock); break; - case isc_nm_tlsdnslistener: - isc__nm_tlsdns_stoplistening(sock); - break; case isc_nm_streamdnslistener: isc__nm_streamdns_stoplistening(sock); break; @@ -2339,10 +2175,8 @@ isc__nmsocket_reset(isc_nmsocket_t *sock) { switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tlsdnssocket: /* - * This can be called from the TCP write timeout, or - * from the TCPDNS or TLSDNS branches of isc_nm_bad_request(). + * This can be called from the TCP write timeout. */ REQUIRE(sock->parent == NULL); break; @@ -2383,12 +2217,8 @@ isc__nmsocket_shutdown(isc_nmsocket_t *sock) { case isc_nm_tcpsocket: isc__nm_tcp_shutdown(sock); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_shutdown(sock); - break; case isc_nm_udplistener: case isc_nm_tcplistener: - case isc_nm_tlsdnslistener: return; default: UNREACHABLE(); @@ -2413,7 +2243,6 @@ shutdown_walk_cb(uv_handle_t *handle, void *arg) { case UV_TCP: switch (sock->type) { case isc_nm_tcpsocket: - case isc_nm_tlsdnssocket: if (sock->parent == NULL) { /* Reset the TCP connections on shutdown */ isc__nmsocket_reset(sock); @@ -2563,7 +2392,6 @@ isc_nm_bad_request(isc_nmhandle_t *handle) { switch (sock->type) { case isc_nm_udpsocket: return; - case isc_nm_tlsdnssocket: case isc_nm_tcpsocket: case isc_nm_streamdnssocket: case isc_nm_tlssocket: @@ -2591,8 +2419,6 @@ isc_nm_xfr_allowed(isc_nmhandle_t *handle) { sock = handle->sock; switch (sock->type) { - case isc_nm_tlsdnssocket: - return (isc__nm_tlsdns_xfr_allowed(sock)); case isc_nm_streamdnssocket: return (isc__nm_streamdns_xfr_allowed(sock)); default: @@ -2632,7 +2458,6 @@ isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) { break; #endif /* HAVE_LIBNGHTTP2 */ case isc_nm_udpsocket: - case isc_nm_tlsdnssocket: case isc_nm_streamdnssocket: return; break; @@ -2658,7 +2483,6 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle) { REQUIRE(VALID_NMSOCK(handle->sock)); switch (handle->sock->type) { - case isc_nm_tlsdnssocket: case isc_nm_tlssocket: return (true); #if HAVE_LIBNGHTTP2 @@ -2683,9 +2507,6 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { sock = handle->sock; switch (sock->type) { - case isc_nm_tlsdnssocket: - return (isc__nm_tlsdns_verify_tls_peer_result_string(handle)); - break; case isc_nm_tlssocket: return (isc__nm_tls_verify_tls_peer_result_string(handle)); break; @@ -2715,9 +2536,6 @@ isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0) { UNUSED(worker); switch (listener->type) { - case isc_nm_tlsdnslistener: - isc__nm_async_tlsdns_set_tlsctx(listener, tlsctx, tid); - break; case isc_nm_tlslistener: isc__nm_async_tls_set_tlsctx(listener, tlsctx, tid); break; @@ -2762,9 +2580,6 @@ isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { case isc_nm_tlslistener: set_tlsctx_workers(listener, tlsctx); break; - case isc_nm_tlsdnslistener: - set_tlsctx_workers(listener, tlsctx); - break; case isc_nm_streamdnslistener: isc__nm_streamdns_set_tlsctx(listener, tlsctx); break; @@ -2971,10 +2786,6 @@ nmsocket_type_totext(isc_nmsocket_type type) { return ("isc_nm_tlssocket"); case isc_nm_tlslistener: return ("isc_nm_tlslistener"); - case isc_nm_tlsdnslistener: - return ("isc_nm_tlsdnslistener"); - case isc_nm_tlsdnssocket: - return ("isc_nm_tlsdnssocket"); case isc_nm_httplistener: return ("isc_nm_httplistener"); case isc_nm_httpsocket: diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c deleted file mode 100644 index c857eb15ad..0000000000 --- a/lib/isc/netmgr/tlsdns.c +++ /dev/null @@ -1,2247 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * 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 "netmgr-int.h" -#include "openssl_shim.h" - -static atomic_uint_fast32_t last_tlsdnsquota_log = 0; - -static void -tls_error(isc_nmsocket_t *sock, isc_result_t result); - -static isc_result_t -tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req); - -static void -tlsdns_close_direct(isc_nmsocket_t *sock); - -static isc_result_t -tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req); -static void -tlsdns_connect_cb(uv_connect_t *uvreq, int status); - -static void -tlsdns_connection_cb(uv_stream_t *server, int status); - -static void -tlsdns_stop_cb(uv_handle_t *handle); - -static void -tlsdns_close_cb(uv_handle_t *uvhandle); - -static isc_result_t -accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota); - -static void -quota_accept_cb(isc_quota_t *quota, void *sock0); - -static void -async_tlsdns_cycle(isc_nmsocket_t *sock) __attribute__((unused)); - -static isc_result_t -tls_cycle(isc_nmsocket_t *sock); - -static void -call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result); - -static void -tlsdns_keep_client_tls_session(isc_nmsocket_t *sock); - -static void -tlsdns_set_tls_shutdown(isc_tls_t *tls) { - (void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN); -} - -static bool -peer_verification_has_failed(isc_nmsocket_t *sock) { - if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE && - SSL_get_verify_result(sock->tls.tls) != X509_V_OK) - { - return (true); - } - - return (false); -} - -static bool -can_log_tlsdns_quota(void) { - isc_stdtime_t now, last; - - isc_stdtime_get(&now); - last = atomic_exchange_relaxed(&last_tlsdnsquota_log, now); - if (now != last) { - return (true); - } - - return (false); -} - -static isc_result_t -tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { - isc__networker_t *worker = NULL; - isc_result_t result = ISC_R_UNSET; - int r; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(VALID_UVREQ(req)); - - REQUIRE(sock->tid == isc_tid()); - - worker = sock->worker; - - atomic_store(&sock->connecting, true); - - /* 2 minute timeout */ - result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - r = uv_tcp_init(&worker->loop->loop, &sock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&sock->uv_handle.handle, sock); - - r = uv_timer_init(&worker->loop->loop, &sock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - - if (isc__nm_closing(worker)) { - return (ISC_R_SHUTTINGDOWN); - } - - r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); - if (r != 0) { - isc__nm_closesocket(sock->fd); - isc__nm_incstats(sock, STATID_OPENFAIL); - return (isc_uverr2result(r)); - } - isc__nm_incstats(sock, STATID_OPEN); - - if (req->local.length != 0) { - r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0); - /* - * In case of shared socket UV_EINVAL will be returned and needs - * to be ignored - */ - if (r != 0 && r != UV_EINVAL) { - isc__nm_incstats(sock, STATID_BINDFAIL); - return (isc_uverr2result(r)); - } - } - - isc__nm_set_network_buffers(sock->worker->netmgr, - &sock->uv_handle.handle); - - uv_handle_set_data(&req->uv_req.handle, req); - r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp, - &req->peer.type.sa, tlsdns_connect_cb); - if (r != 0) { - isc__nm_incstats(sock, STATID_CONNECTFAIL); - return (isc_uverr2result(r)); - } - - uv_handle_set_data((uv_handle_t *)&sock->read_timer, - &req->uv_req.connect); - isc__nmsocket_timer_start(sock); - - atomic_store(&sock->connected, true); - - return (ISC_R_SUCCESS); -} - -void -isc__nm_async_tlsdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsconnect_t *ievent = - (isc__netievent_tlsdnsconnect_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - isc__nm_uvreq_t *req = ievent->req; - isc_result_t result = ISC_R_SUCCESS; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - REQUIRE(sock->parent == NULL); - REQUIRE(sock->tid == isc_tid()); - - result = tlsdns_connect_direct(sock, req); - if (result != ISC_R_SUCCESS) { - atomic_compare_exchange_enforced(&sock->connecting, - &(bool){ true }, false); - isc__nmsocket_clearcb(sock); - isc__nm_connectcb(sock, req, result, true); - atomic_store(&sock->active, false); - isc__nm_tlsdns_close(sock); - } - - /* - * The sock is now attached to the handle. - */ - isc__nmsocket_detach(&sock); -} - -static void -tlsdns_connect_cb(uv_connect_t *uvreq, int status) { - isc_result_t result = ISC_R_UNSET; - isc__nm_uvreq_t *req = NULL; - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle); - struct sockaddr_storage ss; - isc__networker_t *worker = NULL; - int r; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - worker = sock->worker; - - req = uv_handle_get_data((uv_handle_t *)uvreq); - - REQUIRE(VALID_UVREQ(req)); - REQUIRE(VALID_NMHANDLE(req->handle)); - - if (atomic_load(&sock->timedout)) { - result = ISC_R_TIMEDOUT; - goto error; - } else if (isc__nm_closing(worker)) { - /* Network manager shutting down */ - result = ISC_R_SHUTTINGDOWN; - goto error; - } else if (isc__nmsocket_closing(sock)) { - /* Connection canceled */ - result = ISC_R_CANCELED; - goto error; - } else if (status == UV_ETIMEDOUT) { - /* Timeout status code here indicates hard error */ - result = ISC_R_TIMEDOUT; - goto error; - } else if (status == UV_EADDRINUSE) { - /* - * On FreeBSD the TCP connect() call sometimes results in a - * spurious transient EADDRINUSE. Try a few more times before - * giving up. - */ - if (--req->connect_tries > 0) { - r = uv_tcp_connect( - &req->uv_req.connect, &sock->uv_handle.tcp, - &req->peer.type.sa, tlsdns_connect_cb); - if (r != 0) { - result = isc_uverr2result(r); - goto error; - } - return; - } - result = isc_uverr2result(status); - goto error; - } else if (status != 0) { - result = isc_uverr2result(status); - goto error; - } - - isc__nm_incstats(sock, STATID_CONNECT); - r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss, - &(int){ sizeof(ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto error; - } - - sock->tls.state = TLS_STATE_NONE; - sock->tls.tls = isc_tls_create(sock->tls.ctx); - RUNTIME_CHECK(sock->tls.tls != NULL); - - /* - * - */ - r = BIO_new_bio_pair(&sock->tls.ssl_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE, - &sock->tls.app_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE); - RUNTIME_CHECK(r == 1); - - r = BIO_new_bio_pair(&sock->tls.ssl_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE, - &sock->tls.app_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE); - RUNTIME_CHECK(r == 1); - -#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO - /* - * Note that if the rbio and wbio are the same then - * SSL_set0_rbio() and SSL_set0_wbio() each take ownership of - * one reference. Therefore it may be necessary to increment the - * number of references available using BIO_up_ref(3) before - * calling the set0 functions. - */ - SSL_set0_rbio(sock->tls.tls, sock->tls.ssl_rbio); - SSL_set0_wbio(sock->tls.tls, sock->tls.ssl_wbio); -#else - SSL_set_bio(sock->tls.tls, sock->tls.ssl_rbio, sock->tls.ssl_wbio); -#endif - - result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - if (sock->tls.client_sess_cache != NULL) { - isc_tlsctx_client_session_cache_reuse_sockaddr( - sock->tls.client_sess_cache, &sock->peer, - sock->tls.tls); - } - - SSL_set_connect_state(sock->tls.tls); - - /* Setting pending req */ - sock->tls.pending_req = req; - - result = isc__nm_process_sock_buffer(sock); - if (result != ISC_R_SUCCESS) { - sock->tls.pending_req = NULL; - goto error; - } - - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - sock->tls.pending_req = NULL; - goto error; - } - - return; - -error: - isc__nm_failed_connect_cb(sock, req, result, false); -} - -void -isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, - isc_nm_cb_t cb, void *cbarg, unsigned int timeout, - isc_tlsctx_t *sslctx, - isc_tlsctx_client_session_cache_t *client_sess_cache) { - isc_result_t result = ISC_R_SUCCESS; - isc_nmsocket_t *sock = NULL; - isc__netievent_tlsdnsconnect_t *ievent = NULL; - isc__nm_uvreq_t *req = NULL; - sa_family_t sa_family; - isc__networker_t *worker = &mgr->workers[isc_tid()]; - - REQUIRE(VALID_NM(mgr)); - REQUIRE(local != NULL); - REQUIRE(peer != NULL); - REQUIRE(sslctx != NULL); - - sa_family = peer->type.sa.sa_family; - - sock = isc_mem_get(worker->mctx, sizeof(*sock)); - isc__nmsocket_init(sock, worker, isc_nm_tlsdnssocket, local); - - sock->connect_timeout = timeout; - isc_tlsctx_attach(sslctx, &sock->tls.ctx); - atomic_init(&sock->client, true); - atomic_init(&sock->connecting, true); - - req = isc__nm_uvreq_get(sock->worker, sock); - req->cb.connect = cb; - req->cbarg = cbarg; - req->peer = *peer; - req->local = *local; - req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface); - - if (client_sess_cache != NULL) { - INSIST(isc_tlsctx_client_session_cache_getctx( - client_sess_cache) == sslctx); - isc_tlsctx_client_session_cache_attach( - client_sess_cache, &sock->tls.client_sess_cache); - } - - result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - if (isc__nm_closing(worker)) { - result = ISC_R_SHUTTINGDOWN; - goto failure; - } - - (void)isc__nm_socket_min_mtu(sock->fd, sa_family); - (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG); - - /* 2 minute timeout */ - result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - ievent = isc__nm_get_netievent_tlsdnsconnect(sock->worker, sock, req); - - atomic_store(&sock->active, true); - isc__nm_async_tlsdnsconnect(sock->worker, (isc__netievent_t *)ievent); - isc__nm_put_netievent_tlsdnsconnect(sock->worker, ievent); - - atomic_store(&sock->active, true); - - return; - -failure: - atomic_compare_exchange_enforced(&sock->connecting, &(bool){ true }, - false); - isc__nmsocket_clearcb(sock); - isc__nm_connectcb(sock, req, result, true); - atomic_store(&sock->closed, true); - isc__nmsocket_detach(&sock); -} - -static uv_os_sock_t -isc__nm_tlsdns_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) { - isc_result_t result; - uv_os_sock_t sock; - - result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - (void)isc__nm_socket_incoming_cpu(sock); - (void)isc__nm_socket_v6only(sock, sa_family); - - /* FIXME: set mss */ - - result = isc__nm_socket_reuse(sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - if (mgr->load_balance_sockets) { - result = isc__nm_socket_reuse_lb(sock); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - } - - return (sock); -} - -static void -start_tlsdns_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock, - uv_os_sock_t fd, int tid) { - isc__netievent_tlsdnslisten_t *ievent = NULL; - isc_nmsocket_t *csock = &sock->children[tid]; - isc__networker_t *worker = &mgr->workers[tid]; - - isc__nmsocket_init(csock, worker, isc_nm_tlsdnssocket, iface); - csock->parent = sock; - csock->accept_cb = sock->accept_cb; - csock->accept_cbarg = sock->accept_cbarg; - csock->recv_cb = sock->recv_cb; - csock->recv_cbarg = sock->recv_cbarg; - csock->backlog = sock->backlog; - isc_tlsctx_attach(sock->tls.ctx, &csock->tls.ctx); - - /* - * We don't attach to quota, just assign - to avoid - * increasing quota unnecessarily. - */ - csock->pquota = sock->pquota; - isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock); - - if (mgr->load_balance_sockets) { - UNUSED(fd); - csock->fd = isc__nm_tlsdns_lb_socket(mgr, - iface->type.sa.sa_family); - } else { - csock->fd = dup(fd); - } - REQUIRE(csock->fd >= 0); - - ievent = isc__nm_get_netievent_tlsdnslisten(csock->worker, csock); - - if (tid == 0) { - isc__nm_process_ievent(csock->worker, - (isc__netievent_t *)ievent); - } else { - isc__nm_enqueue_ievent(csock->worker, - (isc__netievent_t *)ievent); - } -} - -isc_result_t -isc_nm_listentlsdns(isc_nm_t *mgr, uint32_t workers, isc_sockaddr_t *iface, - isc_nm_recv_cb_t recv_cb, void *recv_cbarg, - isc_nm_accept_cb_t accept_cb, void *accept_cbarg, - int backlog, isc_quota_t *quota, isc_tlsctx_t *sslctx, - isc_nmsocket_t **sockp) { - isc_nmsocket_t *sock = NULL; - size_t children_size = 0; - uv_os_sock_t fd = -1; - isc_result_t result = ISC_R_UNSET; - isc__networker_t *worker = &mgr->workers[isc_tid()]; - - REQUIRE(VALID_NM(mgr)); - REQUIRE(isc_tid() == 0); - - if (workers == 0) { - workers = mgr->nloops; - } - REQUIRE(workers <= mgr->nloops); - - sock = isc_mem_get(worker->mctx, sizeof(*sock)); - isc__nmsocket_init(sock, worker, isc_nm_tlsdnslistener, iface); - - atomic_init(&sock->rchildren, 0); - sock->nchildren = (workers == ISC_NM_LISTEN_ALL) ? (uint32_t)mgr->nloops - : workers; - children_size = sock->nchildren * sizeof(sock->children[0]); - sock->children = isc_mem_getx(worker->mctx, children_size, - ISC_MEM_ZERO); - - isc__nmsocket_barrier_init(sock); - - sock->accept_cb = accept_cb; - sock->accept_cbarg = accept_cbarg; - sock->recv_cb = recv_cb; - sock->recv_cbarg = recv_cbarg; - sock->backlog = backlog; - sock->pquota = quota; - - isc_tlsctx_attach(sslctx, &sock->tls.ctx); - - if (!mgr->load_balance_sockets) { - fd = isc__nm_tlsdns_lb_socket(mgr, iface->type.sa.sa_family); - } - - for (size_t i = 1; i < sock->nchildren; i++) { - start_tlsdns_child(mgr, iface, sock, fd, i); - } - - start_tlsdns_child(mgr, iface, sock, fd, 0); - - if (!mgr->load_balance_sockets) { - isc__nm_closesocket(fd); - } - - LOCK(&sock->lock); - result = sock->result; - UNLOCK(&sock->lock); - INSIST(result != ISC_R_UNSET); - - atomic_store(&sock->active, true); - - if (result != ISC_R_SUCCESS) { - atomic_store(&sock->active, false); - isc__nm_tlsdns_stoplistening(sock); - isc_nmsocket_close(&sock); - - return (result); - } - - REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren); - *sockp = sock; - return (ISC_R_SUCCESS); -} - -void -isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnslisten_t *ievent = - (isc__netievent_tlsdnslisten_t *)ev0; - sa_family_t sa_family; - int r; - int flags = 0; - isc_nmsocket_t *sock = NULL; - isc_result_t result = ISC_R_UNSET; - - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->tid == isc_tid()); - REQUIRE(VALID_NMSOCK(ievent->sock->parent)); - - sock = ievent->sock; - sa_family = sock->iface.type.sa.sa_family; - - REQUIRE(sock->type == isc_nm_tlsdnssocket); - REQUIRE(sock->parent != NULL); - REQUIRE(sock->tid == isc_tid()); - - (void)isc__nm_socket_min_mtu(sock->fd, sa_family); - (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG); - - r = uv_tcp_init(&worker->loop->loop, &sock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&sock->uv_handle.handle, sock); - /* This keeps the socket alive after everything else is gone */ - isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL }); - - r = uv_timer_init(&worker->loop->loop, &sock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - - r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd); - if (r < 0) { - isc__nm_closesocket(sock->fd); - isc__nm_incstats(sock, STATID_OPENFAIL); - goto done; - } - isc__nm_incstats(sock, STATID_OPEN); - - if (sa_family == AF_INET6) { - flags = UV_TCP_IPV6ONLY; - } - - if (sock->worker->netmgr->load_balance_sockets) { - r = isc__nm_tcp_freebind(&sock->uv_handle.tcp, - &sock->iface.type.sa, flags); - if (r < 0) { - isc__nm_incstats(sock, STATID_BINDFAIL); - goto done; - } - } else { - LOCK(&sock->parent->lock); - if (sock->parent->fd == -1) { - r = isc__nm_tcp_freebind(&sock->uv_handle.tcp, - &sock->iface.type.sa, flags); - if (r < 0) { - isc__nm_incstats(sock, STATID_BINDFAIL); - UNLOCK(&sock->parent->lock); - goto done; - } - sock->parent->uv_handle.tcp.flags = - sock->uv_handle.tcp.flags; - sock->parent->fd = sock->fd; - } else { - /* The socket is already bound, just copy the flags */ - sock->uv_handle.tcp.flags = - sock->parent->uv_handle.tcp.flags; - } - UNLOCK(&sock->parent->lock); - } - - isc__nm_set_network_buffers(sock->worker->netmgr, - &sock->uv_handle.handle); - - /* - * The callback will run in the same thread uv_listen() was - * called from, so a race with tlsdns_connection_cb() isn't - * possible. - */ - r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog, - tlsdns_connection_cb); - if (r != 0) { - isc__nmsocket_log(sock, ISC_LOG_ERROR, "uv_listen failed: %s", - isc_result_totext(isc_uverr2result(r))); - isc__nm_incstats(sock, STATID_BINDFAIL); - goto done; - } - - atomic_store(&sock->listening, true); - -done: - result = isc_uverr2result(r); - atomic_fetch_add(&sock->parent->rchildren, 1); - - if (result != ISC_R_SUCCESS) { - sock->pquota = NULL; - } - - LOCK(&sock->parent->lock); - if (sock->parent->result == ISC_R_UNSET) { - sock->parent->result = result; - } else { - REQUIRE(sock->parent->result == result); - } - UNLOCK(&sock->parent->lock); - - REQUIRE(!worker->loop->paused); - isc_barrier_wait(&sock->parent->barrier); -} - -static void -tlsdns_connection_cb(uv_stream_t *server, int status) { - isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server); - isc_result_t result; - isc_quota_t *quota = NULL; - - if (status != 0) { - result = isc_uverr2result(status); - goto done; - } - - REQUIRE(VALID_NMSOCK(ssock)); - REQUIRE(ssock->tid == isc_tid()); - - if (isc__nmsocket_closing(ssock)) { - result = ISC_R_CANCELED; - goto done; - } - - if (ssock->pquota != NULL) { - result = isc_quota_attach_cb(ssock->pquota, "a, - &ssock->quotacb); - if (result == ISC_R_QUOTA) { - isc__nm_incstats(ssock, STATID_ACCEPTFAIL); - goto done; - } - } - - result = accept_connection(ssock, quota); -done: - isc__nm_accept_connection_log(ssock, result, can_log_tlsdns_quota()); -} - -static void -stop_tlsdns_child(isc_nmsocket_t *sock, uint32_t tid) { - isc_nmsocket_t *csock = NULL; - isc__netievent_tcpstop_t *ievent = NULL; - - csock = &sock->children[tid]; - REQUIRE(VALID_NMSOCK(csock)); - - atomic_store(&csock->active, false); - ievent = isc__nm_get_netievent_tlsdnsstop(csock->worker, csock); - - if (tid == 0) { - isc__nm_process_ievent(csock->worker, - (isc__netievent_t *)ievent); - } else { - isc__nm_enqueue_ievent(csock->worker, - (isc__netievent_t *)ievent); - } -} - -static void -stop_tlsdns_parent(isc_nmsocket_t *sock) { - /* Stop the parent */ - atomic_store(&sock->closed, true); - isc__nmsocket_prep_destroy(sock); -} - -void -isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnslistener); - - RUNTIME_CHECK(atomic_compare_exchange_strong(&sock->closing, - &(bool){ false }, true)); - - for (size_t i = 1; i < sock->nchildren; i++) { - stop_tlsdns_child(sock, i); - } - - stop_tlsdns_child(sock, 0); - - stop_tlsdns_parent(sock); -} - -static void -tls_shutdown(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - - isc__netievent_tlsdnsshutdown_t *ievent = - isc__nm_get_netievent_tlsdnsshutdown(sock->worker, sock); - isc__nm_maybe_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -void -isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsstop_t *ievent = - (isc__netievent_tlsdnsstop_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->parent != NULL); - - RUNTIME_CHECK(atomic_compare_exchange_strong(&sock->closing, - &(bool){ false }, true)); - - /* - * The order of the close operation is important here, the uv_close() - * gets scheduled in the reverse order, so we need to close the timer - * last, so its gone by the time we destroy the socket - */ - - /* 2. close the listening socket */ - isc__nmsocket_clearcb(sock); - isc__nm_stop_reading(sock); - uv_close(&sock->uv_handle.handle, tlsdns_stop_cb); - - /* 1. close the read timer */ - isc__nmsocket_timer_stop(sock); - uv_close(&sock->read_timer, NULL); - - (void)atomic_fetch_sub(&sock->parent->rchildren, 1); - - REQUIRE(!worker->loop->paused); - isc_barrier_wait(&sock->parent->barrier); -} - -void -isc__nm_async_tlsdnsshutdown(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsshutdown_t *ievent = - (isc__netievent_tlsdnsshutdown_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - int rv; - int err; - isc_result_t result; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(ievent->sock)); - - if (sock->tls.state != TLS_STATE_IO) { - /* Nothing to do */ - return; - } - - rv = SSL_shutdown(sock->tls.tls); - - if (rv == 1) { - sock->tls.state = TLS_STATE_NONE; - /* FIXME: continue closing the socket */ - return; - } - - if (rv == 0) { - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - tls_error(sock, result); - return; - } - - /* Reschedule closing the socket */ - tls_shutdown(sock); - return; - } - - err = SSL_get_error(sock->tls.tls, rv); - - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_X509_LOOKUP: - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - tls_error(sock, result); - return; - } - - /* Reschedule closing the socket */ - tls_shutdown(sock); - return; - case 0: - UNREACHABLE(); - case SSL_ERROR_ZERO_RETURN: - tls_error(sock, ISC_R_EOF); - break; - default: - tls_error(sock, ISC_R_TLSERROR); - } - return; -} - -void -isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, - bool async) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(result != ISC_R_SUCCESS); - - isc__nmsocket_timer_stop(sock); - isc__nm_stop_reading(sock); - - if (sock->tls.pending_req != NULL) { - isc_result_t failure_result = ISC_R_CANCELED; - isc__nm_uvreq_t *req = sock->tls.pending_req; - sock->tls.pending_req = NULL; - - if (peer_verification_has_failed(sock)) { - /* - * Save error message as 'sock->tls' will get detached. - */ - sock->tls.tls_verify_errmsg = - isc_tls_verify_peer_result_string( - sock->tls.tls); - failure_result = ISC_R_TLSBADPEERCERT; - } - isc__nm_failed_connect_cb(sock, req, failure_result, async); - } - - if (!sock->recv_read) { - goto destroy; - } - sock->recv_read = false; - - if (sock->recv_cb != NULL) { - isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL); - isc__nmsocket_clearcb(sock); - isc__nm_readcb(sock, req, result, async); - } - -destroy: - call_pending_send_callbacks(sock, result); - isc__nmsocket_prep_destroy(sock); - - /* - * We need to detach from quota after the read callback function - * had a chance to be executed. - */ - if (sock->quota != NULL) { - isc_quota_detach(&sock->quota); - } -} - -void -isc__nm_tlsdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - isc_nmsocket_t *sock = handle->sock; - isc__netievent_tlsdnsread_t *ievent = NULL; - isc_nm_t *netmgr = sock->worker->netmgr; - - REQUIRE(sock->type == isc_nm_tlsdnssocket); - REQUIRE(sock->statichandle == handle); - - sock->recv_cb = cb; - sock->recv_cbarg = cbarg; - sock->recv_read = true; - if (sock->read_timeout == 0) { - sock->read_timeout = (atomic_load(&sock->keepalive) - ? atomic_load(&netmgr->keepalive) - : atomic_load(&netmgr->idle)); - } - - ievent = isc__nm_get_netievent_tlsdnsread(sock->worker, sock); - - /* - * This MUST be done asynchronously, no matter which thread - * we're in. The callback function for isc_nm_read() often calls - * isc_nm_read() again; if we tried to do that synchronously - * we'd clash in processbuffer() and grow the stack - * indefinitely. - */ - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); - - return; -} - -void -isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsread_t *ievent = - (isc__netievent_tlsdnsread_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - isc_result_t result = ISC_R_SUCCESS; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - if (isc__nmsocket_closing(sock)) { - sock->reading = true; - isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false); - return; - } - - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - isc__nm_failed_read_cb(sock, result, false); - } -} - -/* - * Process a single packet from the incoming buffer. - * - * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something - * was processed; return ISC_R_NOMORE if there isn't a full message - * to be processed. - * - * The caller will need to unreference the handle. - */ -isc_result_t -isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock) { - size_t len; - isc__nm_uvreq_t *req = NULL; - isc_nmhandle_t *handle = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - if (isc__nmsocket_closing(sock)) { - return (ISC_R_CANCELED); - } - - /* - * If we don't even have the length yet, we can't do - * anything. - */ - if (sock->buf_len < 2) { - return (ISC_R_NOMORE); - } - - /* - * Process the first packet from the buffer, leaving - * the rest (if any) for later. - */ - len = ntohs(*(uint16_t *)sock->buf); - if (len > sock->buf_len - 2) { - return (ISC_R_NOMORE); - } - - if (sock->recv_cb == NULL) { - /* - * recv_cb has been cleared - there is - * nothing to do - */ - return (ISC_R_CANCELED); - } else if (sock->statichandle == NULL && - sock->tls.state == TLS_STATE_IO && - atomic_load(&sock->connected) && - !atomic_load(&sock->connecting)) - { - /* - * It seems that some unexpected data (a DNS message) has - * arrived while we are wrapping up. - */ - return (ISC_R_CANCELED); - } - - req = isc__nm_get_read_req(sock, NULL); - REQUIRE(VALID_UVREQ(req)); - - /* - * We need to launch isc__nm_resume_processing() after the buffer - * has been consumed, thus we must delay detaching the handle. - */ - isc_nmhandle_attach(req->handle, &handle); - - /* - * The callback will be called synchronously because the - * result is ISC_R_SUCCESS, so we don't need to have - * the buffer on the heap - */ - req->uvbuf.base = (char *)sock->buf + 2; - req->uvbuf.len = len; - - /* - * If isc__nm_tlsdns_read() was called, it will be satisfied by - * single DNS message in the next call. - */ - sock->recv_read = false; - - /* - * An assertion failure here means that there's an erroneous - * extra nmhandle detach happening in the callback and - * isc__nm_resume_processing() is called while we're - * processing the buffer. - */ - REQUIRE(sock->processing == false); - sock->processing = true; - isc__nm_readcb(sock, req, ISC_R_SUCCESS, false); - sock->processing = false; - - len += 2; - sock->buf_len -= len; - if (sock->buf_len > 0) { - memmove(sock->buf, sock->buf + len, sock->buf_len); - } - - isc_nmhandle_detach(&handle); - - if (isc__nmsocket_closing(sock)) { - tlsdns_set_tls_shutdown(sock->tls.tls); - tlsdns_keep_client_tls_session(sock); - } - - return (ISC_R_SUCCESS); -} - -static isc_result_t -tls_cycle_input(isc_nmsocket_t *sock) { - isc_result_t result = ISC_R_SUCCESS; - int err = 0; - int rv = 1; - - if (sock->tls.state == TLS_STATE_IO) { - size_t len; - - for (;;) { - (void)SSL_peek(sock->tls.tls, &(char){ '\0' }, 0); - - int pending = SSL_pending(sock->tls.tls); - if (pending > (int)ISC_NETMGR_TCP_RECVBUF_SIZE) { - pending = (int)ISC_NETMGR_TCP_RECVBUF_SIZE; - } - - if (pending != 0) { - if ((sock->buf_len + pending) > sock->buf_size) - { - isc__nm_alloc_dnsbuf( - sock, sock->buf_len + pending); - } - - len = 0; - rv = SSL_read_ex(sock->tls.tls, - sock->buf + sock->buf_len, - sock->buf_size - sock->buf_len, - &len); - if (rv != 1) { - /* - * Process what's in the buffer so far - */ - result = isc__nm_process_sock_buffer( - sock); - if (result != ISC_R_SUCCESS) { - goto failure; - } - /* - * FIXME: Should we call - * isc__nm_failed_read_cb()? - */ - break; - } - - INSIST((size_t)pending == len); - - sock->buf_len += len; - } - result = isc__nm_process_sock_buffer(sock); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - if (atomic_load(&sock->client)) { - break; - } - - if (pending == 0) { - break; - } - } - } else if (!SSL_is_init_finished(sock->tls.tls)) { - if (SSL_is_server(sock->tls.tls)) { - rv = SSL_accept(sock->tls.tls); - } else { - rv = SSL_connect(sock->tls.tls); - } - - } else { - rv = 1; - } - - if (rv <= 0) { - err = SSL_get_error(sock->tls.tls, rv); - } - - switch (err) { - case SSL_ERROR_WANT_READ: - if (sock->tls.state == TLS_STATE_NONE && - !SSL_is_init_finished(sock->tls.tls)) - { - sock->tls.state = TLS_STATE_HANDSHAKE; - result = isc__nm_process_sock_buffer(sock); - if (result != ISC_R_SUCCESS) { - goto failure; - } - } - /* else continue reading */ - break; - case SSL_ERROR_WANT_WRITE: - async_tlsdns_cycle(sock); - break; - case SSL_ERROR_WANT_X509_LOOKUP: - /* Continue reading/writing */ - break; - case 0: - /* Everything is ok, continue */ - break; - case SSL_ERROR_ZERO_RETURN: - return (ISC_R_EOF); - default: - return (ISC_R_TLSERROR); - } - - /* Stop state after handshake */ - if (sock->tls.state == TLS_STATE_HANDSHAKE && - SSL_is_init_finished(sock->tls.tls)) - { - const unsigned char *alpn = NULL; - unsigned int alpnlen = 0; - - isc__nmsocket_log_tls_session_reuse(sock, sock->tls.tls); - - isc_tls_get_selected_alpn(sock->tls.tls, &alpn, &alpnlen); - if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN && - memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn, - ISC_TLS_DOT_PROTO_ALPN_ID_LEN) == 0) - { - sock->tls.alpn_negotiated = true; - } - - sock->tls.state = TLS_STATE_IO; - - if (SSL_is_server(sock->tls.tls)) { - REQUIRE(sock->recv_handle != NULL); - REQUIRE(sock->accept_cb != NULL); - result = sock->accept_cb(sock->recv_handle, - ISC_R_SUCCESS, - sock->accept_cbarg); - if (result != ISC_R_SUCCESS) { - isc_nmhandle_detach(&sock->recv_handle); - goto failure; - } - } else { - isc__nm_uvreq_t *req = sock->tls.pending_req; - sock->tls.pending_req = NULL; - - isc__nmsocket_timer_stop(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, - sock); - - atomic_compare_exchange_enforced( - &sock->connecting, &(bool){ true }, false); - isc__nm_connectcb(sock, req, ISC_R_SUCCESS, true); - } - async_tlsdns_cycle(sock); - } -failure: - return (result); -} - -static void -tls_error(isc_nmsocket_t *sock, isc_result_t result) { - switch (sock->tls.state) { - case TLS_STATE_HANDSHAKE: - case TLS_STATE_IO: - if (atomic_load(&sock->connecting)) { - isc__nm_uvreq_t *req = sock->tls.pending_req; - sock->tls.pending_req = NULL; - - isc__nm_failed_connect_cb(sock, req, result, false); - } else { - isc__nm_tlsdns_failed_read_cb(sock, result, false); - } - break; - case TLS_STATE_ERROR: - return; - default: - break; - } - - sock->tls.state = TLS_STATE_ERROR; - sock->tls.pending_error = result; - - isc__nmsocket_shutdown(sock); -} - -static void -call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result) { - isc__nm_uvreq_t *cbreq = ISC_LIST_HEAD(sock->tls.sendreqs); - while (cbreq != NULL) { - isc__nm_uvreq_t *next = ISC_LIST_NEXT(cbreq, link); - ISC_LIST_UNLINK(sock->tls.sendreqs, cbreq, link); - INSIST(sock == cbreq->handle->sock); - isc__nm_sendcb(sock, cbreq, result, false); - cbreq = next; - } -} - -static void -free_senddata(isc_nmsocket_t *sock, const isc_result_t result) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tls.senddata.base != NULL); - REQUIRE(sock->tls.senddata.length > 0); - - isc_mem_put(sock->worker->mctx, sock->tls.senddata.base, - sock->tls.senddata.length); - sock->tls.senddata.base = NULL; - sock->tls.senddata.length = 0; - - call_pending_send_callbacks(sock, result); -} - -static void -tls_write_cb(uv_write_t *req, int status) { - isc_result_t result = status != 0 ? isc_uverr2result(status) - : ISC_R_SUCCESS; - isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; - isc_nmsocket_t *sock = uvreq->sock; - - isc_nm_timer_stop(uvreq->timer); - isc_nm_timer_detach(&uvreq->timer); - - free_senddata(sock, result); - - isc__nm_uvreq_put(&uvreq, sock); - - if (status != 0) { - tls_error(sock, isc_uverr2result(status)); - return; - } - - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - tls_error(sock, result); - return; - } -} - -static isc_result_t -tls_cycle_output(isc_nmsocket_t *sock) { - isc_result_t result = ISC_R_SUCCESS; - int pending; - - while ((pending = BIO_pending(sock->tls.app_rbio)) > 0) { - isc__nm_uvreq_t *req = NULL; - size_t bytes; - int rv; - int r; - - if (sock->tls.senddata.base != NULL || - sock->tls.senddata.length > 0) - { - break; - } - - if (pending > (int)ISC_NETMGR_TCP_RECVBUF_SIZE) { - pending = (int)ISC_NETMGR_TCP_RECVBUF_SIZE; - } - - sock->tls.senddata.base = isc_mem_get(sock->worker->mctx, - pending); - sock->tls.senddata.length = pending; - - /* It's a bit misnomer here, but it does the right thing */ - req = isc__nm_get_read_req(sock, NULL); - req->uvbuf.base = (char *)sock->tls.senddata.base; - req->uvbuf.len = sock->tls.senddata.length; - - rv = BIO_read_ex(sock->tls.app_rbio, req->uvbuf.base, - req->uvbuf.len, &bytes); - - RUNTIME_CHECK(rv == 1); - INSIST((size_t)pending == bytes); - - r = uv_try_write(&sock->uv_handle.stream, &req->uvbuf, 1); - - if (r == pending) { - /* Wrote everything, restart */ - isc__nm_uvreq_put(&req, sock); - free_senddata(sock, ISC_R_SUCCESS); - continue; - } - - if (r > 0) { - /* Partial write, send rest asynchronously */ - memmove(req->uvbuf.base, req->uvbuf.base + r, - req->uvbuf.len - r); - req->uvbuf.len = req->uvbuf.len - r; - } else if (r == UV_ENOSYS || r == UV_EAGAIN) { - /* uv_try_write is not supported, send - * asynchronously */ - } else { - result = isc_uverr2result(r); - isc__nm_uvreq_put(&req, sock); - free_senddata(sock, result); - break; - } - - r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, - &req->uvbuf, 1, tls_write_cb); - if (r < 0) { - result = isc_uverr2result(r); - isc__nm_uvreq_put(&req, sock); - free_senddata(sock, result); - break; - } - - isc_nm_timer_create(req->handle, isc__nmsocket_writetimeout_cb, - req, &req->timer); - if (sock->write_timeout > 0) { - isc_nm_timer_start(req->timer, sock->write_timeout); - } - - break; - } - - return (result); -} - -static isc_result_t -tls_pop_error(isc_nmsocket_t *sock) { - isc_result_t result; - - if (sock->tls.state != TLS_STATE_ERROR) { - return (ISC_R_SUCCESS); - } - - if (sock->tls.pending_error == ISC_R_SUCCESS) { - return (ISC_R_TLSERROR); - } - - result = sock->tls.pending_error; - sock->tls.pending_error = ISC_R_SUCCESS; - - return (result); -} - -static isc_result_t -tls_cycle(isc_nmsocket_t *sock) { - isc_result_t result; - - /* - * Clear the TLS error queue so that SSL_get_error() and SSL I/O - * routine calls will not get affected by prior error statuses. - * - * See here: - * https://www.openssl.org/docs/man3.0/man3/SSL_get_error.html - * - * In particular, it mentions the following: - * - * The current thread's error queue must be empty before the - * TLS/SSL I/O operation is attempted, or SSL_get_error() will not - * work reliably. - * - * As we use the result of SSL_get_error() to decide on I/O - * operations, we need to ensure that it works reliably by - * cleaning the error queue. - * - * The sum of details: https://stackoverflow.com/a/37980911 - */ - ERR_clear_error(); - - if (isc__nmsocket_closing(sock)) { - return (ISC_R_CANCELED); - } - - result = tls_pop_error(sock); - if (result != ISC_R_SUCCESS) { - goto done; - } - - if (sock->tls.cycle) { - return (ISC_R_SUCCESS); - } - - sock->tls.cycle = true; - result = tls_cycle_input(sock); - if (result != ISC_R_SUCCESS) { - goto done; - } - - result = tls_cycle_output(sock); - if (result != ISC_R_SUCCESS) { - goto done; - } -done: - sock->tls.cycle = false; - - return (result); -} - -static void -async_tlsdns_cycle(isc_nmsocket_t *sock) { - isc__netievent_tlsdnscycle_t *ievent = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - - /* Socket was closed midflight by isc__nm_tlsdns_shutdown() */ - if (isc__nmsocket_closing(sock)) { - return; - } - - ievent = isc__nm_get_netievent_tlsdnscycle(sock->worker, sock); - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -void -isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnscycle_t *ievent = - (isc__netievent_tlsdnscycle_t *)ev0; - isc_result_t result; - isc_nmsocket_t *sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->tid == isc_tid()); - - sock = ievent->sock; - - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - tls_error(sock, result); - } -} - -void -isc__nm_tlsdns_read_cb(uv_stream_t *stream, ssize_t nread, - const uv_buf_t *buf) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream); - size_t len; - isc_result_t result; - int rv; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->reading); - REQUIRE(buf != NULL); - - if (isc__nmsocket_closing(sock)) { - isc__nm_failed_read_cb(sock, ISC_R_CANCELED, true); - goto free; - } - - if (nread < 0) { - if (nread != UV_EOF) { - isc__nm_incstats(sock, STATID_RECVFAIL); - } - - isc__nm_failed_read_cb(sock, isc_uverr2result(nread), true); - - goto free; - } - - if (!atomic_load(&sock->client)) { - sock->read_timeout = atomic_load(&sock->worker->netmgr->idle); - } - - /* - * The input has to be fed into BIO - */ - rv = BIO_write_ex(sock->tls.app_wbio, buf->base, (size_t)nread, &len); - - if (rv <= 0 || (size_t)nread != len) { - isc__nm_failed_read_cb(sock, ISC_R_TLSERROR, true); - goto free; - } - - result = tls_cycle(sock); - if (result != ISC_R_SUCCESS) { - isc__nm_failed_read_cb(sock, result, true); - } -free: - async_tlsdns_cycle(sock); - - if (nread < 0) { - /* - * The buffer may be a null buffer on error. - */ - if (buf->base == NULL && buf->len == 0) { - return; - } - } - - isc__nm_free_uvbuf(sock, buf); -} - -static void -quota_accept_cb(isc_quota_t *quota, void *sock0) { - isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0; - - REQUIRE(VALID_NMSOCK(sock)); - - /* - * Create a tlsdnsaccept event and pass it using the async - * channel. - */ - - isc__netievent_tlsdnsaccept_t *ievent = - isc__nm_get_netievent_tlsdnsaccept(sock->worker, sock, quota); - isc__nm_maybe_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -/* - * This is called after we get a quota_accept_cb() callback. - */ -void -isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsaccept_t *ievent = - (isc__netievent_tlsdnsaccept_t *)ev0; - isc_result_t result; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(ievent->sock)); - REQUIRE(ievent->sock->tid == isc_tid()); - - result = accept_connection(ievent->sock, ievent->quota); - isc__nm_accept_connection_log(ievent->sock, result, - can_log_tlsdns_quota()); -} - -static isc_result_t -accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { - isc_nmsocket_t *csock = NULL; - isc__networker_t *worker = NULL; - int r; - isc_result_t result; - struct sockaddr_storage peer_ss; - struct sockaddr_storage local_ss; - isc_sockaddr_t local; - - REQUIRE(VALID_NMSOCK(ssock)); - REQUIRE(ssock->tid == isc_tid()); - - if (isc__nmsocket_closing(ssock)) { - if (quota != NULL) { - isc_quota_detach("a); - } - return (ISC_R_CANCELED); - } - - REQUIRE(ssock->accept_cb != NULL); - - csock = isc_mem_get(ssock->worker->mctx, sizeof(isc_nmsocket_t)); - isc__nmsocket_init(csock, ssock->worker, isc_nm_tlsdnssocket, - &ssock->iface); - isc__nmsocket_attach(ssock, &csock->server); - csock->accept_cb = ssock->accept_cb; - csock->accept_cbarg = ssock->accept_cbarg; - csock->recv_cb = ssock->recv_cb; - csock->recv_cbarg = ssock->recv_cbarg; - csock->quota = quota; - atomic_init(&csock->accepting, true); - - worker = csock->worker; - - r = uv_tcp_init(&worker->loop->loop, &csock->uv_handle.tcp); - UV_RUNTIME_CHECK(uv_tcp_init, r); - uv_handle_set_data(&csock->uv_handle.handle, csock); - - r = uv_timer_init(&worker->loop->loop, &csock->read_timer); - UV_RUNTIME_CHECK(uv_timer_init, r); - uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock); - - r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - r = uv_tcp_getpeername(&csock->uv_handle.tcp, - (struct sockaddr *)&peer_ss, - &(int){ sizeof(peer_ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - result = isc_sockaddr_fromsockaddr(&csock->peer, - (struct sockaddr *)&peer_ss); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - r = uv_tcp_getsockname(&csock->uv_handle.tcp, - (struct sockaddr *)&local_ss, - &(int){ sizeof(local_ss) }); - if (r != 0) { - result = isc_uverr2result(r); - goto failure; - } - - result = isc_sockaddr_fromsockaddr(&local, - (struct sockaddr *)&local_ss); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - csock->tls.state = TLS_STATE_NONE; - - csock->tls.tls = isc_tls_create(ssock->tls.ctx); - RUNTIME_CHECK(csock->tls.tls != NULL); - - r = BIO_new_bio_pair(&csock->tls.ssl_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE, - &csock->tls.app_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE); - RUNTIME_CHECK(r == 1); - - r = BIO_new_bio_pair(&csock->tls.ssl_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE, - &csock->tls.app_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE); - RUNTIME_CHECK(r == 1); - -#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO - /* - * Note that if the rbio and wbio are the same then - * SSL_set0_rbio() and SSL_set0_wbio() each take ownership of - * one reference. Therefore it may be necessary to increment the - * number of references available using BIO_up_ref(3) before - * calling the set0 functions. - */ - SSL_set0_rbio(csock->tls.tls, csock->tls.ssl_rbio); - SSL_set0_wbio(csock->tls.tls, csock->tls.ssl_wbio); -#else - SSL_set_bio(csock->tls.tls, csock->tls.ssl_rbio, csock->tls.ssl_wbio); -#endif - - SSL_set_accept_state(csock->tls.tls); - - /* FIXME: Set SSL_MODE_RELEASE_BUFFERS */ - - atomic_store(&csock->accepting, false); - - isc__nm_incstats(csock, STATID_ACCEPT); - - csock->read_timeout = atomic_load(&csock->worker->netmgr->init); - - csock->closehandle_cb = isc__nm_resume_processing; - - /* - * We need to keep the handle alive until we fail to read or - * connection is closed by the other side, it will be detached - * via prep_destroy()->tlsdns_close_direct(). - * - * The handle will be either detached on acceptcb failure or in - * the readcb. - */ - csock->recv_handle = isc__nmhandle_get(csock, NULL, &local); - - /* - * The initial timer has been set, update the read timeout for - * the next reads. - */ - csock->read_timeout = - (atomic_load(&csock->keepalive) - ? atomic_load(&csock->worker->netmgr->keepalive) - : atomic_load(&csock->worker->netmgr->idle)); - - result = isc__nm_process_sock_buffer(csock); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - /* - * sock is now attached to the handle. - */ - isc__nmsocket_detach(&csock); - - return (ISC_R_SUCCESS); - -failure: - atomic_store(&csock->active, false); - - isc__nm_failed_accept_cb(csock, result); - - isc__nmsocket_prep_destroy(csock); - - isc__nmsocket_detach(&csock); - - return (result); -} - -void -isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, - isc_nm_cb_t cb, void *cbarg) { - isc__netievent_tlsdnssend_t *ievent = NULL; - isc__nm_uvreq_t *uvreq = NULL; - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - - sock = handle->sock; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - - uvreq = isc__nm_uvreq_get(sock->worker, sock); - *(uint16_t *)uvreq->tcplen = htons(region->length); - uvreq->uvbuf.base = (char *)region->base; - uvreq->uvbuf.len = region->length; - - isc_nmhandle_attach(handle, &uvreq->handle); - - uvreq->cb.send = cb; - uvreq->cbarg = cbarg; - - ievent = isc__nm_get_netievent_tlsdnssend(sock->worker, sock, uvreq); - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); - return; -} - -/* - * Handle 'tcpsend' async event - send a packet on the socket - */ -void -isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { - isc_result_t result; - isc__netievent_tlsdnssend_t *ievent = - (isc__netievent_tlsdnssend_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - isc__nm_uvreq_t *uvreq = ievent->req; - - UNUSED(worker); - - REQUIRE(sock->type == isc_nm_tlsdnssocket); - REQUIRE(sock->tid == isc_tid()); - - if (sock->write_timeout == 0) { - sock->write_timeout = - (atomic_load(&sock->keepalive) - ? atomic_load(&sock->worker->netmgr->keepalive) - : atomic_load(&sock->worker->netmgr->idle)); - } - - result = tlsdns_send_direct(sock, uvreq); - if (result != ISC_R_SUCCESS) { - isc__nm_incstats(sock, STATID_SENDFAIL); - isc__nm_failed_send_cb(sock, uvreq, result, false); - } -} - -static void -tlsdns_send_enqueue(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { - isc__netievent_tlsdnssend_t *ievent = - isc__nm_get_netievent_tlsdnssend(sock->worker, sock, req); - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -static isc_result_t -tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { - isc_result_t result; - int err = 0; - int rv; - size_t bytes = 0; - size_t sendlen; - isc__networker_t *worker = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(VALID_UVREQ(req)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - - result = tls_pop_error(sock); - if (result != ISC_R_SUCCESS) { - return (result); - } - - if (isc__nmsocket_closing(sock)) { - return (ISC_R_CANCELED); - } - - /* Writes won't succeed until handshake end */ - if (!SSL_is_init_finished(sock->tls.tls)) { - goto requeue; - } - - /* - * There's no SSL_writev(), so we need to use a local buffer to - * assemble the whole message - */ - worker = sock->worker; - sendlen = req->uvbuf.len + sizeof(uint16_t); - memmove(worker->sendbuf, req->tcplen, sizeof(uint16_t)); - memmove(worker->sendbuf + sizeof(uint16_t), req->uvbuf.base, - req->uvbuf.len); - - rv = SSL_write_ex(sock->tls.tls, worker->sendbuf, sendlen, &bytes); - if (rv > 0) { - /* SSL_write_ex() doesn't do partial writes */ - INSIST(sendlen == bytes); - - ISC_LIST_APPEND(sock->tls.sendreqs, req, link); - async_tlsdns_cycle(sock); - return (ISC_R_SUCCESS); - } - - /* Nothing was written, maybe enqueue? */ - err = SSL_get_error(sock->tls.tls, rv); - - switch (err) { - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - break; - case 0: - UNREACHABLE(); - default: - return (ISC_R_TLSERROR); - } - - result = tls_cycle(sock); - -requeue: - - tlsdns_send_enqueue(sock, req); - - return (result); -} - -static void -tlsdns_stop_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - - uv_handle_set_data(handle, NULL); - - if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false }, - true)) - { - UNREACHABLE(); - } - - isc__nm_incstats(sock, STATID_CLOSE); - - atomic_store(&sock->listening, false); - - BIO_free_all(sock->tls.app_rbio); - BIO_free_all(sock->tls.app_wbio); - - if (sock->tls.ctx != NULL) { - isc_tlsctx_free(&sock->tls.ctx); - } - - isc__nmsocket_detach(&sock); -} - -static void -tlsdns_close_sock(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - - if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false }, - true)) - { - UNREACHABLE(); - } - - isc__nm_incstats(sock, STATID_CLOSE); - - if (sock->server != NULL) { - isc__nmsocket_detach(&sock->server); - } - - atomic_store(&sock->connected, false); - - if (sock->tls.tls != NULL) { - /* - * Let's shutdown the TLS session properly so that the session - * will remain resumable, if required. - */ - tlsdns_set_tls_shutdown(sock->tls.tls); - tlsdns_keep_client_tls_session(sock); - isc_tls_free(&sock->tls.tls); - } - - BIO_free_all(sock->tls.app_rbio); - BIO_free_all(sock->tls.app_wbio); - - if (sock->tls.ctx != NULL) { - isc_tlsctx_free(&sock->tls.ctx); - } - - isc__nmsocket_prep_destroy(sock); -} - -static void -tlsdns_close_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - uv_handle_set_data(handle, NULL); - - tlsdns_close_sock(sock); -} - -static void -tlsdns_close_direct(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(atomic_load(&sock->closing)); - REQUIRE(sock->parent == NULL); - - REQUIRE(sock->tls.pending_req == NULL); - - if (sock->quota != NULL) { - isc_quota_detach(&sock->quota); - } - - if (sock->recv_handle != NULL) { - isc_nmhandle_detach(&sock->recv_handle); - } - - /* - * The order of the close operation is important here, the uv_close() - * gets scheduled in the reverse order, so we need to close the timer - * last, so its gone by the time we destroy the socket - */ - - if (!uv_is_closing(&sock->uv_handle.handle)) { - /* Normal order of operation */ - - /* 2. close the socket + destroy the socket in callback */ - isc__nmsocket_clearcb(sock); - isc__nm_stop_reading(sock); - uv_close(&sock->uv_handle.handle, tlsdns_close_cb); - - /* 1. close the timer */ - isc__nmsocket_timer_stop(sock); - uv_close((uv_handle_t *)&sock->read_timer, NULL); - } else { - /* The socket was already closed elsewhere */ - - /* 1. close the timer + destroy the socket in callback */ - isc__nmsocket_timer_stop(sock); - uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock); - uv_close((uv_handle_t *)&sock->read_timer, tlsdns_close_cb); - } -} - -void -isc__nm_tlsdns_close(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - REQUIRE(!isc__nmsocket_active(sock)); - - if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false }, - true)) - { - return; - } - - if (sock->tid == isc_tid()) { - tlsdns_close_direct(sock); - } else { - /* - * We need to create an event and pass it using async - * channel - */ - isc__netievent_tlsdnsclose_t *ievent = - isc__nm_get_netievent_tlsdnsclose(sock->worker, sock); - - isc__nm_enqueue_ievent(sock->worker, - (isc__netievent_t *)ievent); - } -} - -void -isc__nm_async_tlsdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnsclose_t *ievent = - (isc__netievent_tlsdnsclose_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - tlsdns_close_direct(sock); -} - -static void -tlsdns_close_connect_cb(uv_handle_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data(handle); - - REQUIRE(VALID_NMSOCK(sock)); - - REQUIRE(sock->tid == isc_tid()); - - isc__nmsocket_prep_destroy(sock); - isc__nmsocket_detach(&sock); -} - -void -isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) { - isc__networker_t *worker = NULL; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - - worker = sock->worker; - - /* - * If the socket is active, mark it inactive and - * continue. If it isn't active, stop now. - */ - if (!isc__nmsocket_deactivate(sock)) { - return; - } - - if (sock->tls.tls) { - /* Shutdown any active TLS connections */ - tlsdns_set_tls_shutdown(sock->tls.tls); - } - - if (atomic_load(&sock->accepting)) { - return; - } - - /* TLS handshake hasn't been completed yet */ - if (atomic_load(&sock->connecting)) { - isc_nmsocket_t *tsock = NULL; - - /* - * TCP connection has been established, now waiting on - * TLS handshake to complete - */ - if (sock->tls.pending_req != NULL) { - isc_result_t result = ISC_R_CANCELED; - isc__nm_uvreq_t *req = sock->tls.pending_req; - sock->tls.pending_req = NULL; - - if (peer_verification_has_failed(sock)) { - /* - * Save error message as 'sock->tls' will get - * detached. - */ - sock->tls.tls_verify_errmsg = - isc_tls_verify_peer_result_string( - sock->tls.tls); - result = ISC_R_TLSBADPEERCERT; - } - isc__nm_failed_connect_cb(sock, req, result, false); - return; - } - - /* The TCP connection hasn't been established yet */ - isc__nmsocket_attach(sock, &tsock); - uv_close(&sock->uv_handle.handle, tlsdns_close_connect_cb); - return; - } - - if (sock->statichandle != NULL) { - if (isc__nm_closing(worker)) { - isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false); - } else { - isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false); - } - return; - } - - /* - * Otherwise, we just send the socket to abyss... - */ - if (sock->parent == NULL) { - isc__nmsocket_prep_destroy(sock); - } -} - -void -isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle) { - isc_nmsocket_t *sock = NULL; - isc__netievent_tlsdnscancel_t *ievent = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - - sock = handle->sock; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - - ievent = isc__nm_get_netievent_tlsdnscancel(sock->worker, sock, handle); - isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); -} - -void -isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) { - isc__netievent_tlsdnscancel_t *ievent = - (isc__netievent_tlsdnscancel_t *)ev0; - isc_nmsocket_t *sock = ievent->sock; - - UNUSED(worker); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_tid()); - - isc__nm_failed_read_cb(sock, ISC_R_EOF, false); -} - -/* Zone transfers/updates over TLS are allowed only when "dot" ALPN - * was negotiated. - * - * Per the XoT spec, we must also check that the TLS version is >= - * 1.3. The check could be added here. However, we still need to - * support platforms where no cryptographic library with TLSv1.3 - * support is available. As a result of this we cannot be too strict - * regarding the minimal TLS protocol version in order to make it - * possible to do encrypted zone transfers over TLSv1.2, as it would - * not be right to leave users on these platforms without means for - * encrypted zone transfers using BIND only. - * - * The ones requiring strict compatibility with the specification - * could disable TLSv1.2 in the configuration file. - */ -bool -isc__nm_tlsdns_xfr_allowed(isc_nmsocket_t *sock) { - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->type == isc_nm_tlsdnssocket); - - return (sock->tls.alpn_negotiated); -} - -const char * -isc__nm_tlsdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - REQUIRE(handle->sock->type == isc_nm_tlsdnssocket); - - sock = handle->sock; - if (sock->tls.tls == NULL) { - return (sock->tls.tls_verify_errmsg); - } - - return (isc_tls_verify_peer_result_string(sock->tls.tls)); -} - -void -isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx, - const int tid) { - REQUIRE(tid >= 0); - - isc_tlsctx_free(&listener->children[tid].tls.ctx); - isc_tlsctx_attach(tlsctx, &listener->children[tid].tls.ctx); -} - -void -isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock) { - if (sock->type == isc_nm_tlsdnslistener || - sock->type == isc_nm_tlsdnssocket) - { - if (sock->tls.client_sess_cache != NULL) { - INSIST(atomic_load(&sock->client)); - INSIST(sock->type == isc_nm_tlsdnssocket); - isc_tlsctx_client_session_cache_detach( - &sock->tls.client_sess_cache); - } - if (sock->tls.ctx != NULL) { - INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs)); - isc_tlsctx_free(&sock->tls.ctx); - } - } -} - -static void -tlsdns_keep_client_tls_session(isc_nmsocket_t *sock) { - /* - * Ensure that the isc_tls_t is being accessed from - * within the worker thread the socket is bound to. - */ - REQUIRE(sock->tid == isc_tid()); - if (sock->tls.client_sess_cache != NULL && - sock->tls.client_session_saved == false) - { - INSIST(atomic_load(&sock->client)); - isc_tlsctx_client_session_cache_keep_sockaddr( - sock->tls.client_sess_cache, &sock->peer, - sock->tls.tls); - sock->tls.client_session_saved = true; - } -} From 56732ac2a0c38e726c2beba4e8acf33226bf7739 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Mon, 5 Dec 2022 20:19:03 +0200 Subject: [PATCH 26/33] TLS: try to avoid allocating send request objects This commit optimises TLS send request object allocation to enable send request object reuse, somewhat reducing pressure on the memory manager. It is especially helpful in the case when Stream DNS uses the TLS implementation as the transport. --- lib/isc/netmgr/netmgr-int.h | 1 + lib/isc/netmgr/tlsstream.c | 66 ++++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 4fe9e1c0c5..bd8d844989 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -878,6 +878,7 @@ struct isc_nmsocket { } state; /*%< The order of these is significant */ size_t nsending; bool tcp_nodelay_value; + isc_nmsocket_tls_send_req_t *send_req; /*%< Send req to reuse */ } tlsstream; #if HAVE_LIBNGHTTP2 diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 05d18e30bf..67298cf8eb 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -131,6 +131,9 @@ tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { (isc_nmsocket_tls_send_req_t *)cbarg; isc_nmsocket_t *tlssock = NULL; bool finish = send_req->finish; + isc_nm_cb_t send_cb = NULL; + void *send_cbarg = NULL; + isc_nmhandle_t *send_handle = NULL; REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); @@ -138,34 +141,52 @@ tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { tlssock = send_req->tlssock; send_req->tlssock = NULL; + send_cb = send_req->cb; + send_cbarg = send_req->cbarg; + send_handle = send_req->handle; + send_req->handle = NULL; if (finish) { tls_try_shutdown(tlssock->tlsstream.tls, true); } - if (send_req->cb != NULL) { - INSIST(VALID_NMHANDLE(tlssock->statichandle)); - send_req->cb(send_req->handle, eresult, send_req->cbarg); - isc_nmhandle_detach(&send_req->handle); - /* The last handle has been just detached: close the underlying - * socket. */ - if (tlssock->statichandle == NULL) { - finish = true; - } - } - - /* We are tying to avoid a memory allocation for small write + /* + * We are tying to avoid a memory allocation for small write * requests. See the mirroring code in the tls_send_outgoing() - * function. */ + * function. The object is attempted to be freed or put for reuse + * before the call to callback because there is a chance that it + * is going to be reused during the call to the callback. + */ if (send_req->data.length > sizeof(send_req->smallbuf)) { isc_mem_put(handle->sock->worker->mctx, send_req->data.base, send_req->data.length); } else { INSIST(&send_req->smallbuf[0] == send_req->data.base); } - isc_mem_put(handle->sock->worker->mctx, send_req, sizeof(*send_req)); + + send_req->data.base = NULL; + send_req->data.length = 0; + + /* Try to keep the object to be reused later - to avoid an allocation */ + if (tlssock->tlsstream.send_req == NULL) { + tlssock->tlsstream.send_req = send_req; + } else { + isc_mem_put(handle->sock->worker->mctx, send_req, + sizeof(*send_req)); + } tlssock->tlsstream.nsending--; + if (send_cb != NULL) { + INSIST(VALID_NMHANDLE(tlssock->statichandle)); + send_cb(send_handle, eresult, send_cbarg); + isc_nmhandle_detach(&send_handle); + /* The last handle has been just detached: close the underlying + * socket. */ + if (tlssock->statichandle == NULL) { + finish = true; + } + } + if (finish && eresult == ISC_R_SUCCESS && tlssock->reading) { tls_failed_read_cb(tlssock, ISC_R_EOF); } else if (eresult == ISC_R_SUCCESS) { @@ -277,7 +298,14 @@ tls_send_outgoing(isc_nmsocket_t *sock, bool finish, isc_nmhandle_t *tlshandle, pending = TLS_BUF_SIZE; } - send_req = isc_mem_get(sock->worker->mctx, sizeof(*send_req)); + /* Try to reuse previously allocated object */ + if (sock->tlsstream.send_req != NULL) { + send_req = sock->tlsstream.send_req; + sock->tlsstream.send_req = NULL; + } else { + send_req = isc_mem_get(sock->worker->mctx, sizeof(*send_req)); + } + *send_req = (isc_nmsocket_tls_send_req_t){ .finish = finish, .data.length = pending }; @@ -1181,6 +1209,14 @@ isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) { isc_tlsctx_client_session_cache_detach( &sock->tlsstream.client_sess_cache); } + + if (sock->tlsstream.send_req != NULL) { + INSIST(sock->tlsstream.send_req->data.base == NULL); + INSIST(sock->tlsstream.send_req->data.length == 0); + isc_mem_put(sock->worker->mctx, + sock->tlsstream.send_req, + sizeof(*sock->tlsstream.send_req)); + } } else if (sock->type == isc_nm_tcpsocket && sock->tlsstream.tlssocket != NULL) { From ad876a65af4120f6f0cae3c328bf19aa06ba8166 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 7 Dec 2022 13:33:52 +0200 Subject: [PATCH 27/33] Add isc__nm_senddns() The new internal function works in the same way as isc_nm_send() except that it sends a DNS message size ahead of the DNS message data (the format used in DNS over TCP). The intention is to provide a fast path for sending DNS messages over streams protocols - that is, without allocating any intermediate memory buffers. --- lib/isc/netmgr/netmgr-int.h | 16 ++++++++ lib/isc/netmgr/netmgr.c | 14 +++++++ lib/isc/netmgr/tcp.c | 77 +++++++++++++++++++++++++++++-------- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index bd8d844989..a57222c367 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1337,6 +1337,14 @@ isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0); * stoplisten, send, read, pause, close). */ +void +isc__nm_tcp_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); +/*%< + * The same as 'isc__nm_tcp_send()', but with data length sent + * ahead of data (two bytes (16 bit) in big-endian format). + */ + void isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0); @@ -1993,3 +2001,11 @@ isc__nmhandle_get_selected_alpn(isc_nmhandle_t *handle, * not negotiated of the underlying protocol of the connection * represented via the given handle does not support ALPN. */ + +void +isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg); +/*%< + * The same as 'isc_nm_send()', but with data length sent + * ahead of data (two bytes (16 bit) in big-endian format). + */ diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index eb10ce67f2..80bcca310d 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1873,6 +1873,20 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, } } +void +isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, + void *cbarg) { + REQUIRE(VALID_NMHANDLE(handle)); + + switch (handle->sock->type) { + case isc_nm_tcpsocket: + isc__nm_tcp_senddns(handle, region, cb, cbarg); + break; + default: + UNREACHABLE(); + } +} + void isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { REQUIRE(VALID_NMHANDLE(handle)); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index c994622d0c..cd49da70b4 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -996,9 +996,9 @@ failure: return (result); } -void -isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, - isc_nm_cb_t cb, void *cbarg) { +static void +tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, + void *cbarg, const bool dnsmsg) { REQUIRE(VALID_NMHANDLE(handle)); REQUIRE(VALID_NMSOCK(handle->sock)); @@ -1011,6 +1011,9 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, REQUIRE(sock->tid == isc_tid()); uvreq = isc__nm_uvreq_get(sock->worker, sock); + if (dnsmsg) { + *(uint16_t *)uvreq->tcplen = htons(region->length); + } uvreq->uvbuf.base = (char *)region->base; uvreq->uvbuf.len = region->length; @@ -1034,6 +1037,18 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, return; } +void +isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tcp_send(handle, region, cb, cbarg, false); +} + +void +isc__nm_tcp_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tcp_send(handle, region, cb, cbarg, true); +} + static void tcp_send_cb(uv_write_t *req, int status) { isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; @@ -1065,27 +1080,59 @@ tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { REQUIRE(sock->type == isc_nm_tcpsocket); int r; + uv_buf_t bufs[2] = { { 0 }, { 0 } }; /* ugly, but required for old GCC + versions */ + size_t nbufs = 1; if (isc__nmsocket_closing(sock)) { return (ISC_R_CANCELED); } - uv_buf_t uvbuf = { .base = req->uvbuf.base, .len = req->uvbuf.len }; + /* Check if we are not trying to send a DNS message */ + if (*(uint16_t *)req->tcplen == 0) { + bufs[0].base = req->uvbuf.base; + bufs[0].len = req->uvbuf.len; - r = uv_try_write(&sock->uv_handle.stream, &uvbuf, 1); + r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); - if (r == (int)(uvbuf.len)) { - /* Wrote everything */ - isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); - return (ISC_R_SUCCESS); - } else if (r > 0) { - uvbuf.base += (size_t)r; - uvbuf.len -= (size_t)r; - } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { - return (isc_uverr2result(r)); + if (r == (int)(bufs[0].len)) { + /* Wrote everything */ + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); + return (ISC_R_SUCCESS); + } else if (r > 0) { + bufs[0].base += (size_t)r; + bufs[0].len -= (size_t)r; + } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { + return (isc_uverr2result(r)); + } + } else { + nbufs = 2; + bufs[0].base = req->tcplen; + bufs[0].len = 2; + bufs[1].base = req->uvbuf.base; + bufs[1].len = req->uvbuf.len; + + r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); + + if (r == (int)(bufs[0].len + bufs[1].len)) { + /* Wrote everything */ + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); + return (ISC_R_SUCCESS); + } else if (r == 1) { + /* Partial write of DNSMSG length */ + bufs[0].base = req->tcplen + 1; + bufs[0].len = 1; + } else if (r > 0) { + /* Partial write of DNSMSG */ + nbufs = 1; + bufs[0].base = req->uvbuf.base + (r - 2); + bufs[0].len = req->uvbuf.len - (r - 2); + } else if (!(r == UV_ENOSYS || r == UV_EAGAIN)) { + return (isc_uverr2result(r)); + } } - r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &uvbuf, 1, + r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, tcp_send_cb); if (r < 0) { return (isc_uverr2result(r)); From cb6f3dc3c823f5e82d537fa40bc9d9514a446e59 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 7 Dec 2022 14:18:33 +0200 Subject: [PATCH 28/33] TLS: isc__nm_senddns() support This commit adds support for isc_nm_senddns() to the generic TLS code. --- lib/isc/netmgr/netmgr-int.h | 8 +++++ lib/isc/netmgr/netmgr.c | 3 ++ lib/isc/netmgr/tlsstream.c | 60 ++++++++++++++++++++++++++++++++----- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index a57222c367..f71d74fb5a 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1444,6 +1444,14 @@ isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, * Back-end implementation of isc_nm_send() for TLSDNS handles. */ +void +isc__nm_tls_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg); +/*%< + * The same as 'isc__nm_tls_send()', but with data length sent + * ahead of data (two bytes (16 bit) in big-endian format). + */ + void isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg); /*%< diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 80bcca310d..24caf453da 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1882,6 +1882,9 @@ isc__nm_senddns(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, case isc_nm_tcpsocket: isc__nm_tcp_senddns(handle, region, cb, cbarg); break; + case isc_nm_tlssocket: + isc__nm_tls_senddns(handle, region, cb, cbarg); + break; default: UNREACHABLE(); } diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 67298cf8eb..8a33711255 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -542,10 +542,41 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, bool sent_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) & SSL_SENT_SHUTDOWN) != 0); - rv = SSL_write_ex(sock->tlsstream.tls, - send_data->uvbuf.base, - send_data->uvbuf.len, &len); - if (rv != 1 || len != send_data->uvbuf.len) { + bool write_failed = false; + if (*(uint16_t *)send_data->tcplen != 0) { + /* + * There is a DNS message length to write - do + * it. + */ + rv = SSL_write_ex( + sock->tlsstream.tls, send_data->tcplen, + sizeof(send_data->tcplen), &len); + if (rv != 1 || len != sizeof(send_data->tcplen)) + { + write_failed = true; + } else { + /* Write data */ + rv = SSL_write_ex(sock->tlsstream.tls, + send_data->uvbuf.base, + send_data->uvbuf.len, + &len); + if (rv != 1 || + len != send_data->uvbuf.len) + { + write_failed = true; + } + } + } else { + /* Write data only */ + rv = SSL_write_ex(sock->tlsstream.tls, + send_data->uvbuf.base, + send_data->uvbuf.len, &len); + if (rv != 1 || len != send_data->uvbuf.len) { + write_failed = true; + } + } + + if (write_failed) { result = received_shutdown || sent_shutdown ? ISC_R_CANCELED : ISC_R_TLSERROR; @@ -931,9 +962,9 @@ done: return; } -void -isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, - isc_nm_cb_t cb, void *cbarg) { +static void +tls_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, + void *cbarg, const bool dnsmsg) { isc__netievent_tlssend_t *ievent = NULL; isc__nm_uvreq_t *uvreq = NULL; isc_nmsocket_t *sock = NULL; @@ -951,6 +982,9 @@ isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, uvreq->cbarg = cbarg; uvreq->uvbuf.base = (char *)region->base; uvreq->uvbuf.len = region->length; + if (dnsmsg) { + *(uint16_t *)uvreq->tcplen = htons(region->length); + } /* * We need to create an event and pass it using async channel @@ -959,6 +993,18 @@ isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, isc__nm_enqueue_ievent(sock->worker, (isc__netievent_t *)ievent); } +void +isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tls_send(handle, region, cb, cbarg, false); +} + +void +isc__nm_tls_senddns(isc_nmhandle_t *handle, const isc_region_t *region, + isc_nm_cb_t cb, void *cbarg) { + tls_send(handle, region, cb, cbarg, true); +} + void isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) { isc_nmsocket_t *sock = NULL; From 0a7e83feeaa23cfac3e3894527b175b123118c7e Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 7 Dec 2022 14:32:20 +0200 Subject: [PATCH 29/33] StreamDNS: Use isc__nm_senddns() to send DNS messages This commit modifies the Stream DNS message so that it uses the optimised code path (isc__nm_senddns()) for sending DNS messages over the underlying transport. This way we avoid allocating any intermediate memory buffers needed to render a DNS message with its length pre-pended ahead of the contents (TCP DNS message format). --- lib/isc/netmgr/streamdns.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c index c72697c8f3..fe8f2512bc 100644 --- a/lib/isc/netmgr/streamdns.c +++ b/lib/isc/netmgr/streamdns.c @@ -78,12 +78,11 @@ typedef struct streamdns_send_req { isc_nm_cb_t cb; /* send callback */ void *cbarg; /* send callback argument */ isc_nmhandle_t *dnshandle; /* Stream DNS socket handle */ - isc_dnsbuffer_t data; /* buffer that contains the DNS message to send */ } streamdns_send_req_t; static streamdns_send_req_t * streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, - isc__nm_uvreq_t *req, isc_region_t *data); + isc__nm_uvreq_t *req); static void streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, @@ -549,7 +548,7 @@ streamdns_try_close_unused(isc_nmsocket_t *sock) { static streamdns_send_req_t * streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, - isc__nm_uvreq_t *req, isc_region_t *data) { + isc__nm_uvreq_t *req) { streamdns_send_req_t *send_req; if (sock->streamdns.send_req != NULL) { @@ -563,7 +562,6 @@ streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, /* Allocate a new object. */ send_req = isc_mem_get(mctx, sizeof(*send_req)); *send_req = (streamdns_send_req_t){ 0 }; - isc_dnsbuffer_init(&send_req->data, mctx); } /* Initialise the send request object */ @@ -571,14 +569,6 @@ streamdns_get_send_req(isc_nmsocket_t *sock, isc_mem_t *mctx, send_req->cbarg = req->cbarg; isc_nmhandle_attach(req->handle, &send_req->dnshandle); - /* Prepare the message */ - /* 1. Add the message length at the very beginning of the message */ - isc_dnsbuffer_putmem_uint16be(&send_req->data, - (uint16_t)req->uvbuf.len); - /* 2. Append the data itself */ - isc_dnsbuffer_putmem(&send_req->data, req->uvbuf.base, req->uvbuf.len); - isc_dnsbuffer_remainingregion(&send_req->data, data); - sock->streamdns.nsending++; return (send_req); @@ -596,7 +586,6 @@ streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, sock->streamdns.nsending--; isc_nmhandle_detach(&send_req->dnshandle); if (sock->streamdns.send_req == NULL) { - isc_dnsbuffer_clear(&send_req->data); sock->streamdns.send_req = send_req; /* * An object has been recycled, @@ -606,7 +595,6 @@ streamdns_put_send_req(isc_mem_t *mctx, streamdns_send_req_t *send_req, } } - isc_dnsbuffer_uninit(&send_req->data); isc_mem_put(mctx, send_req, sizeof(*send_req)); } @@ -917,9 +905,11 @@ isc__nm_async_streamdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { mctx = sock->worker->mctx; - send_req = streamdns_get_send_req(sock, mctx, req, &data); - isc_nm_send(sock->outerhandle, &data, streamdns_writecb, - (void *)send_req); + send_req = streamdns_get_send_req(sock, mctx, req); + data.base = (unsigned char *)req->uvbuf.base; + data.length = req->uvbuf.len; + isc__nm_senddns(sock->outerhandle, &data, streamdns_writecb, + (void *)send_req); isc__nm_uvreq_put(&req, sock); return; From daa23840c5fb003eb7420b41abef2b2e07f61483 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 29 Nov 2022 13:03:56 +0200 Subject: [PATCH 30/33] Modify AUTHORS Add "Artem Boldariev" to the list of BIND9 authors. --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 6943d5a575..dd8241789a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,3 +51,4 @@ Anay Panvalkar colleen Robert Edmonds João Damas +Artem Boldariev (Артем Болдарєв) From 7b062961edf395eb3e4f426882975295f71e7a72 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 23 Nov 2022 15:41:21 +0200 Subject: [PATCH 31/33] Modify CHANGES [GL #3374] Mention that DNS over TCP and DNS over TLS transports are superseded. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 810eb9b691..42fea079a5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +6052. [func] Replace DNS over TCP and DNS over TLS transports + code with a new, unified transport implementation. + [GL #3374] + 6051. [bug] Improve thread safety in the dns_dispatch unit. [GL #3178] [GL #3636] From 849d7292ade674fb55c83055bbfe67b202c9913b Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Wed, 23 Nov 2022 15:44:33 +0200 Subject: [PATCH 32/33] Modify release notes [GL #3374] Mention that DNS over TCP and DNS over TLS transports are superseded. --- doc/notes/notes-current.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 4abcdfd95d..ab6c3d0f9b 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -32,6 +32,9 @@ Feature Changes - None. +- Replace DNS over TCP and DNS over TLS transports code with a new, + unified transport implementation. :gl:`#3374` + Bug Fixes ~~~~~~~~~ From 6cb6373b5a158fd6c3a5bfac1375df127e0a0ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Fri, 16 Dec 2022 13:01:18 +0100 Subject: [PATCH 33/33] Convert Stream DNS to use isc_buffer API Drop the whole isc_dnsbuffer API and use new improved isc_buffer API that provides same functionality as the isc_dnsbuffer unit now. --- .gitlab-ci.yml | 1 - lib/isc/Makefile.am | 1 - lib/isc/include/isc/dnsbuffer.h | 446 ------------------------------- lib/isc/include/isc/dnsstream.h | 83 +++--- lib/isc/netmgr/streamdns.c | 36 +-- tests/isc/dnsstream_utils_test.c | 281 ++++++++++--------- 6 files changed, 200 insertions(+), 648 deletions(-) delete mode 100644 lib/isc/include/isc/dnsbuffer.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd97506400..54b25ecd14 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1521,7 +1521,6 @@ gcov: - for SRC in dns isc; do for DST in dns isc ns; do cp -f "lib/${SRC}/include/${SRC}"/*.h "lib/${DST}/"; done; done - find bin lib -maxdepth 1 -mindepth 1 -type d -exec cp -f lib/isc/include/isc/buffer.h "{}" \; - cp -f lib/isc/include/isc/buffer.h lib/isc/netmgr/buffer.h - - cp -f lib/isc/include/isc/dnsbuffer.h lib/isc/netmgr/dnsbuffer.h - cp -f lib/isc/include/isc/dnsstream.h lib/isc/netmgr/dnsstream.h # Help gcovr find dlz_dbi.c file - for DST in ldap mysql mysqldyn sqlite3 wildcard; do cp contrib/dlz/modules/common/dlz_dbi.c "contrib/dlz/modules/${DST}"; done diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am index 4df3574eb7..fabefc6de4 100644 --- a/lib/isc/Makefile.am +++ b/lib/isc/Makefile.am @@ -25,7 +25,6 @@ libisc_la_HEADERS = \ include/isc/crc64.h \ include/isc/deprecated.h \ include/isc/dir.h \ - include/isc/dnsbuffer.h \ include/isc/dnsstream.h \ include/isc/endian.h \ include/isc/entropy.h \ diff --git a/lib/isc/include/isc/dnsbuffer.h b/lib/isc/include/isc/dnsbuffer.h deleted file mode 100644 index c79c7adb44..0000000000 --- a/lib/isc/include/isc/dnsbuffer.h +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * 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. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -#define ISC_DNSBUFFER_STATIC_BUFFER_SIZE (512) -#define ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE (ISC_BUFFER_INCR * 2) - -typedef struct isc_dnsbuffer { - isc_buffer_t *current; /* pointer to the currently used buffer */ - isc_buffer_t stbuf; /* static memory buffer */ - uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE]; /* storage for the static - buffer */ - isc_buffer_t *dynbuf; /* resizeable dynamic memory buffer */ - isc_mem_t *mctx; -} isc_dnsbuffer_t; -/* - * The 'isc_dnsbuffer_t' object implementation is a thin wrapper on - * top of 'isc_buffer_t' which has the following characteristics: - * - * - provides interface specifically atuned for handling/generating - * DNS messages, especially in the format used for DNS messages over - * TCP; - * - * - avoids allocating dynamic memory when handling small DNS - * messages, while transparently switching to using dynamic memory - * when handling larger messages. This approach significantly - * reduces pressure on the memory allocator, as most of the DNS - * messages are small. - */ - -static inline void -isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx); -/*!< - * \brief Initialise the 'isc_dnsbuffer_t' object, keep a reference to - * 'memctx' inside the object. - * - * Requires: - *\li 'dnsbuf' is not NULL; - *\li 'memctx' is not NULL. - */ - -static inline void -isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Un-initialise the 'isc_dnsbuffer_t' object, de-allocate any - *dynamically allocated memory, detach from an internal memory context - * reference. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline isc_dnsbuffer_t * -isc_dnsbuffer_new(isc_mem_t *memctx); -/*!< - * \brief Allocate and initialise a new 'isc_dnsbuffer_t' object, keep a - * reference to 'memctx' inside the object. - * - * Requires: - *\li 'dnsbuf' is not NULL; - *\li 'memctx' is not NULL. - */ - -static inline void -isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf); -/*!< - * \brief Un-initialise and de-allocate the given 'isc_dnsbuffer_t' object. - * - * Requires: - *\li 'pdnsbuf' is not NULL; - *\li 'pdnsbuf' does not point to NULL. - */ - -static inline void -isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Clear the given 'isc_dnsbuffer_t' object (make it empty). - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline unsigned int -isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Return the total length of the internal memory buffer of - * the given 'isc_dnsbuffer_t' object. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline unsigned int -isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Return the total number of used bytes from the internal - * memory buffer of the given 'isc_dnsbuffer_t' object. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline unsigned int -isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Return the total number of remaining (unprocessed data) - * bytes from the internal memory buffer of the given - * 'isc_dnsbuffer_t' object. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf, - isc_region_t *region); -/*!< - * \brief Make the given 'isc_region_t' object reference remaining - * (unprocessed) data from the internal memory buffer of the given - * 'isc_dnsbuffer_t' object. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Compact the internal used memory region of the internal - * memory buffer of the given 'isc_dnsbuffer_t' object so that it - * occurs at the start of the memory buffer. Then used region is - * shrunk by the size of the processed (consumed) region, and the - * consumed region is then made empty. This way the previously - * processed (consumed) amount of memory can be used again without - * resizing/reallocating the buffer. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline bool -isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Compact the internal used memory region of the internal - * memory buffer of the given 'isc_dnsbuffer_t' object in the case - * when the processed (consumed) region is larger or equal in size to - * the unprocessed one. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n); -/*!< - * \brief Consume the given number of bytes from the beginning of - * the unprocessed data region of the given 'isc_dnsbuffer_t' object. - * The call moves the 'current' unprocessed data region pointer by the - * given number of bytes. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf, - const unsigned int buf_size); -/*!< - * \brief Copy 'buf_size' bytes from the location pointed to by - * 'buf' pointer to the end of the unprocessed data region of the - * given 'isc_dnsbuffer_t' object. Resize/reallocate the internal - * memory buffer if it is too small. - * - * Requires: - *\li 'dnsbuf' is not NULL; - *\li 'buf' is not NULL; - *\li 'buf_size' is greater than '0'. - */ - -static inline uint8_t * -isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Return the pointer to the beginning of unprocessed data - * region of the given 'isc_dnsbuffer_t' object ("current pointer"). - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline uint16_t -isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Lookup an unsigned short (16-bit) value in - * big-endian/network byte order at the beginning of unprocessed data - * region of the given 'isc_dnsbuffer_t' object. - * - * If there is not enough data available in the region, '0' will be - * returned. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline uint16_t -isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf); -/*!< - * \brief Read an unsigned short (16-bit) value in - * big-endian/network byte order at the beginning of unprocessed data - * region of the given 'isc_dnsbuffer_t' object. - * - * If there is not enough data available in the region, '0' will be - * returned. - * - * In the case, when the data has been read successfully, the start of - * the unprocessed data region is advanced by two bytes. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf, - const uint16_t v); -/*!< - * \brief Append a given unsigned short (16-bit) value 'v' converted - * into big-endian/network byte order at the end of unprocessed data - * region of the given 'isc_dnsbuffer_t' object. Resize/reallocate the - * internal memory buffer if it is too small to hold the appended data. - * - * Requires: - *\li 'dnsbuf' is not NULL. - */ - -static inline void -isc_dnsbuffer_init(isc_dnsbuffer_t *restrict dnsbuf, isc_mem_t *memctx) { - REQUIRE(dnsbuf != NULL); - REQUIRE(memctx != NULL); - *dnsbuf = (isc_dnsbuffer_t){ .current = &dnsbuf->stbuf }; - isc_buffer_init(&dnsbuf->stbuf, &dnsbuf->buf[0], sizeof(dnsbuf->buf)); - isc_mem_attach(memctx, &dnsbuf->mctx); -} - -static inline void -isc_dnsbuffer_uninit(isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - isc_buffer_clear(&dnsbuf->stbuf); - if (dnsbuf->dynbuf != NULL) { - isc_buffer_free(&dnsbuf->dynbuf); - } - - if (dnsbuf->mctx != NULL) { - isc_mem_detach(&dnsbuf->mctx); - } -} - -static inline isc_dnsbuffer_t * -isc_dnsbuffer_new(isc_mem_t *memctx) { - isc_dnsbuffer_t *newbuf; - - REQUIRE(memctx != NULL); - - newbuf = isc_mem_get(memctx, sizeof(*newbuf)); - isc_dnsbuffer_init(newbuf, memctx); - - return (newbuf); -} - -static inline void -isc_dnsbuffer_free(isc_dnsbuffer_t **restrict pdnsbuf) { - isc_dnsbuffer_t *restrict buf = NULL; - isc_mem_t *memctx = NULL; - REQUIRE(pdnsbuf != NULL && *pdnsbuf != NULL); - - buf = *pdnsbuf; - - isc_mem_attach(buf->mctx, &memctx); - isc_dnsbuffer_uninit(buf); - isc_mem_putanddetach(&memctx, buf, sizeof(*buf)); - - *pdnsbuf = NULL; -} - -static inline void -isc_dnsbuffer_clear(isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - isc_buffer_clear(dnsbuf->current); -} - -static inline unsigned int -isc_dnsbuffer_length(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - return (isc_buffer_length(dnsbuf->current)); -} - -static inline unsigned int -isc_dnsbuffer_usedlength(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - return (isc_buffer_usedlength(dnsbuf->current)); -} - -static inline unsigned int -isc_dnsbuffer_remaininglength(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - return (isc_buffer_remaininglength(dnsbuf->current)); -} - -static inline void -isc_dnsbuffer_remainingregion(const isc_dnsbuffer_t *restrict dnsbuf, - isc_region_t *region) { - REQUIRE(dnsbuf != NULL); - REQUIRE(region != NULL); - isc_buffer_remainingregion(dnsbuf->current, region); -} - -static inline void -isc_dnsbuffer_compact(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - isc_buffer_compact(dnsbuf->current); -} - -static inline bool -isc_dnsbuffer_trycompact(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - if (isc_buffer_consumedlength(dnsbuf->current) >= - isc_dnsbuffer_remaininglength(dnsbuf)) - { - isc_dnsbuffer_compact(dnsbuf); - return (true); - } - - return (false); -} - -static inline void -isc_dnsbuffer_consume(isc_dnsbuffer_t *restrict dnsbuf, const unsigned int n) { - REQUIRE(dnsbuf != NULL); - isc_buffer_forward(dnsbuf->current, n); -} - -static inline void -isc_dnsbuffer_putmem(isc_dnsbuffer_t *restrict dnsbuf, void *buf, - const unsigned int buf_size) { - REQUIRE(dnsbuf != NULL); - REQUIRE(buf != NULL); - REQUIRE(buf_size > 0); - if (!(dnsbuf->current == &dnsbuf->stbuf && - isc_buffer_availablelength(dnsbuf->current) >= buf_size) && - dnsbuf->dynbuf == NULL) - { - isc_region_t remaining = { 0 }; - unsigned int total_size = 0; - - isc_buffer_remainingregion(&dnsbuf->stbuf, &remaining); - total_size = remaining.length + buf_size; - if (total_size < ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE) { - total_size = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE; - } - isc_buffer_allocate(dnsbuf->mctx, &dnsbuf->dynbuf, total_size); - isc_buffer_setautorealloc(dnsbuf->dynbuf, true); - if (remaining.length > 0) { - isc_buffer_putmem(dnsbuf->dynbuf, remaining.base, - remaining.length); - } - - dnsbuf->current = dnsbuf->dynbuf; - } - - isc_buffer_putmem(dnsbuf->current, buf, buf_size); -} - -static inline uint8_t * -isc_dnsbuffer_current(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - return (isc_buffer_current(dnsbuf->current)); -} - -static inline uint16_t -isc__dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) { - uint16_t v; - uint8_t *p = (uint8_t *)isc_dnsbuffer_current(dnsbuf); - - v = p[0] << 8; - v |= p[1] & 0xFF; - - return (v); -} - -static inline uint16_t -isc_dnsbuffer_peek_uint16be(const isc_dnsbuffer_t *restrict dnsbuf) { - REQUIRE(dnsbuf != NULL); - if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) { - return (0); - } - - return (isc__dnsbuffer_peek_uint16be(dnsbuf)); -} - -static inline uint16_t -isc_dnsbuffer_consume_uint16be(isc_dnsbuffer_t *restrict dnsbuf) { - uint16_t v; - - REQUIRE(dnsbuf != NULL); - - if (isc_dnsbuffer_remaininglength(dnsbuf) < sizeof(uint16_t)) { - return (0); - } - - v = isc__dnsbuffer_peek_uint16be(dnsbuf); - - isc_dnsbuffer_consume(dnsbuf, sizeof(uint16_t)); - - return (v); -} - -static inline void -isc_dnsbuffer_putmem_uint16be(isc_dnsbuffer_t *restrict dnsbuf, - const uint16_t v) { - uint8_t b[2] = { 0 }; - - REQUIRE(dnsbuf != NULL); - - b[0] = v >> 8; - b[1] = v & 0xFF; - - isc_dnsbuffer_putmem(dnsbuf, b, sizeof(b)); -} diff --git a/lib/isc/include/isc/dnsstream.h b/lib/isc/include/isc/dnsstream.h index 6e4ede334a..aab4834aea 100644 --- a/lib/isc/include/isc/dnsstream.h +++ b/lib/isc/include/isc/dnsstream.h @@ -12,12 +12,13 @@ */ #pragma once -#include +#include +#include typedef struct isc_dnsstream_assembler isc_dnsstream_assembler_t; /*!< * \brief The 'isc_dnsstream_assembler_t' object is built on top of - * 'isc_dnsbuffer_t' and intended to encapsulate the state machine + * 'isc_buffer_t' and intended to encapsulate the state machine * used for handling DNS messages received in the format used for * messages transmitted over TCP. * @@ -34,7 +35,7 @@ typedef struct isc_dnsstream_assembler isc_dnsstream_assembler_t; * itself makes it trivial to write unit tests for it, leading to * better verification of its correctness. Another important aspect * of its functioning is directly related to the fact that it is built - * on top of 'isc_dnsbuffer_t', which tries to manage memory in a + * on top of 'isc_buffer_t', which tries to manage memory in a * smart way. In particular: * *\li It tries to use a static buffer for smaller messages, reducing @@ -86,9 +87,12 @@ more; message (i.e. someone attempts to send us junk data). */ +#define ISC_DNSSTREAM_STATIC_BUFFER_SIZE (512) + struct isc_dnsstream_assembler { - isc_dnsbuffer_t dnsbuf; /*!< Internal buffer for assembling DNS + isc_buffer_t dnsbuf; /*!< Internal buffer for assembling DNS messages. */ + uint8_t buf[ISC_DNSSTREAM_STATIC_BUFFER_SIZE]; isc_dnsstream_assembler_cb_t onmsg_cb; /*!< Data processing callback. */ void *cbarg; /*!< Callback argument. */ bool calling_cb; /*mctx); - isc_dnsbuffer_init(&dnsasm->dnsbuf, memctx); + + isc_buffer_init(&dnsasm->dnsbuf, dnsasm->buf, sizeof(dnsasm->buf)); + isc_buffer_setmctx(&dnsasm->dnsbuf, dnsasm->mctx); } static inline void @@ -237,7 +243,8 @@ isc_dnsstream_assembler_uninit(isc_dnsstream_assembler_t *restrict dnsasm) { * make any sense. */ INSIST(dnsasm->calling_cb == false); - isc_dnsbuffer_uninit(&dnsasm->dnsbuf); + isc_buffer_clearmctx(&dnsasm->dnsbuf); + isc_buffer_invalidate(&dnsasm->dnsbuf); if (dnsasm->mctx != NULL) { isc_mem_detach(&dnsasm->mctx); } @@ -286,40 +293,52 @@ isc__dnsstream_assembler_handle_message( isc_dnsstream_assembler_t *restrict dnsasm, void *userarg) { bool cont = false; isc_region_t region = { 0 }; + uint16_t dnslen = 0; isc_result_t result; - uint16_t dnslen = isc_dnsbuffer_peek_uint16be(&dnsasm->dnsbuf); INSIST(dnsasm->calling_cb == false); - if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) < sizeof(uint16_t)) { - result = ISC_R_NOMORE; - } else if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) >= - sizeof(uint16_t) && - dnslen == 0) - { - /* - * Someone seems to send us binary junk or output from /dev/zero - */ - result = ISC_R_RANGE; - isc_dnsbuffer_clear(&dnsasm->dnsbuf); - } else if (dnslen <= (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) - + result = isc_buffer_peekuint16(&dnsasm->dnsbuf, &dnslen); + + switch (result) { + case ISC_R_SUCCESS: + if (dnslen == 0) { + /* This didn't make much sense to me: */ + /* isc_buffer_remaininglength(&dnsasm->dnsbuf) >= + * sizeof(uint16_t) && */ + + /* + * Someone seems to send us binary junk or output from + * /dev/zero + */ + result = ISC_R_RANGE; + isc_buffer_clear(&dnsasm->dnsbuf); + break; + } + + if (dnslen > (isc_buffer_remaininglength(&dnsasm->dnsbuf) - sizeof(uint16_t))) - { - result = ISC_R_SUCCESS; - } else { - result = ISC_R_NOMORE; + { + result = ISC_R_NOMORE; + break; + } + break; + case ISC_R_NOMORE: + break; + default: + UNREACHABLE(); } dnsasm->result = result; dnsasm->calling_cb = true; if (result == ISC_R_SUCCESS) { - (void)isc_dnsbuffer_consume_uint16be(&dnsasm->dnsbuf); - isc_dnsbuffer_remainingregion(&dnsasm->dnsbuf, ®ion); + (void)isc_buffer_getuint16(&dnsasm->dnsbuf); + isc_buffer_remainingregion(&dnsasm->dnsbuf, ®ion); region.length = dnslen; cont = dnsasm->onmsg_cb(dnsasm, ISC_R_SUCCESS, ®ion, dnsasm->cbarg, userarg); - if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) >= dnslen) { - isc_dnsbuffer_consume(&dnsasm->dnsbuf, dnslen); + if (isc_buffer_remaininglength(&dnsasm->dnsbuf) >= dnslen) { + isc_buffer_forward(&dnsasm->dnsbuf, dnslen); } } else { cont = false; @@ -342,15 +361,15 @@ isc_dnsstream_assembler_incoming(isc_dnsstream_assembler_t *restrict dnsasm, INSIST(buf == NULL); } else { INSIST(buf != NULL); - isc_dnsbuffer_putmem(&dnsasm->dnsbuf, buf, buf_size); + isc_buffer_putmem(&dnsasm->dnsbuf, buf, buf_size); } while (isc__dnsstream_assembler_handle_message(dnsasm, userarg)) { - if (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf) == 0) { + if (isc_buffer_remaininglength(&dnsasm->dnsbuf) == 0) { break; } } - isc_dnsbuffer_trycompact(&dnsasm->dnsbuf); + isc_buffer_trycompact(&dnsasm->dnsbuf); } static inline isc_result_t @@ -366,13 +385,13 @@ isc_dnsstream_assembler_remaininglength( const isc_dnsstream_assembler_t *restrict dnsasm) { REQUIRE(dnsasm != NULL); - return (isc_dnsbuffer_remaininglength(&dnsasm->dnsbuf)); + return (isc_buffer_remaininglength(&dnsasm->dnsbuf)); } static inline void isc_dnsstream_assembler_clear(isc_dnsstream_assembler_t *restrict dnsasm) { REQUIRE(dnsasm != NULL); - isc_dnsbuffer_clear(&dnsasm->dnsbuf); + isc_buffer_clear(&dnsasm->dnsbuf); dnsasm->result = ISC_R_UNSET; } diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c index fe8f2512bc..2024f5e362 100644 --- a/lib/isc/netmgr/streamdns.c +++ b/lib/isc/netmgr/streamdns.c @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -24,15 +23,14 @@ /* * Stream DNS is a unified transport capable of serving both DNS over * TCP and DNS over TLS. It is built on top of - * 'isc_dnsstream_assembler_t' and 'isc_dnsbuffer_t'. The first one - * is used for assembling DNS messages in the format used for DNS over - * TCP out of incoming data and is built on top of - * 'isc_dnsbuffer_t'. The 'isc_dnbuffer_t' is a thin wrapper on top of - * 'isc_buffer_t' and is optimised for small (>= 512 bytes) DNS - * messages. For small messages it uses a small static memory buffer, - * but it can automatically switch to a dynamically allocated memory - * buffer for larger ones. This way we avoid unnecessary memory - * allocation requests in most cases, as most DNS messages are small. + * 'isc_dnsstream_assembler_t' which is used for assembling DNS + * messages in the format used for DNS over TCP out of incoming data. + * It is built on top of 'isc_buffer_t' optimised for small (>= 512 + * bytes) DNS messages. For small messages it uses a small static + * memory buffer, but it can automatically switch to a larger + * dynamically allocated memory buffer for larger ones. This way we + * avoid unnecessary memory allocation requests in most cases, as most + * DNS messages are small. * * The use of 'isc_dnsstream_assembler_t' allows decoupling DNS * message assembling code from networking code itself, making it @@ -47,22 +45,12 @@ * The writing is done in a simpler manner due to the fact that we * have full control over the data. For each write request we attempt * to allocate a 'streamdns_send_req_t' structure, whose main purpose - * is to keep the data required for the send request processing. An - * 'isc_dnsbuffer_t' object is used as a data storage for the reasons - * described above. + * is to keep the data required for the send request processing. * * When processing write requests there is an important optimisation: * we attempt to reuse 'streamdns_send_req_t' objects again, in order - * to avoid memory allocations when: - * - * a) requesting memory for the new 'streamdns_send_req_t' object; - * - * b) resizing the 'isc_dnsbuffer_t' to fit large messages, should it - * be required. - * - * The last characteristic is important as it allows gradually growing - * the reused send buffer in a lazy manner when transmitting multiple - * DNS messages (e.g. during zone transfers). + * to avoid memory allocations when requesting memory for the new + * 'streamdns_send_req_t' object. * * To understand how sending is done, start by looking at * 'isc__nm_async_streamdnssend()'. Additionally also take a look at @@ -1147,7 +1135,7 @@ isc__nm_streamdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { return (sock->streamdns.tls_verify_error); } - return (false); + return (NULL); } void diff --git a/tests/isc/dnsstream_utils_test.c b/tests/isc/dnsstream_utils_test.c index 17ff422836..61b63e0fb5 100644 --- a/tests/isc/dnsstream_utils_test.c +++ b/tests/isc/dnsstream_utils_test.c @@ -24,6 +24,7 @@ #define UNIT_TESTING #include +#include #include #include #include @@ -34,17 +35,20 @@ #include +#define STATIC_BUFFER_SIZE (512) +#define DYNAMIC_BUFFER_SIZE (STATIC_BUFFER_SIZE + ISC_BUFFER_INCR) + static int setup_test_dnsbuf(void **state) { - isc_dnsbuffer_t **pdnsbuf = (isc_dnsbuffer_t **)state; - *pdnsbuf = isc_dnsbuffer_new(mctx); + isc_buffer_t **pdnsbuf = (isc_buffer_t **)state; + isc_buffer_allocate(mctx, pdnsbuf, STATIC_BUFFER_SIZE); return (0); } static int teardown_test_dnsbuf(void **state) { - isc_dnsbuffer_free((isc_dnsbuffer_t **)state); + isc_buffer_free((isc_buffer_t **)state); return (0); } @@ -77,8 +81,8 @@ teardown_test_dnsasm(void **state) { } ISC_RUN_TEST_IMPL(dnsbuffer_generic_test) { - uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE / 2] = { 0 }; - isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + uint8_t buf[STATIC_BUFFER_SIZE / 2] = { 0 }; + isc_buffer_t *dnsbuf = (isc_buffer_t *)*state; isc_region_t reg = { 0 }; size_t n = 0; @@ -87,88 +91,74 @@ ISC_RUN_TEST_IMPL(dnsbuffer_generic_test) { } /* sanity checks */ - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_STATIC_BUFFER_SIZE); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == 0); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + assert_true(isc_buffer_usedlength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); - assert_true(isc_dnsbuffer_current(dnsbuf) == dnsbuf->buf); - assert_true(dnsbuf->current == &dnsbuf->stbuf); + assert_true(isc_buffer_current(dnsbuf) == dnsbuf->base); - isc_dnsbuffer_clear(dnsbuf); + isc_buffer_clear(dnsbuf); - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_STATIC_BUFFER_SIZE); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == 0); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + assert_true(isc_buffer_usedlength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); - assert_true(dnsbuf->current == &dnsbuf->stbuf); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_current(dnsbuf) == dnsbuf->base); - assert_true(isc_dnsbuffer_current(dnsbuf) == dnsbuf->buf); - assert_true(dnsbuf->current == &dnsbuf->stbuf); - - for (size_t i = 0; i < sizeof(buf); - i++, isc_dnsbuffer_consume(dnsbuf, 1)) + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == i); } - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); - assert_true(isc_dnsbuffer_usedlength(dnsbuf) == sizeof(buf) * 2); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf) * 2); - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); - assert_true(dnsbuf->current == &dnsbuf->stbuf); - - for (size_t i = 0; i < sizeof(buf); - i++, isc_dnsbuffer_consume(dnsbuf, 1)) + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == i); } - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); - for (size_t i = 0; i < sizeof(buf); - i++, isc_dnsbuffer_consume(dnsbuf, 1)) + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == i); } - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); - assert_true(isc_dnsbuffer_trycompact(dnsbuf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + isc_buffer_trycompact(dnsbuf); - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); - isc_dnsbuffer_remainingregion(dnsbuf, ®); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == reg.length); + isc_buffer_remainingregion(dnsbuf, ®); + assert_true(isc_buffer_remaininglength(dnsbuf) == reg.length); assert_true(reg.length == sizeof(buf)); for (size_t i = 0; i < reg.length; i++) { @@ -177,35 +167,33 @@ ISC_RUN_TEST_IMPL(dnsbuffer_generic_test) { assert_true(d == i); } - isc_dnsbuffer_consume(dnsbuf, reg.length); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + isc_buffer_forward(dnsbuf, reg.length); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); - isc_dnsbuffer_clear(dnsbuf); + isc_buffer_clear(dnsbuf); - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); - n = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + n = DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; for (size_t i = 0; i < n; i++) { - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); } - assert_true(isc_dnsbuffer_length(dnsbuf) > - ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE); - assert_true(isc_dnsbuffer_length(dnsbuf) >= n * sizeof(buf)); + assert_true(isc_buffer_length(dnsbuf) > DYNAMIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) >= n * sizeof(buf)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == n * sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == n * sizeof(buf)); for (size_t i = 0; i < n; i++) { for (size_t k = 0; k < sizeof(buf); - k++, isc_dnsbuffer_consume(dnsbuf, 1)) + k++, isc_buffer_forward(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == k); } @@ -213,64 +201,61 @@ ISC_RUN_TEST_IMPL(dnsbuffer_generic_test) { } ISC_RUN_TEST_IMPL(dnsbuffer_resize_alloc_test) { - uint8_t buf[ISC_DNSBUFFER_STATIC_BUFFER_SIZE / 2] = { 0 }; - isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + uint8_t buf[STATIC_BUFFER_SIZE / 2] = { 0 }; + isc_buffer_t *dnsbuf = (isc_buffer_t *)*state; size_t i = 0, n = 0; for (i = 0; i < sizeof(buf); i++) { buf[i] = (uint8_t)i; } - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); for (i = 0; i < (sizeof(buf) / 3) * 2; - i++, isc_dnsbuffer_consume(dnsbuf, 1)) + i++, isc_buffer_forward(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == i); } - assert_true(isc_dnsbuffer_length(dnsbuf) == - ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); - n = ISC_DNSBUFFER_INITIAL_DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + n = DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; for (size_t k = 0; k < n; k++) { - isc_dnsbuffer_putmem(dnsbuf, buf, sizeof(buf)); + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); } - assert_true(isc_dnsbuffer_length(dnsbuf) >= - ISC_DNSBUFFER_STATIC_BUFFER_SIZE); + assert_true(isc_buffer_length(dnsbuf) >= STATIC_BUFFER_SIZE); - for (; i < sizeof(buf); i++, isc_dnsbuffer_consume(dnsbuf, 1)) { - uint8_t *p = isc_dnsbuffer_current(dnsbuf); + for (; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) { + uint8_t *p = isc_buffer_current(dnsbuf); assert_true(*p == i); } } ISC_RUN_TEST_IMPL(dnsbuffer_be_test) { - isc_dnsbuffer_t *dnsbuf = (isc_dnsbuffer_t *)*state; + isc_buffer_t *dnsbuf = (isc_buffer_t *)*state; const uint16_t u16 = 0xBEEF; uint16_t *pu16; + uint16_t u16v; - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); - assert_true(isc_dnsbuffer_peek_uint16be(dnsbuf) == 0); - assert_true(isc_dnsbuffer_consume_uint16be(dnsbuf) == 0); + isc_buffer_putuint16(dnsbuf, u16); - isc_dnsbuffer_putmem_uint16be(dnsbuf, u16); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(u16)); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(u16)); - - pu16 = (uint16_t *)isc_dnsbuffer_current(dnsbuf); + pu16 = (uint16_t *)isc_buffer_current(dnsbuf); assert_true(*pu16 == htons(u16)); - assert_true(isc_dnsbuffer_peek_uint16be(dnsbuf) == u16); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == sizeof(u16)); + assert_int_equal(isc_buffer_peekuint16(dnsbuf, &u16v), ISC_R_SUCCESS); + assert_int_equal(u16v, u16); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(u16)); - assert_true(isc_dnsbuffer_consume_uint16be(dnsbuf) == u16); - assert_true(isc_dnsbuffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_getuint16(dnsbuf) == u16); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); } typedef struct verify_cbdata { @@ -392,17 +377,18 @@ ISC_RUN_TEST_IMPL(dnsasm_sequence_test) { ISC_RUN_TEST_IMPL(dnsasm_multiple_messages_test) { isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; - isc_dnsbuffer_t dnsbuf; + isc_buffer_t dnsbuf; verify_cbdata_t cbdata = { 0 }; size_t verified = 0; - isc_dnsbuffer_init(&dnsbuf, mctx); - isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)response, sizeof(response)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)request_large, - sizeof(request_large)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)response_large, - sizeof(response_large)); + isc_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); + isc_buffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_buffer_putmem(&dnsbuf, (void *)response, sizeof(response)); + isc_buffer_putmem(&dnsbuf, (void *)request_large, + sizeof(request_large)); + isc_buffer_putmem(&dnsbuf, (void *)response_large, + sizeof(response_large)); cbdata.cont_on_success = false; @@ -411,11 +397,12 @@ ISC_RUN_TEST_IMPL(dnsasm_multiple_messages_test) { */ cbdata.verify_message = (uint8_t *)request; isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); - isc_dnsstream_assembler_incoming( - dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), - isc_dnsbuffer_remaininglength(&dnsbuf)); + isc_dnsstream_assembler_incoming(dnsasm, &verified, + isc_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); - isc_dnsbuffer_uninit(&dnsbuf); + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&dnsbuf); assert_true(verified == 1); assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); @@ -473,26 +460,28 @@ ISC_RUN_TEST_IMPL(dnsasm_error_data_test) { isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; verify_cbdata_t cbdata = { 0 }; size_t verified = 0; - isc_dnsbuffer_t dnsbuf; + isc_buffer_t dnsbuf; uint16_t bad_data = 0; - isc_dnsbuffer_init(&dnsbuf, mctx); + isc_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); - isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)response_large, - sizeof(response_large)); + isc_buffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_buffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); + isc_buffer_putmem(&dnsbuf, (void *)&bad_data, sizeof(bad_data)); + isc_buffer_putmem(&dnsbuf, (void *)response_large, + sizeof(response_large)); cbdata.cont_on_success = false; cbdata.verify_message = (uint8_t *)request; isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); - isc_dnsstream_assembler_incoming( - dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), - isc_dnsbuffer_remaininglength(&dnsbuf)); + isc_dnsstream_assembler_incoming(dnsasm, &verified, + isc_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); - isc_dnsbuffer_uninit(&dnsbuf); + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&dnsbuf); assert_true(verified == 1); assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_SUCCESS); @@ -513,7 +502,7 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; verify_cbdata_t cbdata = { 0 }; verify_regions_cbdata_t cbdata_regions = { 0 }; - isc_dnsbuffer_t dnsbuf; + isc_buffer_t dnsbuf; size_t packetno; isc_region_t packets[] = { { (void *)request, sizeof(request) }, @@ -528,10 +517,11 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { }; const size_t npackets = sizeof(packets) / sizeof(packets[0]); - isc_dnsbuffer_init(&dnsbuf, mctx); + isc_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); + for (size_t i = 0; i < npackets; i++) { - isc_dnsbuffer_putmem(&dnsbuf, packets[i].base, - packets[i].length); + isc_buffer_putmem(&dnsbuf, packets[i].base, packets[i].length); } /* process packet by packet */ @@ -540,9 +530,9 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); /* process random amount of data */ - for (; isc_dnsbuffer_remaininglength(&dnsbuf) > 0;) { + for (; isc_buffer_remaininglength(&dnsbuf) > 0;) { size_t sz = 1 + isc_random_uniform( - isc_dnsbuffer_remaininglength(&dnsbuf)); + isc_buffer_remaininglength(&dnsbuf)); for (bool start = true; packetno < npackets; start = false) { cbdata.verify_message = @@ -551,7 +541,7 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { if (start) { isc_dnsstream_assembler_incoming( dnsasm, &packetno, - isc_dnsbuffer_current(&dnsbuf), sz); + isc_buffer_current(&dnsbuf), sz); } else { isc_dnsstream_assembler_incoming( dnsasm, &packetno, NULL, 0); @@ -564,16 +554,15 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { } } - isc_dnsbuffer_consume(&dnsbuf, sz); + isc_buffer_forward(&dnsbuf, sz); } assert_true(packetno == npackets); assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); - assert_true(isc_dnsbuffer_remaininglength(&dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(&dnsbuf) == 0); for (size_t i = 0; i < npackets; i++) { - isc_dnsbuffer_putmem(&dnsbuf, packets[i].base, - packets[i].length); + isc_buffer_putmem(&dnsbuf, packets[i].base, packets[i].length); } /* try to process multiple packets at once, when possible */ @@ -585,44 +574,48 @@ ISC_RUN_TEST_IMPL(dnsasm_torn_randomly_test) { (void *)&cbdata_regions); /* process random amount of data */ - for (; isc_dnsbuffer_remaininglength(&dnsbuf) > 0;) { + for (; isc_buffer_remaininglength(&dnsbuf) > 0;) { size_t sz = 1 + isc_random_uniform( - isc_dnsbuffer_remaininglength(&dnsbuf)); + isc_buffer_remaininglength(&dnsbuf)); isc_dnsstream_assembler_incoming( - dnsasm, &packetno, isc_dnsbuffer_current(&dnsbuf), sz); + dnsasm, &packetno, isc_buffer_current(&dnsbuf), sz); - isc_dnsbuffer_consume(&dnsbuf, sz); + isc_buffer_forward(&dnsbuf, sz); } assert_true(packetno == npackets); assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); - assert_true(isc_dnsbuffer_remaininglength(&dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(&dnsbuf) == 0); - isc_dnsbuffer_uninit(&dnsbuf); + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&dnsbuf); + dnsasm->cbarg = NULL; /* to make GCC happy about dangling pointers */ } ISC_RUN_TEST_IMPL(dnsasm_clear_buffer_within_cb_test) { isc_dnsstream_assembler_t *dnsasm = (isc_dnsstream_assembler_t *)*state; verify_cbdata_t cbdata = { 0 }; size_t verified = 0; - isc_dnsbuffer_t dnsbuf; + isc_buffer_t dnsbuf; - isc_dnsbuffer_init(&dnsbuf, mctx); + isc_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); - isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)&response, sizeof(response)); - isc_dnsbuffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_buffer_putmem(&dnsbuf, (void *)request, sizeof(request)); + isc_buffer_putmem(&dnsbuf, (void *)&response, sizeof(response)); + isc_buffer_putmem(&dnsbuf, (void *)request, sizeof(request)); cbdata.cont_on_success = true; cbdata.clear_on_success = true; cbdata.verify_message = (uint8_t *)request; isc_dnsstream_assembler_setcb(dnsasm, verify_dnsmsg, (void *)&cbdata); - isc_dnsstream_assembler_incoming( - dnsasm, &verified, isc_dnsbuffer_current(&dnsbuf), - isc_dnsbuffer_remaininglength(&dnsbuf)); + isc_dnsstream_assembler_incoming(dnsasm, &verified, + isc_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); - isc_dnsbuffer_uninit(&dnsbuf); + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&dnsbuf); assert_true(verified == 1); assert_true(isc_dnsstream_assembler_result(dnsasm) == ISC_R_UNSET);