When shutting down TCP sockets, the read callback calling logic was flawed, it would call either one less callback or one extra. Fix the logic in the way: 1. When isc_nm_read() has been called but isc_nm_read_stop() hasn't on the handle, the read callback will be called with ISC_R_CANCELED to cancel active reading from the socket/handle. 2. When isc_nm_read() has been called and isc_nm_read_stop() has been called on the on the handle, the read callback will be called with ISC_R_SHUTTINGDOWN to signal that the dormant (not-reading) socket is being shut down. 3. The .reading and .recv_read flags are little bit tricky. The .reading flag indicates if the outer layer is reading the data (that would be uv_tcp_t for TCP and isc_nmsocket_t (TCP) for TLSStream), the .recv_read flag indicates whether somebody is interested in the data read from the socket. Usually, you would expect that the .reading should be false when .recv_read is false, but it gets even more tricky with TLSStream as the TLS protocol might need to read from the socket even when sending data. Fix the usage of the .recv_read and .reading flags in the TLSStream to their true meaning - which mostly consist of using .recv_read everywhere and then wrapping isc_nm_read() and isc_nm_read_stop() with the .reading flag. 4. The TLS failed read helper has been modified to resemble the TCP code as much as possible, clearing and re-setting the .recv_read flag in the TCP timeout code has been fixed and .recv_read is now cleared when isc_nm_read_stop() has been called on the streaming socket. 5. The use of Network Manager in the named_controlconf, isccc_ccmsg, and isc_httpd units have been greatly simplified due to the improved design. 6. More unit tests for TCP and TLS testing the shutdown conditions have been added. Co-authored-by: Ondřej Surý <ondrej@isc.org> Co-authored-by: Artem Boldariev <artem@isc.org>
2550 lines
58 KiB
C
2550 lines
58 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 <assert.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/async.h>
|
|
#include <isc/atomic.h>
|
|
#include <isc/backtrace.h>
|
|
#include <isc/barrier.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/condition.h>
|
|
#include <isc/errno.h>
|
|
#include <isc/job.h>
|
|
#include <isc/list.h>
|
|
#include <isc/log.h>
|
|
#include <isc/loop.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/netmgr.h>
|
|
#include <isc/quota.h>
|
|
#include <isc/random.h>
|
|
#include <isc/refcount.h>
|
|
#include <isc/region.h>
|
|
#include <isc/result.h>
|
|
#include <isc/sockaddr.h>
|
|
#include <isc/stats.h>
|
|
#include <isc/strerr.h>
|
|
#include <isc/thread.h>
|
|
#include <isc/tid.h>
|
|
#include <isc/tls.h>
|
|
#include <isc/util.h>
|
|
#include <isc/uv.h>
|
|
|
|
#include "../loop_p.h"
|
|
#include "netmgr-int.h"
|
|
#include "openssl_shim.h"
|
|
|
|
/*%
|
|
* Shortcut index arrays to get access to statistics counters.
|
|
*/
|
|
|
|
static const isc_statscounter_t udp4statsindex[] = {
|
|
isc_sockstatscounter_udp4open,
|
|
isc_sockstatscounter_udp4openfail,
|
|
isc_sockstatscounter_udp4close,
|
|
isc_sockstatscounter_udp4bindfail,
|
|
isc_sockstatscounter_udp4connectfail,
|
|
isc_sockstatscounter_udp4connect,
|
|
-1,
|
|
-1,
|
|
isc_sockstatscounter_udp4sendfail,
|
|
isc_sockstatscounter_udp4recvfail,
|
|
isc_sockstatscounter_udp4active
|
|
};
|
|
|
|
static const isc_statscounter_t udp6statsindex[] = {
|
|
isc_sockstatscounter_udp6open,
|
|
isc_sockstatscounter_udp6openfail,
|
|
isc_sockstatscounter_udp6close,
|
|
isc_sockstatscounter_udp6bindfail,
|
|
isc_sockstatscounter_udp6connectfail,
|
|
isc_sockstatscounter_udp6connect,
|
|
-1,
|
|
-1,
|
|
isc_sockstatscounter_udp6sendfail,
|
|
isc_sockstatscounter_udp6recvfail,
|
|
isc_sockstatscounter_udp6active
|
|
};
|
|
|
|
static const isc_statscounter_t tcp4statsindex[] = {
|
|
isc_sockstatscounter_tcp4open, isc_sockstatscounter_tcp4openfail,
|
|
isc_sockstatscounter_tcp4close, isc_sockstatscounter_tcp4bindfail,
|
|
isc_sockstatscounter_tcp4connectfail, isc_sockstatscounter_tcp4connect,
|
|
isc_sockstatscounter_tcp4acceptfail, isc_sockstatscounter_tcp4accept,
|
|
isc_sockstatscounter_tcp4sendfail, isc_sockstatscounter_tcp4recvfail,
|
|
isc_sockstatscounter_tcp4active
|
|
};
|
|
|
|
static const isc_statscounter_t tcp6statsindex[] = {
|
|
isc_sockstatscounter_tcp6open, isc_sockstatscounter_tcp6openfail,
|
|
isc_sockstatscounter_tcp6close, isc_sockstatscounter_tcp6bindfail,
|
|
isc_sockstatscounter_tcp6connectfail, isc_sockstatscounter_tcp6connect,
|
|
isc_sockstatscounter_tcp6acceptfail, isc_sockstatscounter_tcp6accept,
|
|
isc_sockstatscounter_tcp6sendfail, isc_sockstatscounter_tcp6recvfail,
|
|
isc_sockstatscounter_tcp6active
|
|
};
|
|
|
|
#if 0
|
|
/* XXX: not currently used */
|
|
static const isc_statscounter_t unixstatsindex[] = {
|
|
isc_sockstatscounter_unixopen,
|
|
isc_sockstatscounter_unixopenfail,
|
|
isc_sockstatscounter_unixclose,
|
|
isc_sockstatscounter_unixbindfail,
|
|
isc_sockstatscounter_unixconnectfail,
|
|
isc_sockstatscounter_unixconnect,
|
|
isc_sockstatscounter_unixacceptfail,
|
|
isc_sockstatscounter_unixaccept,
|
|
isc_sockstatscounter_unixsendfail,
|
|
isc_sockstatscounter_unixrecvfail,
|
|
isc_sockstatscounter_unixactive
|
|
};
|
|
#endif /* if 0 */
|
|
|
|
static void
|
|
nmsocket_maybe_destroy(isc_nmsocket_t *sock FLARG);
|
|
static void
|
|
nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle);
|
|
|
|
/*%<
|
|
* Issue a 'handle closed' callback on the socket.
|
|
*/
|
|
|
|
static void
|
|
shutdown_walk_cb(uv_handle_t *handle, void *arg);
|
|
|
|
static void
|
|
networker_teardown(void *arg) {
|
|
isc__networker_t *worker = arg;
|
|
isc_loop_t *loop = worker->loop;
|
|
|
|
worker->shuttingdown = true;
|
|
|
|
isc__netmgr_log(worker->netmgr, ISC_LOG_DEBUG(1),
|
|
"Shutting down network manager worker on loop %p(%d)",
|
|
loop, isc_tid());
|
|
|
|
uv_walk(&loop->loop, shutdown_walk_cb, NULL);
|
|
|
|
isc__networker_detach(&worker);
|
|
}
|
|
|
|
static void
|
|
netmgr_teardown(void *arg) {
|
|
isc_nm_t *netmgr = (void *)arg;
|
|
|
|
if (atomic_compare_exchange_strong_acq_rel(&netmgr->shuttingdown,
|
|
&(bool){ false }, true))
|
|
{
|
|
isc__netmgr_log(netmgr, ISC_LOG_DEBUG(1),
|
|
"Shutting down network manager");
|
|
}
|
|
}
|
|
|
|
#if HAVE_DECL_UV_UDP_LINUX_RECVERR
|
|
#define MINIMAL_UV_VERSION UV_VERSION(1, 42, 0)
|
|
#elif HAVE_DECL_UV_UDP_MMSG_FREE
|
|
#define MINIMAL_UV_VERSION UV_VERSION(1, 40, 0)
|
|
#elif HAVE_DECL_UV_UDP_RECVMMSG
|
|
#define MAXIMAL_UV_VERSION UV_VERSION(1, 39, 99)
|
|
#define MINIMAL_UV_VERSION UV_VERSION(1, 37, 0)
|
|
#else
|
|
#define MAXIMAL_UV_VERSION UV_VERSION(1, 34, 99)
|
|
#define MINIMAL_UV_VERSION UV_VERSION(1, 34, 0)
|
|
#endif
|
|
|
|
void
|
|
isc_netmgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t **netmgrp) {
|
|
isc_nm_t *netmgr = NULL;
|
|
|
|
#ifdef MAXIMAL_UV_VERSION
|
|
if (uv_version() > MAXIMAL_UV_VERSION) {
|
|
FATAL_ERROR("libuv version too new: running with libuv %s "
|
|
"when compiled with libuv %s will lead to "
|
|
"libuv failures",
|
|
uv_version_string(), UV_VERSION_STRING);
|
|
}
|
|
#endif /* MAXIMAL_UV_VERSION */
|
|
|
|
if (uv_version() < MINIMAL_UV_VERSION) {
|
|
FATAL_ERROR("libuv version too old: running with libuv %s "
|
|
"when compiled with libuv %s will lead to "
|
|
"libuv failures",
|
|
uv_version_string(), UV_VERSION_STRING);
|
|
}
|
|
|
|
netmgr = isc_mem_get(mctx, sizeof(*netmgr));
|
|
*netmgr = (isc_nm_t){
|
|
.loopmgr = loopmgr,
|
|
.nloops = isc_loopmgr_nloops(loopmgr),
|
|
};
|
|
|
|
isc_mem_attach(mctx, &netmgr->mctx);
|
|
isc_mutex_init(&netmgr->lock);
|
|
isc_refcount_init(&netmgr->references, 1);
|
|
atomic_init(&netmgr->maxudp, 0);
|
|
atomic_init(&netmgr->shuttingdown, false);
|
|
atomic_init(&netmgr->recv_tcp_buffer_size, 0);
|
|
atomic_init(&netmgr->send_tcp_buffer_size, 0);
|
|
atomic_init(&netmgr->recv_udp_buffer_size, 0);
|
|
atomic_init(&netmgr->send_udp_buffer_size, 0);
|
|
#if HAVE_SO_REUSEPORT_LB
|
|
netmgr->load_balance_sockets = true;
|
|
#else
|
|
netmgr->load_balance_sockets = false;
|
|
#endif
|
|
|
|
/*
|
|
* Default TCP timeout values.
|
|
* May be updated by isc_nm_tcptimeouts().
|
|
*/
|
|
atomic_init(&netmgr->init, 30000);
|
|
atomic_init(&netmgr->idle, 30000);
|
|
atomic_init(&netmgr->keepalive, 30000);
|
|
atomic_init(&netmgr->advertised, 30000);
|
|
|
|
netmgr->workers =
|
|
isc_mem_get(mctx, netmgr->nloops * sizeof(netmgr->workers[0]));
|
|
|
|
isc_loopmgr_teardown(loopmgr, netmgr_teardown, netmgr);
|
|
|
|
netmgr->magic = NM_MAGIC;
|
|
|
|
for (size_t i = 0; i < netmgr->nloops; i++) {
|
|
isc_loop_t *loop = isc_loop_get(netmgr->loopmgr, i);
|
|
isc__networker_t *worker = &netmgr->workers[i];
|
|
|
|
*worker = (isc__networker_t){
|
|
.recvbuf = isc_mem_get(loop->mctx,
|
|
ISC_NETMGR_RECVBUF_SIZE),
|
|
.active_sockets = ISC_LIST_INITIALIZER,
|
|
};
|
|
|
|
isc_nm_attach(netmgr, &worker->netmgr);
|
|
|
|
isc_mem_attach(loop->mctx, &worker->mctx);
|
|
|
|
isc_mempool_create(worker->mctx, sizeof(isc__nm_uvreq_t),
|
|
&worker->uvreq_pool);
|
|
|
|
isc_loop_attach(loop, &worker->loop);
|
|
isc_loop_teardown(loop, networker_teardown, worker);
|
|
isc_refcount_init(&worker->references, 1);
|
|
}
|
|
|
|
*netmgrp = netmgr;
|
|
}
|
|
|
|
/*
|
|
* Free the resources of the network manager.
|
|
*/
|
|
static void
|
|
nm_destroy(isc_nm_t **mgr0) {
|
|
REQUIRE(VALID_NM(*mgr0));
|
|
|
|
isc_nm_t *mgr = *mgr0;
|
|
*mgr0 = NULL;
|
|
|
|
isc_refcount_destroy(&mgr->references);
|
|
|
|
mgr->magic = 0;
|
|
|
|
if (mgr->stats != NULL) {
|
|
isc_stats_detach(&mgr->stats);
|
|
}
|
|
|
|
isc_mutex_destroy(&mgr->lock);
|
|
|
|
isc_mem_put(mgr->mctx, mgr->workers,
|
|
mgr->nloops * sizeof(mgr->workers[0]));
|
|
isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
|
|
}
|
|
|
|
void
|
|
isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
REQUIRE(dst != NULL && *dst == NULL);
|
|
|
|
isc_refcount_increment(&mgr->references);
|
|
|
|
*dst = mgr;
|
|
}
|
|
|
|
void
|
|
isc_nm_detach(isc_nm_t **mgr0) {
|
|
isc_nm_t *mgr = NULL;
|
|
|
|
REQUIRE(mgr0 != NULL);
|
|
REQUIRE(VALID_NM(*mgr0));
|
|
|
|
mgr = *mgr0;
|
|
*mgr0 = NULL;
|
|
|
|
if (isc_refcount_decrement(&mgr->references) == 1) {
|
|
nm_destroy(&mgr);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_netmgr_destroy(isc_nm_t **netmgrp) {
|
|
isc_nm_t *mgr = NULL;
|
|
|
|
REQUIRE(VALID_NM(*netmgrp));
|
|
|
|
mgr = *netmgrp;
|
|
*netmgrp = NULL;
|
|
|
|
REQUIRE(isc_refcount_decrement(&mgr->references) == 1);
|
|
nm_destroy(&mgr);
|
|
}
|
|
|
|
void
|
|
isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
atomic_store_relaxed(&mgr->maxudp, maxudp);
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
REQUIRE(handle->sock->tid == isc_tid());
|
|
|
|
switch (handle->sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_udpsocket:
|
|
handle->sock->write_timeout = write_timeout;
|
|
break;
|
|
case isc_nm_tlssocket:
|
|
isc__nmhandle_tls_setwritetimeout(handle, write_timeout);
|
|
break;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nmhandle_streamdns_setwritetimeout(handle, write_timeout);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
|
|
uint32_t keepalive, uint32_t advertised) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
atomic_store_relaxed(&mgr->init, init);
|
|
atomic_store_relaxed(&mgr->idle, idle);
|
|
atomic_store_relaxed(&mgr->keepalive, keepalive);
|
|
atomic_store_relaxed(&mgr->advertised, advertised);
|
|
}
|
|
|
|
void
|
|
isc_nm_setnetbuffers(isc_nm_t *mgr, int32_t recv_tcp, int32_t send_tcp,
|
|
int32_t recv_udp, int32_t send_udp) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
atomic_store_relaxed(&mgr->recv_tcp_buffer_size, recv_tcp);
|
|
atomic_store_relaxed(&mgr->send_tcp_buffer_size, send_tcp);
|
|
atomic_store_relaxed(&mgr->recv_udp_buffer_size, recv_udp);
|
|
atomic_store_relaxed(&mgr->send_udp_buffer_size, send_udp);
|
|
}
|
|
|
|
bool
|
|
isc_nm_getloadbalancesockets(isc_nm_t *mgr) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
return (mgr->load_balance_sockets);
|
|
}
|
|
|
|
void
|
|
isc_nm_setloadbalancesockets(isc_nm_t *mgr, ISC_ATTR_UNUSED bool enabled) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
#if HAVE_SO_REUSEPORT_LB
|
|
mgr->load_balance_sockets = enabled;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
|
|
uint32_t *keepalive, uint32_t *advertised) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
|
|
if (initial != NULL) {
|
|
*initial = atomic_load_relaxed(&mgr->init);
|
|
}
|
|
|
|
if (idle != NULL) {
|
|
*idle = atomic_load_relaxed(&mgr->idle);
|
|
}
|
|
|
|
if (keepalive != NULL) {
|
|
*keepalive = atomic_load_relaxed(&mgr->keepalive);
|
|
}
|
|
|
|
if (advertised != NULL) {
|
|
*advertised = atomic_load_relaxed(&mgr->advertised);
|
|
}
|
|
}
|
|
|
|
bool
|
|
isc__nmsocket_active(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
return (sock->active);
|
|
}
|
|
|
|
void
|
|
isc___nmsocket_attach(isc_nmsocket_t *sock, isc_nmsocket_t **target FLARG) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(target != NULL && *target == NULL);
|
|
|
|
isc_nmsocket_t *rsock = NULL;
|
|
|
|
if (sock->parent != NULL) {
|
|
rsock = sock->parent;
|
|
INSIST(rsock->parent == NULL); /* sanity check */
|
|
} else {
|
|
rsock = sock;
|
|
}
|
|
|
|
NETMGR_TRACE_LOG("isc__nmsocket_attach():%p->references = %" PRIuFAST32
|
|
"\n",
|
|
rsock, isc_refcount_current(&rsock->references) + 1);
|
|
|
|
isc_refcount_increment0(&rsock->references);
|
|
|
|
*target = sock;
|
|
}
|
|
|
|
/*
|
|
* Free all resources inside a socket (including its children if any).
|
|
*/
|
|
static void
|
|
nmsocket_cleanup(void *arg) {
|
|
isc_nmsocket_t *sock = arg;
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(!isc__nmsocket_active(sock));
|
|
|
|
isc_nmhandle_t *handle = NULL;
|
|
isc__networker_t *worker = sock->worker;
|
|
|
|
isc_refcount_destroy(&sock->references);
|
|
|
|
isc__nm_decstats(sock, STATID_ACTIVE);
|
|
|
|
REQUIRE(!sock->destroying);
|
|
sock->destroying = true;
|
|
|
|
if (sock->parent == NULL && sock->children != NULL) {
|
|
/*
|
|
* We shouldn't be here unless there are no active handles,
|
|
* so we can clean up and free the children.
|
|
*/
|
|
for (size_t i = 0; i < sock->nchildren; i++) {
|
|
isc_refcount_decrementz(&sock->children[i].references);
|
|
nmsocket_cleanup(&sock->children[i]);
|
|
}
|
|
|
|
/*
|
|
* Now free them.
|
|
*/
|
|
isc_mem_put(sock->worker->mctx, sock->children,
|
|
sock->nchildren * sizeof(*sock));
|
|
sock->children = NULL;
|
|
sock->nchildren = 0;
|
|
}
|
|
|
|
sock->statichandle = NULL;
|
|
|
|
if (sock->outerhandle != NULL) {
|
|
isc_nmhandle_detach(&sock->outerhandle);
|
|
}
|
|
|
|
if (sock->outer != NULL) {
|
|
isc__nmsocket_detach(&sock->outer);
|
|
}
|
|
|
|
while ((handle = ISC_LIST_HEAD(sock->inactive_handles)) != NULL) {
|
|
ISC_LIST_DEQUEUE(sock->inactive_handles, handle, inactive_link);
|
|
nmhandle_free(sock, handle);
|
|
}
|
|
|
|
INSIST(sock->server == NULL);
|
|
sock->pquota = NULL;
|
|
|
|
isc__nm_tls_cleanup_data(sock);
|
|
#if HAVE_LIBNGHTTP2
|
|
isc__nm_http_cleanup_data(sock);
|
|
#endif
|
|
isc__nm_streamdns_cleanup_data(sock);
|
|
|
|
if (sock->barriers_initialised) {
|
|
isc_barrier_destroy(&sock->listen_barrier);
|
|
isc_barrier_destroy(&sock->stop_barrier);
|
|
}
|
|
|
|
sock->magic = 0;
|
|
|
|
/* Don't free child socket */
|
|
if (sock->parent == NULL) {
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
ISC_LIST_UNLINK(worker->active_sockets, sock, active_link);
|
|
|
|
isc_mem_put(worker->mctx, sock, sizeof(*sock));
|
|
}
|
|
|
|
isc__networker_detach(&worker);
|
|
}
|
|
|
|
static bool
|
|
nmsocket_has_active_handles(isc_nmsocket_t *sock) {
|
|
if (!ISC_LIST_EMPTY(sock->active_handles)) {
|
|
return (true);
|
|
}
|
|
|
|
if (sock->children != NULL) {
|
|
for (size_t i = 0; i < sock->nchildren; i++) {
|
|
isc_nmsocket_t *csock = &sock->children[i];
|
|
if (!ISC_LIST_EMPTY(csock->active_handles)) {
|
|
return (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
static void
|
|
nmsocket_maybe_destroy(isc_nmsocket_t *sock FLARG) {
|
|
NETMGR_TRACE_LOG("%s():%p->references = %" PRIuFAST32 "\n", __func__,
|
|
sock, isc_refcount_current(&sock->references));
|
|
|
|
if (sock->parent != NULL) {
|
|
/*
|
|
* This is a child socket and cannot be destroyed except
|
|
* as a side effect of destroying the parent, so let's go
|
|
* see if the parent is ready to be destroyed.
|
|
*/
|
|
nmsocket_maybe_destroy(sock->parent FLARG_PASS);
|
|
return;
|
|
}
|
|
|
|
REQUIRE(!sock->destroying);
|
|
REQUIRE(!sock->active);
|
|
|
|
if (!sock->closed) {
|
|
return;
|
|
}
|
|
|
|
if (isc_refcount_current(&sock->references) != 0) {
|
|
/*
|
|
* Using such check is valid only if we don't use
|
|
* isc_refcount_increment0() on the same variable.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
NETMGR_TRACE_LOG("%s:%p->statichandle = %p\n", __func__, sock,
|
|
sock->statichandle);
|
|
|
|
/*
|
|
* This is a parent socket (or a standalone). See whether the
|
|
* children have active handles before deciding whether to
|
|
* accept destruction.
|
|
*/
|
|
if (sock->statichandle == NULL && nmsocket_has_active_handles(sock)) {
|
|
return;
|
|
}
|
|
|
|
if (sock->tid == isc_tid()) {
|
|
nmsocket_cleanup(sock);
|
|
} else {
|
|
isc_async_run(sock->worker->loop, nmsocket_cleanup, sock);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
|
|
REQUIRE(sock->parent == NULL);
|
|
|
|
NETMGR_TRACE_LOG("isc___nmsocket_prep_destroy():%p->references = "
|
|
"%" PRIuFAST32 "\n",
|
|
sock, isc_refcount_current(&sock->references));
|
|
|
|
/*
|
|
* The final external reference to the socket is gone. We can try
|
|
* destroying the socket, but we have to wait for all the inflight
|
|
* handles to finish first.
|
|
*/
|
|
sock->active = false;
|
|
|
|
/*
|
|
* If the socket has children, they have been marked inactive by the
|
|
* shutdown uv_walk
|
|
*/
|
|
|
|
/*
|
|
* If we're here then we already stopped listening; otherwise
|
|
* we'd have a hanging reference from the listening process.
|
|
*
|
|
* If it's a regular socket we may need to close it.
|
|
*/
|
|
if (!sock->closing && !sock->closed) {
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
isc__nm_udp_close(sock);
|
|
return;
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_close(sock);
|
|
return;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nm_streamdns_close(sock);
|
|
return;
|
|
case isc_nm_tlssocket:
|
|
isc__nm_tls_close(sock);
|
|
return;
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httpsocket:
|
|
isc__nm_http_close(sock);
|
|
return;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
nmsocket_maybe_destroy(sock FLARG_PASS);
|
|
}
|
|
|
|
void
|
|
isc___nmsocket_detach(isc_nmsocket_t **sockp FLARG) {
|
|
REQUIRE(sockp != NULL && *sockp != NULL);
|
|
REQUIRE(VALID_NMSOCK(*sockp));
|
|
|
|
isc_nmsocket_t *sock = *sockp, *rsock = NULL;
|
|
*sockp = NULL;
|
|
|
|
/*
|
|
* If the socket is a part of a set (a child socket) we are
|
|
* counting references for the whole set at the parent.
|
|
*/
|
|
if (sock->parent != NULL) {
|
|
rsock = sock->parent;
|
|
INSIST(rsock->parent == NULL); /* Sanity check */
|
|
} else {
|
|
rsock = sock;
|
|
}
|
|
|
|
NETMGR_TRACE_LOG("isc__nmsocket_detach():%p->references = %" PRIuFAST32
|
|
"\n",
|
|
rsock, isc_refcount_current(&rsock->references) - 1);
|
|
|
|
if (isc_refcount_decrement(&rsock->references) == 1) {
|
|
isc___nmsocket_prep_destroy(rsock FLARG_PASS);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nmsocket_close(isc_nmsocket_t **sockp) {
|
|
REQUIRE(sockp != NULL);
|
|
REQUIRE(VALID_NMSOCK(*sockp));
|
|
REQUIRE((*sockp)->type == isc_nm_udplistener ||
|
|
(*sockp)->type == isc_nm_tcplistener ||
|
|
(*sockp)->type == isc_nm_streamdnslistener ||
|
|
(*sockp)->type == isc_nm_tlslistener ||
|
|
(*sockp)->type == isc_nm_httplistener);
|
|
|
|
isc__nmsocket_detach(sockp);
|
|
}
|
|
|
|
void
|
|
isc___nmsocket_init(isc_nmsocket_t *sock, isc__networker_t *worker,
|
|
isc_nmsocket_type type, isc_sockaddr_t *iface,
|
|
isc_nmsocket_t *parent FLARG) {
|
|
uint16_t family;
|
|
|
|
REQUIRE(sock != NULL);
|
|
REQUIRE(worker != NULL);
|
|
|
|
*sock = (isc_nmsocket_t){
|
|
.type = type,
|
|
.tid = worker->loop->tid,
|
|
.fd = -1,
|
|
.inactive_handles = ISC_LIST_INITIALIZER,
|
|
.result = ISC_R_UNSET,
|
|
.active_handles = ISC_LIST_INITIALIZER,
|
|
.active_link = ISC_LINK_INITIALIZER,
|
|
.active = true,
|
|
.job = ISC_JOB_INITIALIZER,
|
|
.quotacb = ISC_JOB_INITIALIZER,
|
|
};
|
|
|
|
if (iface != NULL) {
|
|
family = iface->type.sa.sa_family;
|
|
sock->iface = *iface;
|
|
} else {
|
|
family = AF_UNSPEC;
|
|
}
|
|
|
|
if (parent) {
|
|
sock->parent = parent;
|
|
} else {
|
|
ISC_LIST_APPEND(worker->active_sockets, sock, active_link);
|
|
}
|
|
|
|
#if ISC_NETMGR_TRACE
|
|
sock->backtrace_size = isc_backtrace(sock->backtrace, TRACE_SIZE);
|
|
#endif
|
|
|
|
isc__networker_attach(worker, &sock->worker);
|
|
sock->uv_handle.handle.data = sock;
|
|
|
|
ISC_LINK_INIT(&sock->quotacb, link);
|
|
|
|
switch (type) {
|
|
case isc_nm_udpsocket:
|
|
case isc_nm_udplistener:
|
|
switch (family) {
|
|
case AF_INET:
|
|
sock->statsindex = udp4statsindex;
|
|
break;
|
|
case AF_INET6:
|
|
sock->statsindex = udp6statsindex;
|
|
break;
|
|
case AF_UNSPEC:
|
|
/*
|
|
* Route sockets are AF_UNSPEC, and don't
|
|
* have stats counters.
|
|
*/
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_tcplistener:
|
|
case isc_nm_httpsocket:
|
|
case isc_nm_httplistener:
|
|
switch (family) {
|
|
case AF_INET:
|
|
sock->statsindex = tcp4statsindex;
|
|
break;
|
|
case AF_INET6:
|
|
sock->statsindex = tcp6statsindex;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
isc_refcount_init(&sock->references, 1);
|
|
|
|
memset(&sock->tlsstream, 0, sizeof(sock->tlsstream));
|
|
|
|
NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %" PRIuFAST32
|
|
"\n",
|
|
sock, isc_refcount_current(&sock->references));
|
|
|
|
#if HAVE_LIBNGHTTP2
|
|
isc__nm_http_initsocket(sock);
|
|
#endif
|
|
|
|
sock->magic = NMSOCK_MAGIC;
|
|
|
|
isc__nm_incstats(sock, STATID_ACTIVE);
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_clearcb(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
sock->recv_cb = NULL;
|
|
sock->recv_cbarg = NULL;
|
|
sock->accept_cb = NULL;
|
|
sock->accept_cbarg = NULL;
|
|
sock->connect_cb = NULL;
|
|
sock->connect_cbarg = NULL;
|
|
}
|
|
|
|
void
|
|
isc__nm_free_uvbuf(isc_nmsocket_t *sock, const uv_buf_t *buf) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
REQUIRE(buf->base == sock->worker->recvbuf);
|
|
|
|
sock->worker->recvbuf_inuse = false;
|
|
}
|
|
|
|
static isc_nmhandle_t *
|
|
alloc_handle(isc_nmsocket_t *sock) {
|
|
isc_nmhandle_t *handle = isc_mem_get(sock->worker->mctx,
|
|
sizeof(isc_nmhandle_t));
|
|
|
|
*handle = (isc_nmhandle_t){
|
|
.magic = NMHANDLE_MAGIC,
|
|
.active_link = ISC_LINK_INITIALIZER,
|
|
.inactive_link = ISC_LINK_INITIALIZER,
|
|
.job = ISC_JOB_INITIALIZER,
|
|
};
|
|
isc_refcount_init(&handle->references, 1);
|
|
|
|
return (handle);
|
|
}
|
|
|
|
static isc_nmhandle_t *
|
|
dequeue_handle(isc_nmsocket_t *sock) {
|
|
#if !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__
|
|
isc_nmhandle_t *handle = ISC_LIST_HEAD(sock->inactive_handles);
|
|
if (handle != NULL) {
|
|
ISC_LIST_DEQUEUE(sock->inactive_handles, handle, inactive_link);
|
|
|
|
isc_refcount_init(&handle->references, 1);
|
|
INSIST(VALID_NMHANDLE(handle));
|
|
return (handle);
|
|
}
|
|
#else
|
|
INSIST(ISC_LIST_EMPTY(sock->inactive_handles));
|
|
#endif /* !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__ */
|
|
return (NULL);
|
|
}
|
|
|
|
isc_nmhandle_t *
|
|
isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t const *peer,
|
|
isc_sockaddr_t const *local FLARG) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
isc_nmhandle_t *handle = dequeue_handle(sock);
|
|
if (handle == NULL) {
|
|
handle = alloc_handle(sock);
|
|
}
|
|
|
|
NETMGR_TRACE_LOG(
|
|
"isc__nmhandle_get():handle %p->references = %" PRIuFAST32 "\n",
|
|
handle, isc_refcount_current(&handle->references));
|
|
|
|
isc___nmsocket_attach(sock, &handle->sock FLARG_PASS);
|
|
|
|
#if ISC_NETMGR_TRACE
|
|
handle->backtrace_size = isc_backtrace(handle->backtrace, TRACE_SIZE);
|
|
#endif
|
|
|
|
if (peer != NULL) {
|
|
handle->peer = *peer;
|
|
} else {
|
|
handle->peer = sock->peer;
|
|
}
|
|
|
|
if (local != NULL) {
|
|
handle->local = *local;
|
|
} else {
|
|
handle->local = sock->iface;
|
|
}
|
|
|
|
ISC_LIST_APPEND(sock->active_handles, handle, active_link);
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
if (!sock->client) {
|
|
break;
|
|
}
|
|
FALLTHROUGH;
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_tlssocket:
|
|
INSIST(sock->statichandle == NULL);
|
|
|
|
/*
|
|
* statichandle must be assigned, not attached;
|
|
* otherwise, if a handle was detached elsewhere
|
|
* it could never reach 0 references, and the
|
|
* handle and socket would never be freed.
|
|
*/
|
|
sock->statichandle = handle;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#if HAVE_LIBNGHTTP2
|
|
if (sock->type == isc_nm_httpsocket && sock->h2.session) {
|
|
isc__nm_httpsession_attach(sock->h2.session,
|
|
&handle->httpsession);
|
|
}
|
|
#endif
|
|
|
|
return (handle);
|
|
}
|
|
|
|
bool
|
|
isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
return (handle->sock->type == isc_nm_tcpsocket ||
|
|
handle->sock->type == isc_nm_tlssocket ||
|
|
handle->sock->type == isc_nm_httpsocket ||
|
|
handle->sock->type == isc_nm_streamdnssocket);
|
|
}
|
|
|
|
static void
|
|
nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
|
|
isc_refcount_destroy(&handle->references);
|
|
|
|
handle->magic = 0;
|
|
|
|
if (handle->dofree != NULL) {
|
|
handle->dofree(handle->opaque);
|
|
}
|
|
|
|
isc_mem_put(sock->worker->mctx, handle, sizeof(*handle));
|
|
}
|
|
|
|
static void
|
|
nmhandle__destroy(isc_nmhandle_t *handle) {
|
|
isc_nmsocket_t *sock = handle->sock;
|
|
handle->sock = NULL;
|
|
|
|
#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
|
|
nmhandle_free(sock, handle);
|
|
#else
|
|
if (sock->active) {
|
|
ISC_LIST_APPEND(sock->inactive_handles, handle, inactive_link);
|
|
} else {
|
|
nmhandle_free(sock, handle);
|
|
}
|
|
#endif
|
|
|
|
isc__nmsocket_detach(&sock);
|
|
}
|
|
|
|
static void
|
|
isc__nm_closehandle_job(void *arg) {
|
|
isc_nmhandle_t *handle = arg;
|
|
isc_nmsocket_t *sock = handle->sock;
|
|
|
|
sock->closehandle_cb(sock);
|
|
|
|
nmhandle__destroy(handle);
|
|
}
|
|
|
|
static void
|
|
nmhandle_destroy(isc_nmhandle_t *handle) {
|
|
isc_nmsocket_t *sock = handle->sock;
|
|
|
|
if (handle->doreset != NULL) {
|
|
handle->doreset(handle->opaque);
|
|
}
|
|
|
|
#if HAVE_LIBNGHTTP2
|
|
if (sock->type == isc_nm_httpsocket && handle->httpsession != NULL) {
|
|
isc__nm_httpsession_detach(&handle->httpsession);
|
|
}
|
|
#endif
|
|
|
|
if (handle == sock->statichandle) {
|
|
/* statichandle is assigned, not attached. */
|
|
sock->statichandle = NULL;
|
|
}
|
|
|
|
ISC_LIST_UNLINK(sock->active_handles, handle, active_link);
|
|
|
|
if (sock->closehandle_cb == NULL) {
|
|
nmhandle__destroy(handle);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the socket has a callback configured for that (e.g.,
|
|
* to perform cleanup after request processing), call it
|
|
* now asynchronously.
|
|
*/
|
|
isc_job_run(sock->worker->loop, &handle->job, isc__nm_closehandle_job,
|
|
handle);
|
|
}
|
|
|
|
#if ISC_NETMGR_TRACE
|
|
ISC_REFCOUNT_TRACE_IMPL(isc_nmhandle, nmhandle_destroy)
|
|
#else
|
|
ISC_REFCOUNT_IMPL(isc_nmhandle, nmhandle_destroy);
|
|
#endif
|
|
|
|
void *
|
|
isc_nmhandle_getdata(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
return (handle->opaque);
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg,
|
|
isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
handle->opaque = arg;
|
|
handle->doreset = doreset;
|
|
handle->dofree = dofree;
|
|
}
|
|
|
|
void
|
|
isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
|
isc_result_t eresult, bool async) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(VALID_UVREQ(req));
|
|
|
|
if (req->cb.send != NULL) {
|
|
isc__nm_sendcb(sock, req, eresult, async);
|
|
} else {
|
|
isc__nm_uvreq_put(&req);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
|
isc_result_t eresult, bool async) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(VALID_UVREQ(req));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
REQUIRE(req->cb.connect != NULL);
|
|
REQUIRE(sock->connecting);
|
|
|
|
sock->connecting = false;
|
|
|
|
isc__nm_incstats(sock, STATID_CONNECTFAIL);
|
|
|
|
isc__nmsocket_timer_stop(sock);
|
|
uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
|
|
|
|
isc__nmsocket_clearcb(sock);
|
|
isc__nm_connectcb(sock, req, eresult, async);
|
|
|
|
isc__nmsocket_prep_destroy(sock);
|
|
}
|
|
|
|
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);
|
|
return;
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_failed_read_cb(sock, result, async);
|
|
return;
|
|
case isc_nm_tlssocket:
|
|
isc__nm_tls_failed_read_cb(sock, result, async);
|
|
return;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nm_streamdns_failed_read_cb(sock, result, async);
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_connecttimeout_cb(uv_timer_t *timer) {
|
|
uv_connect_t *uvreq = uv_handle_get_data((uv_handle_t *)timer);
|
|
isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
|
|
isc__nm_uvreq_t *req = uv_handle_get_data((uv_handle_t *)uvreq);
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
REQUIRE(VALID_UVREQ(req));
|
|
REQUIRE(VALID_NMHANDLE(req->handle));
|
|
REQUIRE(sock->connecting);
|
|
|
|
isc__nmsocket_timer_stop(sock);
|
|
|
|
/*
|
|
* Mark the connection as timed out and shutdown the socket.
|
|
*/
|
|
REQUIRE(!sock->timedout);
|
|
sock->timedout = true;
|
|
isc__nmsocket_shutdown(sock);
|
|
}
|
|
|
|
void
|
|
isc__nm_accept_connection_log(isc_nmsocket_t *sock, isc_result_t result,
|
|
bool can_log_quota) {
|
|
int level;
|
|
|
|
switch (result) {
|
|
case ISC_R_SUCCESS:
|
|
case ISC_R_NOCONN:
|
|
return;
|
|
case ISC_R_QUOTA:
|
|
case ISC_R_SOFTQUOTA:
|
|
if (!can_log_quota) {
|
|
return;
|
|
}
|
|
level = ISC_LOG_INFO;
|
|
break;
|
|
case ISC_R_NOTCONNECTED:
|
|
level = ISC_LOG_INFO;
|
|
break;
|
|
default:
|
|
level = ISC_LOG_ERROR;
|
|
}
|
|
|
|
isc__nmsocket_log(sock, level, "Accepting TCP connection failed: %s",
|
|
isc_result_totext(result));
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult) {
|
|
isc__nm_uvreq_t *req = data;
|
|
isc_nmsocket_t *sock = NULL;
|
|
|
|
REQUIRE(eresult == ISC_R_TIMEDOUT);
|
|
REQUIRE(VALID_UVREQ(req));
|
|
REQUIRE(VALID_NMSOCK(req->sock));
|
|
|
|
sock = req->sock;
|
|
|
|
isc__nmsocket_reset(sock);
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_readtimeout_cb(uv_timer_t *timer) {
|
|
isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer);
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
if (sock->client) {
|
|
uv_timer_stop(timer);
|
|
|
|
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)) {
|
|
isc__nmsocket_clearcb(sock);
|
|
isc__nm_failed_read_cb(sock, ISC_R_TIMEDOUT, false);
|
|
}
|
|
} else {
|
|
isc__nm_failed_read_cb(sock, ISC_R_TIMEDOUT, false);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (sock->connecting) {
|
|
int r;
|
|
|
|
if (sock->connect_timeout == 0) {
|
|
return;
|
|
}
|
|
|
|
r = uv_timer_start(&sock->read_timer,
|
|
isc__nmsocket_connecttimeout_cb,
|
|
sock->connect_timeout + 10, 0);
|
|
UV_RUNTIME_CHECK(uv_timer_start, r);
|
|
|
|
} else {
|
|
int r;
|
|
|
|
if (sock->read_timeout == 0) {
|
|
return;
|
|
}
|
|
|
|
r = uv_timer_start(&sock->read_timer,
|
|
isc__nmsocket_readtimeout_cb,
|
|
sock->read_timeout, 0);
|
|
UV_RUNTIME_CHECK(uv_timer_start, r);
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_timer_start(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
if (isc__nmsocket_timer_running(sock)) {
|
|
return;
|
|
}
|
|
|
|
isc__nmsocket_timer_restart(sock);
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_timer_stop(isc_nmsocket_t *sock) {
|
|
int r;
|
|
|
|
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);
|
|
UV_RUNTIME_CHECK(uv_timer_stop, r);
|
|
}
|
|
|
|
isc__nm_uvreq_t *
|
|
isc___nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr FLARG) {
|
|
isc__nm_uvreq_t *req = NULL;
|
|
|
|
req = isc__nm_uvreq_get(sock);
|
|
req->cb.recv = sock->recv_cb;
|
|
req->cbarg = sock->recv_cbarg;
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_tlssocket:
|
|
#if ISC_NETMGR_TRACE
|
|
isc_nmhandle__attach(sock->statichandle,
|
|
&req->handle FLARG_PASS);
|
|
#else
|
|
isc_nmhandle_attach(sock->statichandle, &req->handle);
|
|
#endif
|
|
break;
|
|
case isc_nm_streamdnssocket:
|
|
#if ISC_NETMGR_TRACE
|
|
isc_nmhandle__attach(sock->recv_handle,
|
|
&req->handle FLARG_PASS);
|
|
#else
|
|
isc_nmhandle_attach(sock->recv_handle, &req->handle);
|
|
#endif
|
|
break;
|
|
default:
|
|
if (sock->client && sock->statichandle != NULL) {
|
|
#if ISC_NETMGR_TRACE
|
|
isc_nmhandle__attach(sock->statichandle,
|
|
&req->handle FLARG_PASS);
|
|
#else
|
|
isc_nmhandle_attach(sock->statichandle, &req->handle);
|
|
#endif
|
|
} else {
|
|
req->handle = isc___nmhandle_get(sock, sockaddr,
|
|
NULL FLARG_PASS);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (req);
|
|
}
|
|
|
|
/*%<
|
|
* Allocator callback for read operations.
|
|
*
|
|
* Note this doesn't actually allocate anything, it just assigns the
|
|
* worker's receive buffer to a socket, and marks it as "in use".
|
|
*/
|
|
void
|
|
isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
|
|
isc_nmsocket_t *sock = uv_handle_get_data(handle);
|
|
isc__networker_t *worker = NULL;
|
|
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
/*
|
|
* The size provided by libuv is only suggested size, and it always
|
|
* defaults to 64 * 1024 in the current versions of libuv (see
|
|
* src/unix/udp.c and src/unix/stream.c).
|
|
*/
|
|
UNUSED(size);
|
|
|
|
worker = sock->worker;
|
|
INSIST(!worker->recvbuf_inuse);
|
|
INSIST(worker->recvbuf != NULL);
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
buf->len = ISC_NETMGR_UDP_RECVBUF_SIZE;
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
buf->len = ISC_NETMGR_TCP_RECVBUF_SIZE;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
REQUIRE(buf->len <= ISC_NETMGR_RECVBUF_SIZE);
|
|
buf->base = worker->recvbuf;
|
|
|
|
worker->recvbuf_inuse = true;
|
|
}
|
|
|
|
isc_result_t
|
|
isc__nm_start_reading(isc_nmsocket_t *sock) {
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
int r;
|
|
|
|
if (uv_is_active(&sock->uv_handle.handle)) {
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb,
|
|
isc__nm_udp_read_cb);
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
|
|
isc__nm_tcp_read_cb);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
if (r != 0) {
|
|
result = isc_uverr2result(r);
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
isc__nm_stop_reading(isc_nmsocket_t *sock) {
|
|
int r;
|
|
|
|
if (!uv_is_active(&sock->uv_handle.handle)) {
|
|
return;
|
|
}
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
r = uv_udp_recv_stop(&sock->uv_handle.udp);
|
|
UV_RUNTIME_CHECK(uv_udp_recv_stop, r);
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
r = uv_read_stop(&sock->uv_handle.stream);
|
|
UV_RUNTIME_CHECK(uv_read_stop, r);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
bool
|
|
isc__nm_closing(isc__networker_t *worker) {
|
|
return (worker->shuttingdown);
|
|
}
|
|
|
|
bool
|
|
isc__nmsocket_closing(isc_nmsocket_t *sock) {
|
|
return (!sock->active || sock->closing ||
|
|
isc__nm_closing(sock->worker) ||
|
|
(sock->server != NULL && !isc__nmsocket_active(sock->server)));
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
switch (handle->sock->type) {
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httpsocket:
|
|
isc__nm_http_cleartimeout(handle);
|
|
return;
|
|
#endif
|
|
case isc_nm_tlssocket:
|
|
isc__nm_tls_cleartimeout(handle);
|
|
return;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nmhandle_streamdns_cleartimeout(handle);
|
|
return;
|
|
default:
|
|
handle->sock->read_timeout = 0;
|
|
|
|
if (uv_is_active((uv_handle_t *)&handle->sock->read_timer)) {
|
|
isc__nmsocket_timer_stop(handle->sock);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
switch (handle->sock->type) {
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httpsocket:
|
|
isc__nm_http_settimeout(handle, timeout);
|
|
return;
|
|
#endif
|
|
case isc_nm_tlssocket:
|
|
isc__nm_tls_settimeout(handle, timeout);
|
|
return;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nmhandle_streamdns_settimeout(handle, timeout);
|
|
return;
|
|
default:
|
|
handle->sock->read_timeout = timeout;
|
|
isc__nmsocket_timer_restart(handle->sock);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
isc_nm_t *netmgr = NULL;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
sock = handle->sock;
|
|
netmgr = sock->worker->netmgr;
|
|
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
sock->keepalive = value;
|
|
sock->read_timeout =
|
|
value ? atomic_load_relaxed(&netmgr->keepalive)
|
|
: atomic_load_relaxed(&netmgr->idle);
|
|
sock->write_timeout =
|
|
value ? atomic_load_relaxed(&netmgr->keepalive)
|
|
: atomic_load_relaxed(&netmgr->idle);
|
|
break;
|
|
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;
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
|
default:
|
|
/*
|
|
* For any other protocol, this is a no-op.
|
|
*/
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool
|
|
isc_nmhandle_timer_running(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
return (isc__nmsocket_timer_running(handle->sock));
|
|
}
|
|
|
|
isc_sockaddr_t
|
|
isc_nmhandle_peeraddr(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
return (handle->peer);
|
|
}
|
|
|
|
isc_sockaddr_t
|
|
isc_nmhandle_localaddr(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
return (handle->local);
|
|
}
|
|
|
|
isc_nm_t *
|
|
isc_nmhandle_netmgr(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
return (handle->sock->worker->netmgr);
|
|
}
|
|
|
|
isc__nm_uvreq_t *
|
|
isc___nm_uvreq_get(isc_nmsocket_t *sock FLARG) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(sock->tid == isc_tid());
|
|
|
|
isc__networker_t *worker = sock->worker;
|
|
|
|
isc__nm_uvreq_t *req = isc_mempool_get(worker->uvreq_pool);
|
|
*req = (isc__nm_uvreq_t){
|
|
.connect_tries = 3,
|
|
.link = ISC_LINK_INITIALIZER,
|
|
.active_link = ISC_LINK_INITIALIZER,
|
|
.job = ISC_JOB_INITIALIZER,
|
|
.magic = UVREQ_MAGIC,
|
|
};
|
|
uv_handle_set_data(&req->uv_req.handle, req);
|
|
|
|
isc___nmsocket_attach(sock, &req->sock FLARG_PASS);
|
|
|
|
ISC_LIST_APPEND(sock->active_uvreqs, req, active_link);
|
|
|
|
return (req);
|
|
}
|
|
|
|
void
|
|
isc___nm_uvreq_put(isc__nm_uvreq_t **reqp FLARG) {
|
|
REQUIRE(reqp != NULL && VALID_UVREQ(*reqp));
|
|
|
|
isc__nm_uvreq_t *req = *reqp;
|
|
isc_nmhandle_t *handle = req->handle;
|
|
isc_nmsocket_t *sock = req->sock;
|
|
|
|
*reqp = NULL;
|
|
req->handle = NULL;
|
|
|
|
REQUIRE(VALID_UVREQ(req));
|
|
|
|
ISC_LIST_UNLINK(sock->active_uvreqs, req, active_link);
|
|
|
|
if (handle != NULL) {
|
|
#if ISC_NETMGR_TRACE
|
|
isc_nmhandle__detach(&handle, func, file, line);
|
|
#else
|
|
isc_nmhandle_detach(&handle);
|
|
#endif
|
|
}
|
|
|
|
isc_mempool_put(sock->worker->uvreq_pool, req);
|
|
|
|
isc___nmsocket_detach(&sock FLARG_PASS);
|
|
}
|
|
|
|
void
|
|
isc_nm_send(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_udpsocket:
|
|
case isc_nm_udplistener:
|
|
isc__nm_udp_send(handle, region, cb, cbarg);
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_send(handle, region, cb, cbarg);
|
|
break;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nm_streamdns_send(handle, region, cb, cbarg);
|
|
break;
|
|
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;
|
|
#endif
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
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));
|
|
|
|
switch (handle->sock->type) {
|
|
case isc_nm_udpsocket:
|
|
isc__nm_udp_read(handle, cb, cbarg);
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_read(handle, cb, cbarg);
|
|
break;
|
|
case isc_nm_streamdnssocket:
|
|
isc__nm_streamdns_read(handle, cb, cbarg);
|
|
break;
|
|
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;
|
|
#endif
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static void
|
|
cancelread_cb(void *arg) {
|
|
isc_nmhandle_t *handle = arg;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
REQUIRE(handle->sock->tid == isc_tid());
|
|
|
|
REQUIRE(handle->sock->tid == isc_tid());
|
|
|
|
switch (handle->sock->type) {
|
|
case isc_nm_udpsocket:
|
|
case isc_nm_streamdnssocket:
|
|
case isc_nm_httpsocket:
|
|
isc__nm_failed_read_cb(handle->sock, ISC_R_CANCELED, false);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
isc_nmhandle_detach(&handle);
|
|
}
|
|
|
|
void
|
|
isc_nm_cancelread(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
/* Running this directly could cause a dead-lock */
|
|
isc_nmhandle_ref(handle);
|
|
isc_async_run(handle->sock->worker->loop, cancelread_cb, handle);
|
|
}
|
|
|
|
void
|
|
isc_nm_read_stop(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
|
|
isc_nmsocket_t *sock = handle->sock;
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_read_stop(handle);
|
|
break;
|
|
case isc_nm_tlssocket:
|
|
isc__nm_tls_read_stop(handle);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nmhandle_close(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
isc__nmsocket_clearcb(handle->sock);
|
|
isc__nmsocket_prep_destroy(handle->sock);
|
|
}
|
|
|
|
void
|
|
isc_nm_stoplistening(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udplistener:
|
|
isc__nm_udp_stoplistening(sock);
|
|
break;
|
|
case isc_nm_tcplistener:
|
|
isc__nm_tcp_stoplistening(sock);
|
|
break;
|
|
case isc_nm_streamdnslistener:
|
|
isc__nm_streamdns_stoplistening(sock);
|
|
break;
|
|
case isc_nm_tlslistener:
|
|
isc__nm_tls_stoplistening(sock);
|
|
break;
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httplistener:
|
|
isc__nm_http_stoplistening(sock);
|
|
break;
|
|
#endif
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_stop(isc_nmsocket_t *listener) {
|
|
REQUIRE(VALID_NMSOCK(listener));
|
|
REQUIRE(listener->tid == isc_tid());
|
|
REQUIRE(listener->tid == 0);
|
|
REQUIRE(listener->type == isc_nm_httplistener ||
|
|
listener->type == isc_nm_tlslistener ||
|
|
listener->type == isc_nm_streamdnslistener);
|
|
REQUIRE(!listener->closing);
|
|
|
|
listener->closing = true;
|
|
|
|
REQUIRE(listener->outer != NULL);
|
|
isc_nm_stoplistening(listener->outer);
|
|
|
|
listener->accept_cb = NULL;
|
|
listener->accept_cbarg = NULL;
|
|
listener->recv_cb = NULL;
|
|
listener->recv_cbarg = NULL;
|
|
|
|
isc__nmsocket_detach(&listener->outer);
|
|
|
|
listener->closed = true;
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_barrier_init(isc_nmsocket_t *listener) {
|
|
REQUIRE(listener->nchildren > 0);
|
|
isc_barrier_init(&listener->listen_barrier, listener->nchildren);
|
|
isc_barrier_init(&listener->stop_barrier, listener->nchildren);
|
|
listener->barriers_initialised = true;
|
|
}
|
|
|
|
static void
|
|
isc___nm_connectcb(void *arg) {
|
|
isc__nm_uvreq_t *uvreq = arg;
|
|
|
|
uvreq->cb.connect(uvreq->handle, uvreq->result, uvreq->cbarg);
|
|
isc__nm_uvreq_put(&uvreq);
|
|
}
|
|
|
|
void
|
|
isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
|
|
isc_result_t eresult, bool async) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(VALID_UVREQ(uvreq));
|
|
REQUIRE(VALID_NMHANDLE(uvreq->handle));
|
|
REQUIRE(uvreq->cb.connect != NULL);
|
|
|
|
uvreq->result = eresult;
|
|
|
|
if (!async) {
|
|
isc___nm_connectcb(uvreq);
|
|
return;
|
|
}
|
|
|
|
isc_job_run(sock->worker->loop, &uvreq->job, isc___nm_connectcb, uvreq);
|
|
}
|
|
|
|
static void
|
|
isc___nm_readcb(void *arg) {
|
|
isc__nm_uvreq_t *uvreq = arg;
|
|
isc_region_t region;
|
|
|
|
region.base = (unsigned char *)uvreq->uvbuf.base;
|
|
region.length = uvreq->uvbuf.len;
|
|
uvreq->cb.recv(uvreq->handle, uvreq->result, ®ion, uvreq->cbarg);
|
|
|
|
isc__nm_uvreq_put(&uvreq);
|
|
}
|
|
|
|
void
|
|
isc__nm_readcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
|
|
isc_result_t eresult, bool async) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(VALID_UVREQ(uvreq));
|
|
REQUIRE(VALID_NMHANDLE(uvreq->handle));
|
|
|
|
uvreq->result = eresult;
|
|
|
|
if (!async) {
|
|
isc___nm_readcb(uvreq);
|
|
return;
|
|
}
|
|
|
|
isc_job_run(sock->worker->loop, &uvreq->job, isc___nm_readcb, uvreq);
|
|
}
|
|
|
|
static void
|
|
isc___nm_sendcb(void *arg) {
|
|
isc__nm_uvreq_t *uvreq = arg;
|
|
|
|
uvreq->cb.send(uvreq->handle, uvreq->result, uvreq->cbarg);
|
|
isc__nm_uvreq_put(&uvreq);
|
|
}
|
|
|
|
void
|
|
isc__nm_sendcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
|
|
isc_result_t eresult, bool async) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(VALID_UVREQ(uvreq));
|
|
REQUIRE(VALID_NMHANDLE(uvreq->handle));
|
|
|
|
uvreq->result = eresult;
|
|
|
|
if (!async) {
|
|
isc___nm_sendcb(uvreq);
|
|
return;
|
|
}
|
|
|
|
isc_job_run(sock->worker->loop, &uvreq->job, isc___nm_sendcb, uvreq);
|
|
}
|
|
|
|
static void
|
|
reset_shutdown(uv_handle_t *handle) {
|
|
isc_nmsocket_t *sock = uv_handle_get_data(handle);
|
|
|
|
isc__nmsocket_shutdown(sock);
|
|
isc__nmsocket_detach(&sock);
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_reset(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
if (!uv_is_closing(&sock->uv_handle.handle) &&
|
|
uv_is_active(&sock->uv_handle.handle))
|
|
{
|
|
/*
|
|
* The real shutdown will be handled in the respective
|
|
* close functions.
|
|
*/
|
|
isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
|
|
int r = uv_tcp_close_reset(&sock->uv_handle.tcp,
|
|
reset_shutdown);
|
|
UV_RUNTIME_CHECK(uv_tcp_close_reset, r);
|
|
} else {
|
|
isc__nmsocket_shutdown(sock);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_shutdown(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
isc__nm_udp_shutdown(sock);
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
isc__nm_tcp_shutdown(sock);
|
|
break;
|
|
case isc_nm_udplistener:
|
|
case isc_nm_tcplistener:
|
|
return;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static void
|
|
shutdown_walk_cb(uv_handle_t *handle, void *arg) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
UNUSED(arg);
|
|
|
|
if (uv_is_closing(handle)) {
|
|
return;
|
|
}
|
|
|
|
sock = uv_handle_get_data(handle);
|
|
|
|
switch (handle->type) {
|
|
case UV_UDP:
|
|
isc__nmsocket_shutdown(sock);
|
|
return;
|
|
case UV_TCP:
|
|
switch (sock->type) {
|
|
case isc_nm_tcpsocket:
|
|
if (sock->parent == NULL) {
|
|
/* Reset the TCP connections on shutdown */
|
|
isc__nmsocket_reset(sock);
|
|
return;
|
|
}
|
|
FALLTHROUGH;
|
|
default:
|
|
isc__nmsocket_shutdown(sock);
|
|
}
|
|
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats) {
|
|
REQUIRE(VALID_NM(mgr));
|
|
REQUIRE(mgr->stats == NULL);
|
|
REQUIRE(isc_stats_ncounters(stats) == isc_sockstatscounter_max);
|
|
|
|
isc_stats_attach(stats, &mgr->stats);
|
|
}
|
|
|
|
void
|
|
isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(id < STATID_MAX);
|
|
|
|
if (sock->statsindex != NULL && sock->worker->netmgr->stats != NULL) {
|
|
isc_stats_increment(sock->worker->netmgr->stats,
|
|
sock->statsindex[id]);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc__nm_decstats(isc_nmsocket_t *sock, isc__nm_statid_t id) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
REQUIRE(id < STATID_MAX);
|
|
|
|
if (sock->statsindex != NULL && sock->worker->netmgr->stats != NULL) {
|
|
isc_stats_decrement(sock->worker->netmgr->stats,
|
|
sock->statsindex[id]);
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
isc_nm_checkaddr(const isc_sockaddr_t *addr, isc_socktype_t type) {
|
|
int proto, pf, addrlen, fd, r;
|
|
|
|
REQUIRE(addr != NULL);
|
|
|
|
switch (type) {
|
|
case isc_socktype_tcp:
|
|
proto = SOCK_STREAM;
|
|
break;
|
|
case isc_socktype_udp:
|
|
proto = SOCK_DGRAM;
|
|
break;
|
|
default:
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
pf = isc_sockaddr_pf(addr);
|
|
if (pf == AF_INET) {
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
} else {
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
}
|
|
|
|
fd = socket(pf, proto, 0);
|
|
if (fd < 0) {
|
|
return (isc_errno_toresult(errno));
|
|
}
|
|
|
|
r = bind(fd, (const struct sockaddr *)&addr->type.sa, addrlen);
|
|
if (r < 0) {
|
|
close(fd);
|
|
return (isc_errno_toresult(errno));
|
|
}
|
|
|
|
close(fd);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
#if defined(TCP_CONNECTIONTIMEOUT)
|
|
#define TIMEOUT_TYPE int
|
|
#define TIMEOUT_DIV 1000
|
|
#define TIMEOUT_OPTNAME TCP_CONNECTIONTIMEOUT
|
|
#elif defined(TCP_RXT_CONNDROPTIME)
|
|
#define TIMEOUT_TYPE int
|
|
#define TIMEOUT_DIV 1000
|
|
#define TIMEOUT_OPTNAME TCP_RXT_CONNDROPTIME
|
|
#elif defined(TCP_USER_TIMEOUT)
|
|
#define TIMEOUT_TYPE unsigned int
|
|
#define TIMEOUT_DIV 1
|
|
#define TIMEOUT_OPTNAME TCP_USER_TIMEOUT
|
|
#elif defined(TCP_KEEPINIT)
|
|
#define TIMEOUT_TYPE int
|
|
#define TIMEOUT_DIV 1000
|
|
#define TIMEOUT_OPTNAME TCP_KEEPINIT
|
|
#endif
|
|
|
|
void
|
|
isc__nm_set_network_buffers(isc_nm_t *nm, uv_handle_t *handle) {
|
|
int32_t recv_buffer_size = 0;
|
|
int32_t send_buffer_size = 0;
|
|
|
|
switch (handle->type) {
|
|
case UV_TCP:
|
|
recv_buffer_size =
|
|
atomic_load_relaxed(&nm->recv_tcp_buffer_size);
|
|
send_buffer_size =
|
|
atomic_load_relaxed(&nm->send_tcp_buffer_size);
|
|
break;
|
|
case UV_UDP:
|
|
recv_buffer_size =
|
|
atomic_load_relaxed(&nm->recv_udp_buffer_size);
|
|
send_buffer_size =
|
|
atomic_load_relaxed(&nm->send_udp_buffer_size);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
if (recv_buffer_size > 0) {
|
|
int r = uv_recv_buffer_size(handle, &recv_buffer_size);
|
|
UV_RUNTIME_CHECK(uv_recv_buffer_size, r);
|
|
}
|
|
|
|
if (send_buffer_size > 0) {
|
|
int r = uv_send_buffer_size(handle, &send_buffer_size);
|
|
UV_RUNTIME_CHECK(uv_send_buffer_size, r);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nm_bad_request(isc_nmhandle_t *handle) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
sock = handle->sock;
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_udpsocket:
|
|
return;
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_streamdnssocket:
|
|
case isc_nm_tlssocket:
|
|
REQUIRE(sock->parent == NULL);
|
|
isc__nmsocket_reset(sock);
|
|
return;
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httpsocket:
|
|
isc__nm_http_bad_request(handle);
|
|
return;
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
isc_nm_xfr_checkperm(isc_nmhandle_t *handle) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
isc_result_t result = ISC_R_NOPERM;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
sock = handle->sock;
|
|
|
|
switch (sock->type) {
|
|
case isc_nm_streamdnssocket:
|
|
result = isc__nm_streamdns_xfr_checkperm(sock);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
bool
|
|
isc_nm_is_http_handle(isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
return (handle->sock->type == isc_nm_httpsocket);
|
|
}
|
|
|
|
void
|
|
isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
REQUIRE(!handle->sock->client);
|
|
|
|
#if !HAVE_LIBNGHTTP2
|
|
UNUSED(ttl);
|
|
#endif
|
|
|
|
sock = handle->sock;
|
|
switch (sock->type) {
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httpsocket:
|
|
isc__nm_http_set_maxage(handle, ttl);
|
|
break;
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
|
case isc_nm_udpsocket:
|
|
case isc_nm_streamdnssocket:
|
|
return;
|
|
break;
|
|
case isc_nm_tcpsocket:
|
|
case isc_nm_tlssocket:
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
isc_nmsocket_type
|
|
isc_nm_socket_type(const isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
return (handle->sock->type);
|
|
}
|
|
|
|
bool
|
|
isc_nm_has_encryption(const isc_nmhandle_t *handle) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
switch (handle->sock->type) {
|
|
case isc_nm_tlssocket:
|
|
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);
|
|
};
|
|
|
|
return (false);
|
|
}
|
|
|
|
const char *
|
|
isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
sock = handle->sock;
|
|
switch (sock->type) {
|
|
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;
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
typedef struct settlsctx_data {
|
|
isc_nmsocket_t *listener;
|
|
isc_tlsctx_t *tlsctx;
|
|
} settlsctx_data_t;
|
|
|
|
static void
|
|
settlsctx_cb(void *arg) {
|
|
settlsctx_data_t *data = arg;
|
|
const uint32_t tid = isc_tid();
|
|
isc_nmsocket_t *listener = data->listener;
|
|
isc_tlsctx_t *tlsctx = data->tlsctx;
|
|
isc__networker_t *worker = &listener->worker->netmgr->workers[tid];
|
|
|
|
isc_mem_put(worker->loop->mctx, data, sizeof(*data));
|
|
|
|
REQUIRE(listener->type == isc_nm_tlslistener);
|
|
|
|
isc__nm_async_tls_set_tlsctx(listener, tlsctx, tid);
|
|
|
|
isc__nmsocket_detach(&listener);
|
|
isc_tlsctx_free(&tlsctx);
|
|
}
|
|
|
|
static void
|
|
set_tlsctx_workers(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
|
|
const size_t nworkers =
|
|
(size_t)isc_loopmgr_nloops(listener->worker->netmgr->loopmgr);
|
|
/* Update the TLS context reference for every worker thread. */
|
|
for (size_t i = 0; i < nworkers; i++) {
|
|
isc__networker_t *worker =
|
|
&listener->worker->netmgr->workers[i];
|
|
settlsctx_data_t *data = isc_mem_getx(
|
|
worker->loop->mctx, sizeof(*data), ISC_MEM_ZERO);
|
|
|
|
isc__nmsocket_attach(listener, &data->listener);
|
|
isc_tlsctx_attach(tlsctx, &data->tlsctx);
|
|
|
|
isc_async_run(worker->loop, settlsctx_cb, data);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
|
|
REQUIRE(VALID_NMSOCK(listener));
|
|
REQUIRE(tlsctx != NULL);
|
|
|
|
switch (listener->type) {
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httplistener:
|
|
/*
|
|
* We handle HTTP listener sockets differently, as they rely
|
|
* on underlying TLS sockets for networking. The TLS context
|
|
* will get passed to these underlying sockets via the call to
|
|
* isc__nm_http_set_tlsctx().
|
|
*/
|
|
isc__nm_http_set_tlsctx(listener, tlsctx);
|
|
break;
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
|
case isc_nm_tlslistener:
|
|
set_tlsctx_workers(listener, tlsctx);
|
|
break;
|
|
case isc_nm_streamdnslistener:
|
|
isc__nm_streamdns_set_tlsctx(listener, tlsctx);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
};
|
|
}
|
|
|
|
void
|
|
isc_nmsocket_set_max_streams(isc_nmsocket_t *listener,
|
|
const uint32_t max_streams) {
|
|
REQUIRE(VALID_NMSOCK(listener));
|
|
switch (listener->type) {
|
|
#if HAVE_LIBNGHTTP2
|
|
case isc_nm_httplistener:
|
|
isc__nm_http_set_max_streams(listener, max_streams);
|
|
break;
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
|
default:
|
|
UNUSED(max_streams);
|
|
break;
|
|
};
|
|
return;
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls) {
|
|
const int log_level = ISC_LOG_DEBUG(1);
|
|
char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
|
|
char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
|
|
|
|
REQUIRE(tls != NULL);
|
|
|
|
if (!isc_log_wouldlog(isc_lctx, log_level)) {
|
|
return;
|
|
};
|
|
|
|
isc_sockaddr_format(&sock->peer, client_sabuf, sizeof(client_sabuf));
|
|
isc_sockaddr_format(&sock->iface, local_sabuf, sizeof(local_sabuf));
|
|
isc__nmsocket_log(sock, log_level, "TLS %s session %s for %s on %s",
|
|
SSL_is_server(tls) ? "server" : "client",
|
|
SSL_session_reused(tls) ? "resumed" : "created",
|
|
client_sabuf, local_sabuf);
|
|
}
|
|
|
|
static void
|
|
isc__networker_destroy(isc__networker_t *worker) {
|
|
isc_nm_t *netmgr = worker->netmgr;
|
|
worker->netmgr = NULL;
|
|
|
|
isc__netmgr_log(netmgr, ISC_LOG_DEBUG(1),
|
|
"Destroying network manager worker on loop %p(%d)",
|
|
worker->loop, isc_tid());
|
|
|
|
isc_loop_detach(&worker->loop);
|
|
|
|
isc_mempool_destroy(&worker->uvreq_pool);
|
|
|
|
isc_mem_putanddetach(&worker->mctx, worker->recvbuf,
|
|
ISC_NETMGR_RECVBUF_SIZE);
|
|
isc_nm_detach(&netmgr);
|
|
}
|
|
|
|
ISC_REFCOUNT_IMPL(isc__networker, isc__networker_destroy);
|
|
|
|
void
|
|
isc__netmgr_log(const isc_nm_t *netmgr, int level, const char *fmt, ...) {
|
|
char msgbuf[2048];
|
|
va_list ap;
|
|
|
|
if (!isc_log_wouldlog(isc_lctx, level)) {
|
|
return;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
isc_log_write(isc_lctx, ISC_LOGCATEGORY_DEFAULT, ISC_LOGMODULE_NETMGR,
|
|
level, "netmgr %p: %s", netmgr, msgbuf);
|
|
}
|
|
|
|
void
|
|
isc__nmsocket_log(const isc_nmsocket_t *sock, int level, const char *fmt, ...) {
|
|
char msgbuf[2048];
|
|
va_list ap;
|
|
|
|
if (!isc_log_wouldlog(isc_lctx, level)) {
|
|
return;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
isc_log_write(isc_lctx, ISC_LOGCATEGORY_DEFAULT, ISC_LOGMODULE_NETMGR,
|
|
level, "socket %p: %s", sock, msgbuf);
|
|
}
|
|
|
|
void
|
|
isc__nmhandle_log(const isc_nmhandle_t *handle, int level, const char *fmt,
|
|
...) {
|
|
char msgbuf[2048];
|
|
va_list ap;
|
|
|
|
if (!isc_log_wouldlog(isc_lctx, level)) {
|
|
return;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
isc__nmsocket_log(handle->sock, level, "handle %p: %s", handle, msgbuf);
|
|
}
|
|
|
|
void
|
|
isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
isc_nmsocket_t *sock = handle->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) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
isc_nmsocket_t *sock = handle->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) {
|
|
REQUIRE(VALID_NMHANDLE(handle));
|
|
REQUIRE(VALID_NMSOCK(handle->sock));
|
|
|
|
isc_result_t result = ISC_R_FAILURE;
|
|
isc_nmsocket_t *sock = handle->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);
|
|
}
|
|
|
|
isc_sockaddr_t
|
|
isc_nmsocket_getaddr(isc_nmsocket_t *sock) {
|
|
REQUIRE(VALID_NMSOCK(sock));
|
|
return (sock->iface);
|
|
}
|
|
|
|
#if ISC_NETMGR_TRACE
|
|
/*
|
|
* Dump all active sockets in netmgr. We output to stderr
|
|
* as the logger might be already shut down.
|
|
*/
|
|
|
|
static const char *
|
|
nmsocket_type_totext(isc_nmsocket_type type) {
|
|
switch (type) {
|
|
case isc_nm_udpsocket:
|
|
return ("isc_nm_udpsocket");
|
|
case isc_nm_udplistener:
|
|
return ("isc_nm_udplistener");
|
|
case isc_nm_tcpsocket:
|
|
return ("isc_nm_tcpsocket");
|
|
case isc_nm_tcplistener:
|
|
return ("isc_nm_tcplistener");
|
|
case isc_nm_tlssocket:
|
|
return ("isc_nm_tlssocket");
|
|
case isc_nm_tlslistener:
|
|
return ("isc_nm_tlslistener");
|
|
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();
|
|
}
|
|
}
|
|
|
|
static void
|
|
nmhandle_dump(isc_nmhandle_t *handle) {
|
|
fprintf(stderr, "Active handle %p, refs %" PRIuFAST32 "\n", handle,
|
|
isc_refcount_current(&handle->references));
|
|
fprintf(stderr, "Created by:\n");
|
|
isc_backtrace_symbols_fd(handle->backtrace, handle->backtrace_size,
|
|
STDERR_FILENO);
|
|
fprintf(stderr, "\n\n");
|
|
}
|
|
|
|
static void
|
|
nmsocket_dump(isc_nmsocket_t *sock) {
|
|
isc_nmhandle_t *handle = NULL;
|
|
|
|
fprintf(stderr, "\n=================\n");
|
|
fprintf(stderr, "Active %s socket %p, type %s, refs %" PRIuFAST32 "\n",
|
|
sock->client ? "client" : "server", sock,
|
|
nmsocket_type_totext(sock->type),
|
|
isc_refcount_current(&sock->references));
|
|
fprintf(stderr,
|
|
"Parent %p, listener %p, server %p, statichandle = "
|
|
"%p\n",
|
|
sock->parent, sock->listener, sock->server, sock->statichandle);
|
|
fprintf(stderr, "Flags:%s%s%s%s%s\n", sock->active ? " active" : "",
|
|
sock->closing ? " closing" : "",
|
|
sock->destroying ? " destroying" : "",
|
|
sock->connecting ? " connecting" : "",
|
|
sock->accepting ? " accepting" : "");
|
|
fprintf(stderr, "Created by:\n");
|
|
isc_backtrace_symbols_fd(sock->backtrace, sock->backtrace_size,
|
|
STDERR_FILENO);
|
|
fprintf(stderr, "\n");
|
|
|
|
for (handle = ISC_LIST_HEAD(sock->active_handles); handle != NULL;
|
|
handle = ISC_LIST_NEXT(handle, active_link))
|
|
{
|
|
static bool first = true;
|
|
if (first) {
|
|
fprintf(stderr, "Active handles:\n");
|
|
first = false;
|
|
}
|
|
nmhandle_dump(handle);
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
void
|
|
isc__nm_dump_active(isc__networker_t *worker) {
|
|
isc_nmsocket_t *sock = NULL;
|
|
bool first = true;
|
|
|
|
for (sock = ISC_LIST_HEAD(worker->active_sockets); sock != NULL;
|
|
sock = ISC_LIST_NEXT(sock, active_link))
|
|
{
|
|
if (first) {
|
|
fprintf(stderr, "Outstanding sockets\n");
|
|
first = false;
|
|
}
|
|
nmsocket_dump(sock);
|
|
}
|
|
}
|
|
#endif
|