mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-29 05:28:00 +00:00
Change the internal type used for isc_tid unit to isc_tid_t to hide the specific integer type being used for the 'tid'. Internally, the signed integer type is being used. This allows us to have negatively indexed arrays that works both for threads with assigned tid and the threads with unassigned tid. This should be used only in specific situations.
1189 lines
32 KiB
C
1189 lines
32 KiB
C
/*
|
|
* 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 <limits.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/async.h>
|
|
#include <isc/atomic.h>
|
|
#include <isc/result.h>
|
|
#include <isc/thread.h>
|
|
|
|
#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_streamdns_send()'. 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_resume_processing(void *arg);
|
|
|
|
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);
|
|
|
|
/* Restart the timer only if there's a last single active handle */
|
|
isc_nmhandle_t *handle = ISC_LIST_HEAD(sock->active_handles);
|
|
INSIST(handle != NULL);
|
|
if (ISC_LIST_NEXT(handle, active_link) == NULL) {
|
|
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 last_datum = isc_dnsstream_assembler_remaininglength(
|
|
dnsasm) == region->length;
|
|
/*
|
|
* Stop after one message if a client connection.
|
|
*/
|
|
bool stop = sock->client;
|
|
|
|
sock->reading = false;
|
|
if (sock->recv_cb != NULL) {
|
|
if (!sock->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;
|
|
}
|
|
|
|
if (sock->active_handles_max != 0 &&
|
|
(sock->active_handles_cur >= sock->active_handles_max))
|
|
{
|
|
stop = true;
|
|
}
|
|
INSIST(sock->active_handles_cur <= sock->active_handles_max);
|
|
|
|
isc__nmsocket_timer_stop(sock);
|
|
if (stop) {
|
|
streamdns_pauseread(sock, transphandle);
|
|
} else if (last_datum) {
|
|
/*
|
|
* We have processed all data, need to read more.
|
|
* The call also restarts the timer.
|
|
*/
|
|
streamdns_readmore(sock, transphandle);
|
|
} else {
|
|
/*
|
|
* Process more DNS messages in the next loop tick.
|
|
*/
|
|
streamdns_pauseread(sock, transphandle);
|
|
isc_async_run(sock->worker->loop, streamdns_resume_processing,
|
|
sock);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* 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_mempool_get(worker->nmsocket_pool);
|
|
isc__nmsocket_init(sock, worker, type, addr, NULL);
|
|
sock->result = ISC_R_UNSET;
|
|
if (type == isc_nm_streamdnssocket) {
|
|
sock->read_timeout = isc_nm_getinitialtimeout(worker->netmgr);
|
|
sock->client = !is_server;
|
|
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) {
|
|
sock->connecting = false;
|
|
INSIST(sock->connect_cb != NULL);
|
|
sock->connect_cb(handle, result, sock->connect_cbarg);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc__nmsocket_clearcb(handle->sock);
|
|
} else {
|
|
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);
|
|
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);
|
|
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 *tlsctx,
|
|
const char *sni_hostname,
|
|
isc_tlsctx_client_session_cache_t *client_sess_cache,
|
|
isc_nm_proxy_type_t proxy_type,
|
|
isc_nm_proxyheader_info_t *proxy_info) {
|
|
isc_nmsocket_t *nsock = NULL;
|
|
isc__networker_t *worker = NULL;
|
|
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
worker = &mgr->workers[isc_tid()];
|
|
|
|
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;
|
|
|
|
switch (proxy_type) {
|
|
case ISC_NM_PROXY_NONE:
|
|
if (tlsctx == 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, tlsctx, sni_hostname, client_sess_cache,
|
|
nsock->connect_timeout, false, proxy_info);
|
|
}
|
|
break;
|
|
case ISC_NM_PROXY_PLAIN:
|
|
if (tlsctx == NULL) {
|
|
isc_nm_proxystreamconnect(mgr, local, peer,
|
|
streamdns_transport_connected,
|
|
nsock, nsock->connect_timeout,
|
|
NULL, NULL, NULL, proxy_info);
|
|
} else {
|
|
isc_nm_tlsconnect(
|
|
mgr, local, peer, streamdns_transport_connected,
|
|
nsock, tlsctx, sni_hostname, client_sess_cache,
|
|
nsock->connect_timeout, true, proxy_info);
|
|
}
|
|
break;
|
|
case ISC_NM_PROXY_ENCRYPTED:
|
|
INSIST(tlsctx != NULL);
|
|
isc_nm_proxystreamconnect(
|
|
mgr, local, peer, streamdns_transport_connected, nsock,
|
|
nsock->connect_timeout, tlsctx, sni_hostname,
|
|
client_sess_cache, proxy_info);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(result != ISC_R_SUCCESS);
|
|
|
|
/* Nobody is reading from the socket yet */
|
|
if (sock->recv_handle == NULL) {
|
|
goto destroy;
|
|
}
|
|
|
|
if (sock->client && result == ISC_R_TIMEDOUT) {
|
|
if (sock->recv_cb != NULL) {
|
|
isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
|
|
isc__nm_readcb(sock, req, ISC_R_TIMEDOUT, false);
|
|
}
|
|
|
|
if (isc__nmsocket_timer_running(sock)) {
|
|
/* Timer was restarted, bail-out */
|
|
return;
|
|
}
|
|
|
|
isc__nmsocket_clearcb(sock);
|
|
|
|
goto destroy;
|
|
}
|
|
|
|
isc_dnsstream_assembler_clear(sock->streamdns.input);
|
|
|
|
/* Nobody expects the callback if isc_nm_read() wasn't called */
|
|
if (!sock->client || sock->reading) {
|
|
sock->reading = 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);
|
|
}
|
|
|
|
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.
|
|
*/
|
|
/* FIXME: call failed_read_cb(?) */
|
|
if (sock->outerhandle != NULL) {
|
|
isc_nmhandle_detach(&sock->outerhandle);
|
|
}
|
|
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(!sock->client);
|
|
|
|
if (streamdns_closing(sock)) {
|
|
return;
|
|
}
|
|
|
|
if (sock->active_handles_max != 0 &&
|
|
(sock->active_handles_cur >= sock->active_handles_max))
|
|
{
|
|
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;
|
|
isc_tid_t tid = isc_tid();
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
if (isc__nm_closing(handle->sock->worker)) {
|
|
return ISC_R_SHUTTINGDOWN;
|
|
} else if (result != ISC_R_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
REQUIRE(VALID_NMSOCK(listensock));
|
|
REQUIRE(listensock->type == isc_nm_streamdnslistener);
|
|
|
|
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;
|
|
nsock->read_timeout =
|
|
isc_nm_getinitialtimeout(handle->sock->worker->netmgr);
|
|
nsock->accepting = true;
|
|
nsock->active = true;
|
|
|
|
isc__nmsocket_attach(handle->sock, &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);
|
|
nsock->closed = true;
|
|
goto exit;
|
|
}
|
|
|
|
nsock->closehandle_cb = streamdns_resume_processing;
|
|
isc__nmhandle_set_manual_timer(nsock->outerhandle, true);
|
|
/* settimeout restarts the timer */
|
|
isc_nmhandle_settimeout(
|
|
nsock->outerhandle,
|
|
isc_nm_getinitialtimeout(nsock->worker->netmgr));
|
|
(void)isc_nmhandle_set_tcp_nodelay(nsock->outerhandle, true);
|
|
streamdns_handle_incoming_data(nsock, nsock->outerhandle, NULL, 0);
|
|
|
|
exit:
|
|
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_nm_proxy_type_t proxy_type, isc_nmsocket_t **sockp) {
|
|
isc_result_t result = ISC_R_FAILURE;
|
|
isc_nmsocket_t *listener = NULL;
|
|
isc__networker_t *worker = NULL;
|
|
|
|
REQUIRE(VALID_NM(mgr));
|
|
REQUIRE(isc_tid() == 0);
|
|
|
|
worker = &mgr->workers[isc_tid()];
|
|
|
|
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;
|
|
|
|
switch (proxy_type) {
|
|
case ISC_NM_PROXY_NONE:
|
|
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, false,
|
|
&listener->outer);
|
|
}
|
|
break;
|
|
case ISC_NM_PROXY_PLAIN:
|
|
if (tlsctx == NULL) {
|
|
result = isc_nm_listenproxystream(
|
|
mgr, workers, iface, streamdns_accept_cb,
|
|
listener, backlog, quota, NULL,
|
|
&listener->outer);
|
|
} else {
|
|
result = isc_nm_listentls(mgr, workers, iface,
|
|
streamdns_accept_cb, listener,
|
|
backlog, quota, tlsctx, true,
|
|
&listener->outer);
|
|
}
|
|
break;
|
|
case ISC_NM_PROXY_ENCRYPTED:
|
|
INSIST(tlsctx != NULL);
|
|
result = isc_nm_listenproxystream(
|
|
mgr, workers, iface, streamdns_accept_cb, listener,
|
|
backlog, quota, tlsctx, &listener->outer);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
};
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
listener->closed = true;
|
|
isc__nmsocket_detach(&listener);
|
|
return result;
|
|
}
|
|
|
|
/* copy the actual port we're listening on into sock->iface */
|
|
if (isc_sockaddr_getport(iface) == 0) {
|
|
listener->iface = listener->outer->iface;
|
|
}
|
|
|
|
listener->result = result;
|
|
listener->active = true;
|
|
INSIST(listener->outer->streamdns.listener == NULL);
|
|
listener->nchildren = 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:
|
|
case isc_nm_proxystreamlistener:
|
|
if (sock->streamdns.listener != NULL) {
|
|
isc__nmsocket_detach(&sock->streamdns.listener);
|
|
}
|
|
break;
|
|
case isc_nm_tlssocket:
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_proxystreamsocket:
|
|
if (sock->streamdns.sock != NULL) {
|
|
isc__nmsocket_detach(&sock->streamdns.sock);
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
streamdns_read_cb(void *arg) {
|
|
isc_nmsocket_t *sock = arg;
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
if (streamdns_closing(sock)) {
|
|
streamdns_failed_read_cb(sock, ISC_R_CANCELED, false);
|
|
goto detach;
|
|
}
|
|
|
|
if (sock->streamdns.reading) {
|
|
goto detach;
|
|
}
|
|
|
|
INSIST(VALID_NMHANDLE(sock->outerhandle));
|
|
streamdns_handle_incoming_data(sock, sock->outerhandle, NULL, 0);
|
|
detach:
|
|
isc__nmsocket_detach(&sock);
|
|
}
|
|
|
|
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;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
sock = handle->sock;
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->type == isc_nm_streamdnssocket);
|
|
REQUIRE(sock->recv_handle == handle || sock->recv_handle == NULL);
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
closing = streamdns_closing(sock);
|
|
|
|
sock->recv_cb = cb;
|
|
sock->recv_cbarg = cbarg;
|
|
sock->reading = true;
|
|
if (sock->recv_handle == NULL) {
|
|
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 (!closing && isc_dnsstream_assembler_result(sock->streamdns.input) ==
|
|
ISC_R_UNSET)
|
|
{
|
|
isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
|
|
streamdns_read_cb(sock);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
|
|
isc_job_run(sock->worker->loop, &sock->job, streamdns_read_cb, sock);
|
|
}
|
|
|
|
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;
|
|
streamdns_send_req_t *send_req;
|
|
isc_mem_t *mctx;
|
|
isc_region_t data = { 0 };
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
REQUIRE(region->length <= UINT16_MAX);
|
|
|
|
sock = handle->sock;
|
|
|
|
REQUIRE(sock->type == isc_nm_streamdnssocket);
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
uvreq = isc__nm_uvreq_get(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;
|
|
|
|
if (streamdns_closing(sock)) {
|
|
isc__nm_failed_send_cb(sock, uvreq, ISC_R_CANCELED, true);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
mctx = sock->worker->mctx;
|
|
send_req = streamdns_get_send_req(sock, mctx, uvreq);
|
|
data.base = (unsigned char *)uvreq->uvbuf.base;
|
|
data.length = uvreq->uvbuf.len;
|
|
isc__nm_senddns(sock->outerhandle, &data, streamdns_writecb,
|
|
(void *)send_req);
|
|
|
|
isc__nm_uvreq_put(&uvreq);
|
|
}
|
|
|
|
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__nmsocket_timer_stop(sock);
|
|
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);
|
|
sock->closed = true;
|
|
sock->active = false;
|
|
}
|
|
|
|
void
|
|
isc__nm_streamdns_close(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->type == isc_nm_streamdnssocket);
|
|
REQUIRE(sock->tid == isc_tid());
|
|
REQUIRE(!sock->closing);
|
|
|
|
sock->closing = true;
|
|
|
|
streamdns_close_direct(sock);
|
|
}
|
|
|
|
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__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);
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
isc__nm_streamdns_xfr_checkperm(isc_nmsocket_t *sock) {
|
|
isc_result_t result = ISC_R_NOPERM;
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->type == isc_nm_streamdnssocket);
|
|
|
|
if (sock->outerhandle != NULL) {
|
|
if (isc_nm_has_encryption(sock->outerhandle) &&
|
|
!sock->streamdns.dot_alpn_negotiated)
|
|
{
|
|
result = ISC_R_DOTALPNERROR;
|
|
} else {
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
}
|