diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a1ddc9b67..54b25ecd14 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/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/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 (Артем Болдарєв) 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] diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index b85142e6b4..217dd3e6f8 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 }; @@ -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 */ @@ -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"); 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/bin/tests/test_server.c b/bin/tests/test_server.c index 05a3c0b13e..05acf8430a 100644 --- a/bin/tests/test_server.c +++ b/bin/tests/test_server.c @@ -249,14 +249,14 @@ 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); - 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/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 ~~~~~~~~~ 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 fdf2d03f50..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( @@ -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/isc/Makefile.am b/lib/isc/Makefile.am index e3ead9b465..fabefc6de4 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/dnsstream.h \ include/isc/endian.h \ include/isc/entropy.h \ include/isc/errno.h \ @@ -113,10 +114,10 @@ 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 \ - netmgr/tlsdns.c \ + netmgr/tlsstream.c \ netmgr/udp.c \ aes.c \ ascii.c \ @@ -253,8 +254,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/dnsstream.h b/lib/isc/include/isc/dnsstream.h new file mode 100644 index 0000000000..aab4834aea --- /dev/null +++ b/lib/isc/include/isc/dnsstream.h @@ -0,0 +1,397 @@ +/* + * 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 + +typedef struct isc_dnsstream_assembler isc_dnsstream_assembler_t; +/*!< + * \brief The 'isc_dnsstream_assembler_t' object is built on top of + * '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. + * + * 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_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 + * 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). + */ + +#define ISC_DNSSTREAM_STATIC_BUFFER_SIZE (512) + +struct isc_dnsstream_assembler { + 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_buffer_init(&dnsasm->dnsbuf, dnsasm->buf, sizeof(dnsasm->buf)); + isc_buffer_setmctx(&dnsasm->dnsbuf, dnsasm->mctx); +} + +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_buffer_clearmctx(&dnsasm->dnsbuf); + isc_buffer_invalidate(&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 }; + uint16_t dnslen = 0; + isc_result_t result; + + INSIST(dnsasm->calling_cb == false); + + 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_NOMORE; + break; + } + break; + case ISC_R_NOMORE: + break; + default: + UNREACHABLE(); + } + + dnsasm->result = result; + dnsasm->calling_cb = true; + if (result == ISC_R_SUCCESS) { + (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_buffer_remaininglength(&dnsasm->dnsbuf) >= dnslen) { + isc_buffer_forward(&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_buffer_putmem(&dnsasm->dnsbuf, buf, buf_size); + } + + while (isc__dnsstream_assembler_handle_message(dnsasm, userarg)) { + if (isc_buffer_remaininglength(&dnsasm->dnsbuf) == 0) { + break; + } + } + isc_buffer_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_buffer_remaininglength(&dnsasm->dnsbuf)); +} + +static inline void +isc_dnsstream_assembler_clear(isc_dnsstream_assembler_t *restrict dnsasm) { + REQUIRE(dnsasm != NULL); + + isc_buffer_clear(&dnsasm->dnsbuf); + dnsasm->result = ISC_R_UNSET; +} diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index c9ce9907fe..f672d3e141 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'. @@ -521,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, @@ -537,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, @@ -732,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/include/isc/types.h b/lib/isc/include/isc/types.h index 51a246dfab..cdbe4bc443 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -106,18 +106,16 @@ 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_tlssocket = 1 << 3, + 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_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/http.c b/lib/isc/netmgr/http.c index 7cb69b5f97..4eb5a147b6 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) @@ -2424,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); + (void)isc_nmhandle_set_tcp_nodelay(transphandle, true); } static isc_result_t diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index d467b81d04..f71d74fb5a 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 @@ -256,31 +257,20 @@ 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, netievent_tlsdobio, - netievent_tlsdnsaccept, - netievent_tlsdnsconnect, - netievent_tlsdnsclose, - netievent_tlsdnssend, - netievent_tlsdnsread, - netievent_tlsdnscancel, - netievent_tlsdnscycle, - netievent_tlsdnsshutdown, - netievent_httpclose, netievent_httpsend, netievent_httpendpoints, + netievent_streamdnsclose, + netievent_streamdnssend, + netievent_streamdnsread, + netievent_streamdnscancel, + netievent_connectcb, netievent_readcb, netievent_sendcb, @@ -293,10 +283,6 @@ typedef enum isc__netievent_type { netievent_tcplisten, netievent_tcpstop, - netievent_tcpdnslisten, - netievent_tcpdnsstop, - netievent_tlsdnslisten, - netievent_tlsdnsstop, netievent_detach, } isc__netievent_type; @@ -747,7 +733,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; @@ -758,6 +743,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, @@ -868,34 +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; - -#if HAVE_LIBNGHTTP2 /*% TLS stuff */ struct tlsstream { bool server; @@ -918,10 +877,24 @@ struct isc_nmsocket { TLS_CLOSED } 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 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 @@ -1036,11 +1009,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 @@ -1062,6 +1030,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 +1323,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 @@ -1365,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); @@ -1378,67 +1358,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); @@ -1517,10 +1436,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); - -#if HAVE_LIBNGHTTP2 void isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region, isc_nm_cb_t cb, void *cbarg); @@ -1529,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); /*%< @@ -1563,6 +1486,12 @@ isc__nm_tls_cleartimeout(isc_nmhandle_t *handle); * around. */ +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); @@ -1580,10 +1509,29 @@ 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__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); +void +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); + +#if HAVE_LIBNGHTTP2 + void isc__nm_http_stoplistening(isc_nmsocket_t *sock); @@ -1678,6 +1626,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); @@ -1747,9 +1768,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 @@ -1804,15 +1826,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); @@ -1841,6 +1854,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); @@ -1854,15 +1872,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); @@ -1891,6 +1900,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); @@ -1900,17 +1914,6 @@ 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); isc__nm_uvreq_t * isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr); @@ -1924,26 +1927,17 @@ 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 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); @@ -1996,3 +1990,30 @@ 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. + */ + +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. + */ + +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 9563f27ef7..24caf453da 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -345,15 +345,14 @@ 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; -#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; default: UNREACHABLE(); break; @@ -451,35 +450,19 @@ 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); - NETIEVENT_CASE(tlsdnsconnect); - NETIEVENT_CASE(tlsdnssend); - NETIEVENT_CASE(tlsdnscancel); - NETIEVENT_CASE(tlsdnsclose); - NETIEVENT_CASE(tlsdnsread); - 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); #endif + NETIEVENT_CASE(streamdnsread); + NETIEVENT_CASE(streamdnssend); + NETIEVENT_CASE(streamdnsclose); + NETIEVENT_CASE(streamdnscancel); + NETIEVENT_CASE(settlsctx); NETIEVENT_CASE(sockstop); @@ -521,26 +504,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); -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); @@ -556,6 +519,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); @@ -692,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); } @@ -710,11 +674,11 @@ 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); if (sock->barrier_initialised) { isc_barrier_destroy(&sock->barrier); @@ -838,16 +802,13 @@ 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); + case isc_nm_streamdnssocket: + isc__nm_streamdns_close(sock); return; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_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; @@ -894,8 +855,7 @@ 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 || (*sockp)->type == isc_nm_httplistener); @@ -921,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) { @@ -968,10 +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_tcpdnssocket: - case isc_nm_tcpdnslistener: - case isc_nm_tlsdnssocket: - case isc_nm_tlsdnslistener: case isc_nm_httpsocket: case isc_nm_httplistener: switch (family) { @@ -991,9 +946,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", @@ -1106,8 +1059,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; } @@ -1156,10 +1107,9 @@ 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); + handle->sock->type == isc_nm_httpsocket || + handle->sock->type == isc_nm_streamdnssocket); } static void @@ -1304,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) { @@ -1388,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); @@ -1395,17 +1329,12 @@ 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; -#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; default: UNREACHABLE(); } @@ -1425,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. */ @@ -1511,6 +1435,17 @@ void isc__nmsocket_timer_restart(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { + case isc_nm_tlssocket: + isc__nmsocket_tls_timer_restart(sock); + return; + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_timer_restart(sock); + return; + default: + break; + } + if (uv_is_closing((uv_handle_t *)&sock->read_timer)) { return; } @@ -1545,6 +1480,15 @@ bool isc__nmsocket_timer_running(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { + case isc_nm_tlssocket: + return (isc__nmsocket_tls_timer_running(sock)); + case isc_nm_streamdnssocket: + return (isc__nmsocket_streamdns_timer_running(sock)); + default: + break; + } + return (uv_is_active((uv_handle_t *)&sock->read_timer)); } @@ -1565,6 +1509,17 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) { REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { + case isc_nm_tlssocket: + isc__nmsocket_tls_timer_stop(sock); + return; + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_timer_stop(sock); + return; + default: + break; + } + /* uv_timer_stop() is idempotent, no need to check if running */ r = uv_timer_stop(&sock->read_timer); @@ -1584,6 +1539,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); @@ -1624,8 +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_tcpdnssocket: - case isc_nm_tlsdnssocket: buf->len = ISC_NETMGR_TCP_RECVBUF_SIZE; break; default: @@ -1656,14 +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_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); - break; default: UNREACHABLE(); } @@ -1690,8 +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_tcpdnssocket: - case isc_nm_tlsdnssocket: r = uv_read_stop(&sock->uv_handle.stream); UV_RUNTIME_CHECK(uv_read_stop, r); break; @@ -1713,92 +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_tcpdnssocket: - return (isc__nm_tcpdns_processbuffer(sock)); - 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)); @@ -1809,10 +1669,13 @@ 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; default: handle->sock->read_timeout = 0; @@ -1832,10 +1695,13 @@ 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; default: handle->sock->read_timeout = timeout; isc__nmsocket_timer_restart(handle->sock); @@ -1855,18 +1721,19 @@ 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) : atomic_load(&netmgr->idle); sock->write_timeout = value ? atomic_load(&netmgr->keepalive) : atomic_load(&netmgr->idle); break; -#if HAVE_LIBNGHTTP2 + case isc_nm_streamdnssocket: + isc__nmhandle_streamdns_keepalive(handle, value); + break; 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; @@ -1990,16 +1857,13 @@ 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); + case isc_nm_streamdnssocket: + isc__nm_streamdns_send(handle, region, cb, cbarg); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_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; @@ -2009,6 +1873,23 @@ 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; + case isc_nm_tlssocket: + isc__nm_tls_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)); @@ -2020,16 +1901,13 @@ 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); + case isc_nm_streamdnssocket: + isc__nm_streamdns_read(handle, cb, cbarg); break; - case isc_nm_tlsdnssocket: - isc__nm_tlsdns_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; @@ -2047,11 +1925,8 @@ 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); + case isc_nm_streamdnssocket: + isc__nm_streamdns_cancelread(handle); break; default: UNREACHABLE(); @@ -2068,11 +1943,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(); } @@ -2086,19 +1959,16 @@ 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; - case isc_nm_tlsdnslistener: - isc__nm_tlsdns_stoplistening(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; @@ -2322,14 +2192,17 @@ 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 - * 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; + case isc_nm_tlssocket: + isc__nmsocket_tls_reset(sock); + return; + case isc_nm_streamdnssocket: + isc__nmsocket_streamdns_reset(sock); + return; default: UNREACHABLE(); break; @@ -2361,16 +2234,8 @@ 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: UNREACHABLE(); @@ -2395,8 +2260,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 */ isc__nmsocket_reset(sock); @@ -2546,8 +2409,9 @@ 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: + case isc_nm_tlssocket: REQUIRE(sock->parent == NULL); isc__nmsocket_reset(sock); return; @@ -2555,10 +2419,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(); @@ -2576,10 +2436,8 @@ 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: + return (isc__nm_streamdns_xfr_allowed(sock)); default: return (false); } @@ -2617,15 +2475,11 @@ 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; break; - case isc_nm_tcpsocket: -#if HAVE_LIBNGHTTP2 case isc_nm_tlssocket: -#endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); break; @@ -2646,15 +2500,14 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle) { REQUIRE(VALID_NMSOCK(handle->sock)); 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: return (isc__nm_http_has_encryption(handle)); #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + return (isc__nm_streamdns_has_encryption(handle)); default: return (false); }; @@ -2671,17 +2524,18 @@ 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; -#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; #endif /* HAVE_LIBNGHTTP2 */ + case isc_nm_streamdnssocket: + return (isc__nm_streamdns_verify_tls_peer_result_string( + handle)); + break; default: break; } @@ -2699,14 +2553,9 @@ 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; -#if HAVE_LIBNGHTTP2 case isc_nm_tlslistener: isc__nm_async_tls_set_tlsctx(listener, tlsctx, tid); break; -#endif /* HAVE_LIBNGHTTP2 */ default: UNREACHABLE(); break; @@ -2744,12 +2593,12 @@ 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); + case isc_nm_streamdnslistener: + isc__nm_streamdns_set_tlsctx(listener, tlsctx); break; default: UNREACHABLE(); @@ -2864,6 +2713,75 @@ 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; + case isc_nm_tlssocket: + isc__nmhandle_tls_set_manual_timer(handle, manual); + return; + default: + break; + }; + + 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) { + case isc_nm_tlssocket: + isc__nmhandle_tls_get_selected_alpn(handle, alpn, alpnlen); + return; + default: + break; + }; +} + +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; + case isc_nm_tlssocket: + result = isc__nmhandle_tls_set_tcp_nodelay(handle, value); + break; + default: + UNREACHABLE(); + break; + }; + + return (result); +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr @@ -2881,22 +2799,18 @@ 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: 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: 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/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); diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c new file mode 100644 index 0000000000..2024f5e362 --- /dev/null +++ b/lib/isc/netmgr/streamdns.c @@ -0,0 +1,1177 @@ +/* + * 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 "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' 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 + * 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. + * + * 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 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 + * '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 */ +} 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); + +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); + (void)isc_nmhandle_set_tcp_nodelay(sock->outerhandle, true); + 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) { + 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 }; + } + + /* Initialise the send request object */ + send_req->cb = req->cb.send; + send_req->cbarg = req->cbarg; + isc_nmhandle_attach(req->handle, &send_req->dnshandle); + + 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) { + sock->streamdns.send_req = send_req; + /* + * An object has been recycled, + * if not - we are going to destroy it. + */ + return; + } + } + + 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); + (void)isc_nmhandle_set_tcp_nodelay(nsock->outerhandle, true); + 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.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; +} + +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 (NULL); +} + +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); +} diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 52e5abac16..cd49da70b4 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); } @@ -994,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)); @@ -1009,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; @@ -1032,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; @@ -1063,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)); @@ -1225,3 +1274,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; +} 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); -} 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; - } -} diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index a1e59b54ad..8a33711255 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 @@ -128,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)); @@ -135,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) { @@ -274,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 }; @@ -458,6 +489,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 +526,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) { @@ -505,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; @@ -523,6 +591,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 +601,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 +640,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 +724,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, @@ -707,6 +801,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; @@ -764,6 +869,9 @@ 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); } @@ -854,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; @@ -874,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 @@ -882,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; @@ -1081,6 +1204,9 @@ 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; error: @@ -1129,6 +1255,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) { @@ -1203,6 +1337,56 @@ 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); + } +} + +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); +} + +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; @@ -1305,3 +1489,58 @@ 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; +} + +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); +} + +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); +} diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index 289c177a8d..e72134c8a7 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -719,19 +719,19 @@ 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), "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..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", @@ -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)) { /* 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); 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..61b63e0fb5 --- /dev/null +++ b/tests/isc/dnsstream_utils_test.c @@ -0,0 +1,647 @@ +/* + * 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 + +#include "dnsstream_utils_test_data.h" + +#include + +#define STATIC_BUFFER_SIZE (512) +#define DYNAMIC_BUFFER_SIZE (STATIC_BUFFER_SIZE + ISC_BUFFER_INCR) + +static int +setup_test_dnsbuf(void **state) { + 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_buffer_free((isc_buffer_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[STATIC_BUFFER_SIZE / 2] = { 0 }; + isc_buffer_t *dnsbuf = (isc_buffer_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_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + assert_true(isc_buffer_usedlength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); + + assert_true(isc_buffer_current(dnsbuf) == dnsbuf->base); + + isc_buffer_clear(dnsbuf); + + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + assert_true(isc_buffer_usedlength(dnsbuf) == 0); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); + + assert_true(isc_buffer_current(dnsbuf) == dnsbuf->base); + + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) + { + uint8_t *p = isc_buffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf)); + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); + assert_true(isc_buffer_usedlength(dnsbuf) == sizeof(buf) * 2); + + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) + { + uint8_t *p = isc_buffer_current(dnsbuf); + + assert_true(*p == i); + } + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); + + for (size_t i = 0; i < sizeof(buf); i++, isc_buffer_forward(dnsbuf, 1)) + { + uint8_t *p = isc_buffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + isc_buffer_trycompact(dnsbuf); + + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + 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++) { + uint8_t d = (uint8_t)reg.base[i]; + + assert_true(d == i); + } + + isc_buffer_forward(dnsbuf, reg.length); + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(buf)); + + isc_buffer_clear(dnsbuf); + + assert_true(isc_buffer_length(dnsbuf) == DYNAMIC_BUFFER_SIZE); + + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + + n = DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + for (size_t i = 0; i < n; i++) { + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + } + + assert_true(isc_buffer_length(dnsbuf) > DYNAMIC_BUFFER_SIZE); + assert_true(isc_buffer_length(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_buffer_forward(dnsbuf, 1)) + { + uint8_t *p = isc_buffer_current(dnsbuf); + + assert_true(*p == k); + } + } +} + +ISC_RUN_TEST_IMPL(dnsbuffer_resize_alloc_test) { + 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_buffer_putmem(dnsbuf, buf, sizeof(buf)); + + for (i = 0; i < (sizeof(buf) / 3) * 2; + i++, isc_buffer_forward(dnsbuf, 1)) + { + uint8_t *p = isc_buffer_current(dnsbuf); + + assert_true(*p == i); + } + + assert_true(isc_buffer_length(dnsbuf) == STATIC_BUFFER_SIZE); + + n = DYNAMIC_BUFFER_SIZE / sizeof(buf) + 1; + for (size_t k = 0; k < n; k++) { + isc_buffer_putmem(dnsbuf, buf, sizeof(buf)); + } + + assert_true(isc_buffer_length(dnsbuf) >= STATIC_BUFFER_SIZE); + + 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_buffer_t *dnsbuf = (isc_buffer_t *)*state; + const uint16_t u16 = 0xBEEF; + uint16_t *pu16; + uint16_t u16v; + + assert_true(isc_buffer_remaininglength(dnsbuf) == 0); + + isc_buffer_putuint16(dnsbuf, u16); + + assert_true(isc_buffer_remaininglength(dnsbuf) == sizeof(u16)); + + pu16 = (uint16_t *)isc_buffer_current(dnsbuf); + assert_true(*pu16 == htons(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_buffer_getuint16(dnsbuf) == u16); + assert_true(isc_buffer_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_buffer_t dnsbuf; + verify_cbdata_t cbdata = { 0 }; + size_t verified = 0; + + 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; + + /* + * 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_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); + + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&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_buffer_t dnsbuf; + uint16_t bad_data = 0; + + isc_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); + + 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_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); + + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&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_buffer_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_buffer_init(&dnsbuf, NULL, 0); + isc_buffer_setmctx(&dnsbuf, mctx); + + for (size_t i = 0; i < npackets; i++) { + isc_buffer_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_buffer_remaininglength(&dnsbuf) > 0;) { + size_t sz = 1 + isc_random_uniform( + isc_buffer_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_buffer_current(&dnsbuf), sz); + } else { + isc_dnsstream_assembler_incoming( + dnsasm, &packetno, NULL, 0); + } + + if (isc_dnsstream_assembler_result(dnsasm) == + ISC_R_NOMORE) + { + break; + } + } + + isc_buffer_forward(&dnsbuf, sz); + } + + assert_true(packetno == npackets); + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); + assert_true(isc_buffer_remaininglength(&dnsbuf) == 0); + + for (size_t i = 0; i < npackets; i++) { + isc_buffer_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_buffer_remaininglength(&dnsbuf) > 0;) { + size_t sz = 1 + isc_random_uniform( + isc_buffer_remaininglength(&dnsbuf)); + + isc_dnsstream_assembler_incoming( + dnsasm, &packetno, isc_buffer_current(&dnsbuf), sz); + + isc_buffer_forward(&dnsbuf, sz); + } + + assert_true(packetno == npackets); + assert_true(isc_dnsstream_assembler_remaininglength(dnsasm) == 0); + assert_true(isc_buffer_remaininglength(&dnsbuf) == 0); + + 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_buffer_t dnsbuf; + + 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, 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_buffer_current(&dnsbuf), + isc_buffer_remaininglength(&dnsbuf)); + + isc_buffer_clearmctx(&dnsbuf); + isc_buffer_invalidate(&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 + }; 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/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/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 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) {