Commit 53f0b6c3 authored by Evan Hunt's avatar Evan Hunt

convert ns_client and related objects to use netmgr

- ns__client_request() is now called by netmgr with an isc_nmhandle_t
  parameter. The handle can then be permanently associated with an
  ns_client object.
- The task manager is paused so that isc_task events that may be
  triggred during client processing will not fire until after the netmgr is
  finished with it. Before any asynchronous event, the client MUST
  call isc_nmhandle_ref(client->handle), to prevent the client from
  being reset and reused while waiting for an event to process. When
  the asynchronous event is complete, isc_nmhandle_unref(client->handle)
  must be called to ensure the handle can be reused later.
- reference counting of client objects is now handled in the nmhandle
  object.  when the handle references drop to zero, the client's "reset"
  callback is used to free temporary resources and reiniialize it,
  whereupon the handle (and associated client) is placed in the
  "inactive handles" queue.  when the sysstem is shutdown and the
  handles are cleaned up, the client's "put" callback is called to free
  all remaining resources.
- because client allocation is no longer handled in the same way,
  the '-T clienttest' option has now been removed and is no longer
  used by any system tests.
- the unit tests require wrapping the isc_nmhandle_unref() function;
  when LD_WRAP is supported, that is used. otherwise we link a
  libwrap.so interposer library and use that.
parent 33bf9033
......@@ -19,6 +19,7 @@
#include <isc/rwlock.h>
#include <isc/log.h>
#include <isc/net.h>
#include <isc/netmgr.h>
#include <isccfg/aclconf.h>
#include <isccfg/cfg.h>
......@@ -62,6 +63,7 @@ EXTERN bool named_g_run_done INIT(false);
*/
EXTERN isc_timermgr_t * named_g_timermgr INIT(NULL);
EXTERN isc_socketmgr_t * named_g_socketmgr INIT(NULL);
EXTERN isc_nm_t * named_g_nm INIT(NULL);
EXTERN cfg_parser_t * named_g_parser INIT(NULL);
EXTERN cfg_parser_t * named_g_addparser INIT(NULL);
EXTERN const char * named_g_version INIT(VERSION);
......
......@@ -24,6 +24,7 @@
#include <isc/file.h>
#include <isc/hash.h>
#include <isc/httpd.h>
#include <isc/netmgr.h>
#include <isc/os.h>
#include <isc/platform.h>
#include <isc/print.h>
......@@ -124,7 +125,6 @@ static int maxudp = 0;
/*
* -T options:
*/
static bool clienttest = false;
static bool dropedns = false;
static bool ednsformerr = false;
static bool ednsnotimp = false;
......@@ -622,17 +622,12 @@ parse_T_opt(char *option) {
/*
* force the server to behave (or misbehave) in
* specified ways for testing purposes.
*
* clienttest: make clients single shot with their
* own memory context.
* delay=xxxx: delay client responses by xxxx ms to
* simulate remote servers.
* dscp=x: check that dscp values are as
* expected and assert otherwise.
*/
if (!strcmp(option, "clienttest")) {
clienttest = true;
} else if (!strncmp(option, "delay=", 6)) {
if (!strncmp(option, "delay=", 6)) {
delay = atoi(option + 6);
} else if (!strcmp(option, "dropedns")) {
dropedns = true;
......@@ -897,8 +892,15 @@ create_managers(void) {
"using %u UDP listener%s per interface",
named_g_udpdisp, named_g_udpdisp == 1 ? "" : "s");
result = isc_taskmgr_create(named_g_mctx, named_g_cpus, 0, NULL,
&named_g_taskmgr);
named_g_nm = isc_nm_start(named_g_mctx, named_g_cpus);
if (named_g_nm == NULL) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_nm_start() failed");
return (ISC_R_UNEXPECTED);
}
result = isc_taskmgr_create(named_g_mctx, named_g_cpus, 0,
named_g_nm, &named_g_taskmgr);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_taskmgr_create() failed: %s",
......@@ -923,6 +925,7 @@ create_managers(void) {
return (ISC_R_UNEXPECTED);
}
isc_socketmgr_maxudp(named_g_socketmgr, maxudp);
isc_nm_maxudp(named_g_nm, maxudp);
result = isc_socketmgr_getmaxsockets(named_g_socketmgr, &socks);
if (result == ISC_R_SUCCESS) {
isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
......@@ -941,6 +944,7 @@ destroy_managers(void) {
isc_taskmgr_destroy(&named_g_taskmgr);
isc_timermgr_destroy(&named_g_timermgr);
isc_socketmgr_destroy(&named_g_socketmgr);
isc_nm_destroy(&named_g_nm);
}
static void
......@@ -1254,8 +1258,6 @@ setup(void) {
/*
* Modify server context according to command line options
*/
if (clienttest)
ns_server_setoption(sctx, NS_SERVER_CLIENTTEST, true);
if (disable4)
ns_server_setoption(sctx, NS_SERVER_DISABLE4, true);
if (disable6)
......
......@@ -9462,6 +9462,7 @@ run_server(isc_task_t *task, isc_event_t *event) {
CHECKFATAL(ns_interfacemgr_create(named_g_mctx, server->sctx,
named_g_taskmgr, named_g_timermgr,
named_g_socketmgr,
named_g_nm,
named_g_dispatchmgr,
server->task, named_g_udpdisp, geoip,
&server->interfacemgr),
......@@ -9525,6 +9526,12 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
UNUSED(task);
INSIST(task == server->task);
/*
* We need to shutdown the interface before going
* exclusive (which would pause the netmgr).
*/
ns_interfacemgr_shutdown(server->interfacemgr);
result = isc_task_beginexclusive(server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
......@@ -9582,7 +9589,6 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
isc_timer_detach(&server->pps_timer);
isc_timer_detach(&server->tat_timer);
ns_interfacemgr_shutdown(server->interfacemgr);
ns_interfacemgr_detach(&server->interfacemgr);
dns_dispatchmgr_destroy(&named_g_dispatchmgr);
......
......@@ -584,10 +584,6 @@ By default, start.pl starts a "named" server with the following options:
preventing multiple instances of this named running in this
directory (which could possibly interfere with the test).
In addition, start.pl also sets the following undocumented flag:
-T clienttest Makes clients single-shot with their own memory context.
All output is sent to a file called "named.run" in the nameserver directory.
The options used to start named can be altered. There are three ways of doing
......@@ -608,9 +604,9 @@ the named command-line arguments. The rest of the file is ignored.
3. Tweaking the default command line arguments with "-T" options. This flag is
used to alter the behavior of BIND for testing and is not documented in the
ARM. The "clienttest" option has already been mentioned, but the presence of
certain files in the "nsN" directory adds flags to the default command line
(the content of the files is irrelevant - it is only the presence that counts):
ARM. The presence of certain files in the "nsN" directory adds flags to
the default command line (the content of the files is irrelevant - it
is only the presence that counts):
named.noaa Appends "-T noaa" to the command line, which causes
"named" to never set the AA bit in an answer.
......@@ -635,7 +631,6 @@ certain files in the "nsN" directory adds flags to the default command line
the additional section if the response is triggered by RPZ
rewriting).
Starting Other Nameservers
---
In contrast to "named", nameservers written in Perl or Python (whose script
......
# this server runs named with only one worker thread
-m record,size,mctx -c named.conf -d 99 -D additional-ns1 -X named.lock -g -T clienttest -n 1
-m record,size,mctx -c named.conf -d 99 -D additional-ns1 -X named.lock -g -n 1
# this server only has 127.0.0.1 in its localhost/localnets ACLs
-m record,size,mctx -c named.conf -d 99 -D allow-query-ns3 -X named.lock -g -T clienttest -T fixedlocal
-m record,size,mctx -c named.conf -d 99 -D allow-query-ns3 -X named.lock -g -T fixedlocal
-D delzone-ns2 -X named.lock -m record,size,mctx -T clienttest -c named.conf -g -U 4
-D delzone-ns2 -X named.lock -m record,size,mctx -c named.conf -g -U 4
-m record,size,mctx -c named.conf -d 99 -D dnssec-ns6 -X named.lock -g -T nonearest -T clienttest -T tat=1
-m record,size,mctx -c named.conf -d 99 -D dnssec-ns6 -X named.lock -g -T nonearest -T tat=1
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns1 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns1 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns2 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns2 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns3 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns3 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns4 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns4 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns5 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns5 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns6 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns6 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns7 -X named.lock -g -U 4 -T dscp=46
-m record,size,mctx -c named.conf -d 99 -D dscp-ns7 -X named.lock -g -U 4 -T dscp=46
-D dupsigs-ns1 -X named.lock -m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T sigvalinsecs
-D dupsigs-ns1 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4 -T sigvalinsecs
# Don't specify '-T clienttest' as it consumes lots of memory with this test
-D fetchlimit-ns3 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4
-m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns4 -X named.lock -g -U 4 -T noedns
-m record,size,mctx -c named.conf -d 99 -D legacy-ns4 -X named.lock -g -U 4 -T noedns
-m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns5 -X named.lock -g -U 4 -T noedns
-m record,size,mctx -c named.conf -d 99 -D legacy-ns5 -X named.lock -g -U 4 -T noedns
-m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns6 -X named.lock -g -U 4 -T maxudp512
-m record,size,mctx -c named.conf -d 99 -D legacy-ns6 -X named.lock -g -U 4 -T maxudp512
-m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns7 -X named.lock -g -U 4 -T maxudp512
-m record,size,mctx -c named.conf -d 99 -D legacy-ns7 -X named.lock -g -U 4 -T maxudp512
......@@ -36,7 +36,7 @@ DLFILE="named_deflog"
PIDFILE="${THISDIR}/${CONFDIR}/named.pid"
myRNDC="$RNDC -c ${THISDIR}/${CONFDIR}/rndc.conf"
myNAMED="$NAMED -c ${THISDIR}/${CONFDIR}/named.conf -m record,size,mctx -T clienttest -T nosyslog -d 99 -D logfileconfig-ns1 -X named.lock -U 4"
myNAMED="$NAMED -c ${THISDIR}/${CONFDIR}/named.conf -m record,size,mctx -T nosyslog -d 99 -D logfileconfig-ns1 -X named.lock -U 4"
# Test given condition. If true, test again after a second. Used for testing
# filesystem-dependent conditions in order to prevent false negatives caused by
......
-D mirror-ns3 -X named.lock -m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T tat=3
-D mirror-ns3 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4 -T tat=3
-m record,size,mctx -T clienttest -c named.conf -d 99 -D mkeys-ns2 -X named.lock -g -T mkeytimers=5/10/20 -T tat=1
-m record,size,mctx -c named.conf -d 99 -D mkeys-ns2 -X named.lock -g -T mkeytimers=5/10/20 -T tat=1
-m record,size,mctx -T clienttest -c named.conf -d 99 -D mkeys-ns3 -X named.lock -g -T mkeytimers=5/10/20
-m record,size,mctx -c named.conf -d 99 -D mkeys-ns3 -X named.lock -g -T mkeytimers=5/10/20
-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g
-m record,size,mctx -c named.conf -d 99 -X named.lock -g
-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40
-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40
-m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=5/10/20
-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T mkeytimers=5/10/20
-D nsupdate-ns5 -m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
-D nsupdate-ns5 -m record,size,mctx -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
-D nsupdate-ns6 -m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
-D nsupdate-ns6 -m record,size,mctx -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
# this server runs named with the "-T clienttest" option omitted
-m record,size,mctx -c named.conf -d 99 -D resolver-ns7 -X named.lock -g
# teardown of a huge zone with tracing enabled takes way too long
# -m none is set so that stop.pl does not timeout
-D rndc-ns6 -X named.lock -m none -T clienttest -c named.conf -d 99 -g -U 4
-D rndc-ns6 -X named.lock -m none -c named.conf -d 99 -g -U 4
......@@ -257,7 +257,6 @@ sub construct_ns_command {
$command .= "-D $test-$server ";
$command .= "-X named.lock ";
$command .= "-m record,size,mctx ";
$command .= "-T clienttest ";
foreach my $t_option(
"dropedns", "ednsformerr", "ednsnotimp", "ednsrefused",
......
......@@ -598,7 +598,8 @@ deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
dns_qid_t *qid;
REQUIRE(disp->port_table != NULL);
REQUIRE(portentry != NULL && isc_refcount_current(&portentry->refs) > 0);
REQUIRE(portentry != NULL &&
isc_refcount_current(&portentry->refs) > 0);
if (isc_refcount_decrement(&portentry->refs) == 1) {
qid = DNS_QID(disp);
......
......@@ -1103,7 +1103,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
result = ISC_R_NOMEMORY;
goto cleanup;
}
rdataset = isc_mempool_get(msg->rdspool);
rdataset = isc_mempool_get(msg->rdspool);
if (rdataset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
......
......@@ -54,6 +54,7 @@
#include <dns/view.h>
#include <dns/zone.h>
#include <ns/client.h>
#include <ns/interfacemgr.h>
#include <ns/log.h>
#include <ns/notify.h>
......@@ -97,20 +98,6 @@
#define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
#define TCP_BUFFER_SIZE (65535 + 2)
#define SEND_BUFFER_SIZE 4096
#define RECV_BUFFER_SIZE 4096
#define NMCTXS 100
/*%<
* Number of 'mctx pools' for clients. (Should this be configurable?)
* When enabling threads, we use a pool of memory contexts shared by
* client objects, since concurrent access to a shared context would cause
* heavy contentions. The above constant is expected to be enough for
* completely avoiding contentions among threads for an authoritative-only
* server.
*/
#define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
......@@ -119,108 +106,9 @@
#define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
#define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
/*% nameserver client manager structure */
struct ns_clientmgr {
/* Unlocked. */
unsigned int magic;
/* The queue object has its own locks */
client_queue_t inactive; /*%< To be recycled */
isc_mem_t * mctx;
ns_server_t * sctx;
isc_taskmgr_t * taskmgr;
isc_timermgr_t * timermgr;
isc_task_t * excl;
/* Lock covers manager state. */
isc_mutex_t lock;
bool exiting;
/* Lock covers the clients list */
isc_mutex_t listlock;
client_list_t clients; /*%< All active clients */
/* Lock covers the recursing list */
isc_mutex_t reclock;
client_list_t recursing; /*%< Recursing clients */
#if NMCTXS > 0
/*%< mctx pool for clients. */
unsigned int nextmctx;
isc_mem_t * mctxpool[NMCTXS];
#endif
};
#define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
/*!
* Client object states. Ordering is significant: higher-numbered
* states are generally "more active", meaning that the client can
* have more dynamically allocated data, outstanding events, etc.
* In the list below, any such properties listed for state N
* also apply to any state > N.
*
* To force the client into a less active state, set client->newstate
* to that state and call exit_check(). This will cause any
* activities defined for higher-numbered states to be aborted.
*/
#define NS_CLIENTSTATE_FREED 0
/*%<
* The client object no longer exists.
*/
#define NS_CLIENTSTATE_INACTIVE 1
/*%<
* The client object exists and has a task and timer.
* Its "query" struct and sendbuf are initialized.
* It is on the client manager's list of inactive clients.
* It has a message and OPT, both in the reset state.
*/
#define NS_CLIENTSTATE_READY 2
/*%<
* The client object is either a TCP or a UDP one, and
* it is associated with a network interface. It is on the
* client manager's list of active clients.
*
* If it is a TCP client object, it has a TCP listener socket
* and an outstanding TCP listen request.
*
* If it is a UDP client object, it has a UDP listener socket
* and an outstanding UDP receive request.
*/
#define NS_CLIENTSTATE_READING 3
/*%<
* The client object is a TCP client object that has received
* a connection. It has a tcpsocket, tcpmsg, TCP quota, and an
* outstanding TCP read request. This state is not used for
* UDP client objects.
*/
#define NS_CLIENTSTATE_WORKING 4
/*%<
* The client object has received a request and is working
* on it. It has a view, and it may have any of a non-reset OPT,
* recursion quota, and an outstanding write request.
*/
#define NS_CLIENTSTATE_RECURSING 5
/*%<
* The client object is recursing. It will be on the 'recursing'
* list.
*/
#define NS_CLIENTSTATE_MAX 9
/*%<
* Sentinel value used to indicate "no state". When client->newstate
* has this value, we are not attempting to exit the current state.
* Must be greater than any valid state.
*/
/*
* Enable ns_client_dropport() by default.
*/
......@@ -230,22 +118,16 @@ struct ns_clientmgr {
LIBNS_EXTERNAL_DATA unsigned int ns_client_requests;
static void read_settimeout(ns_client_t *client, bool newconn);
static void client_read(ns_client_t *client, bool newconn);
static void client_accept(ns_client_t *client);
static void client_udprecv(ns_client_t *client);
static void clientmgr_attach(ns_clientmgr_t *source, ns_clientmgr_t **targetp);
static void clientmgr_detach(ns_clientmgr_t **mp);
static void clientmgr_destroy(ns_clientmgr_t *manager);
static bool exit_check(ns_client_t *client);
static void ns_client_endrequest(ns_client_t *client);
static void client_start(isc_task_t *task, isc_event_t *event);
static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
dns_dispatch_t *disp, bool tcp);
static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
isc_socket_t *sock, ns_client_t *oldclient);
static void compute_cookie(ns_client_t *client, uint32_t when,
uint32_t nonce, const unsigned char *secret,
isc_buffer_t *buf);
static void
get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp);
void
ns_client_recursing(ns_client_t *client) {
......@@ -253,7 +135,7 @@ ns_client_recursing(ns_client_t *client) {
REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
LOCK(&client->manager->reclock);
client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
client->state = NS_CLIENTSTATE_RECURSING;
ISC_LIST_APPEND(client->manager->recursing, client, rlink);
UNLOCK(&client->manager->reclock);
}
......@@ -275,596 +157,9 @@ ns_client_killoldestquery(ns_client_t *client) {
void
ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
isc_result_t result;
isc_interval_t interval;
isc_interval_set(&interval, seconds, 0);
result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
&interval, false);
client->timerset = true;
if (result != ISC_R_SUCCESS) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
"setting timeout: %s",
isc_result_totext(result));
/* Continue anyway. */
}
}
static void
read_settimeout(ns_client_t *client, bool newconn) {
isc_result_t result;
isc_interval_t interval;
unsigned int ds;
if (newconn)
ds = client->sctx->initialtimo;
else if (USEKEEPALIVE(client))
ds = client->sctx->keepalivetimo;
else
ds = client->sctx->idletimo;
isc_interval_set(&interval, ds / 10, 100000000 * (ds % 10));
result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
&interval, false);
client->timerset = true;
if (result != ISC_R_SUCCESS) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
"setting timeout: %s",
isc_result_totext(result));
/* Continue anyway. */
}
}
/*%
* Allocate a reference-counted object that will maintain a single pointer to
* the (also reference-counted) TCP client quota, shared between all the
* clients processing queries on a single TCP connection, so that all
* clients sharing the one socket will together consume only one slot in
* the 'tcp-clients' quota.
*/
static isc_result_t
tcpconn_init(ns_client_t *client, bool force) {
isc_result_t result;
isc_quota_t *quota = NULL;
ns_tcpconn_t *tconn = NULL;
REQUIRE(client->tcpconn == NULL);
/*
* Try to attach to the quota first, so we won't pointlessly
* allocate memory for a tcpconn object if we can't get one.
*/
if (force) {
result = isc_quota_force(&client->sctx->tcpquota, &quota);
} else {
result = isc_quota_attach(&client->sctx->tcpquota, &quota);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* A global memory context is used for the allocation as different
* client structures may have different memory contexts assigned and a
* reference counter allocated here might need to be freed by a
* different client. The performance impact caused by memory context
* contention here is expected to be negligible, given that this code
* is only executed for TCP connections.
*/
tconn = isc_mem_allocate(client->sctx->mctx, sizeof(*tconn));
isc_refcount_init(&tconn->refs, 1);
tconn->tcpquota = quota;
quota = NULL;
tconn->pipelined = false;
client->tcpconn = tconn;
return (ISC_R_SUCCESS);
}
/*%
* Increase the count of client structures sharing the TCP connection
* that 'source' is associated with; add a pointer to the same tcpconn
* to 'target', thus associating it with the same TCP connection.
*/
static void
tcpconn_attach(ns_client_t *source, ns_client_t *target) {
int old_refs;
REQUIRE(source->tcpconn != NULL);
REQUIRE(target->tcpconn == NULL);
REQUIRE(source->tcpconn->pipelined);
old_refs = isc_refcount_increment(&source->tcpconn->refs);
INSIST(old_refs > 0);
target->tcpconn = source->tcpconn;
}
/*%
* Decrease the count of client structures sharing the TCP connection that
* 'client' is associated with. If this is the last client using this TCP
* connection, we detach from the TCP quota and free the tcpconn
* object. Either way, client->tcpconn is set to NULL.
*/
static void
tcpconn_detach(ns_client_t *client) {
ns_tcpconn_t *tconn = NULL;
int old_refs;
REQUIRE(client->tcpconn != NULL);
tconn = client->tcpconn;
client->tcpconn = NULL;
old_refs = isc_refcount_decrement(&tconn->refs);
INSIST(old_refs > 0);
if (old_refs == 1) {
isc_quota_detach(&tconn->tcpquota);
isc_mem_free(client->sctx->mctx, tconn);
}
}
/*%
* Mark a client as active and increment the interface's 'ntcpactive'
* counter, as a signal that there is at least one client servicing
* TCP queries for the interface. If we reach the TCP client quota at
* some point, this will be used to determine whether a quota overrun
* should be permitted.
*
* Marking the client active with the 'tcpactive' flag ensures proper
* accounting, by preventing us from incrementing or decrementing
* 'ntcpactive' more than once per client.
*/
static void
mark_tcp_active(ns_client_t *client, bool active) {
if (active && !client->tcpactive) {
isc_refcount_increment0(&client->interface->ntcpactive);
client->tcpactive = active;
} else if (!active && client->tcpactive) {
uint32_t old =
isc_refcount_decrement(&client->interface->ntcpactive);
INSIST(old > 0);
client->tcpactive = active;
}
}
/*%
* Check for a deactivation or shutdown request and take appropriate
* action. Returns true if either is in progress; in this case
* the caller must no longer use the client object as it may have been
* freed.
*/
static bool
exit_check(ns_client_t *client) {
bool destroy_manager = false;
ns_clientmgr_t *manager = NULL;
REQUIRE(NS_CLIENT_VALID(client));
manager = client->manager;
if (client->state <= client->newstate)
return (false); /* Business as usual. */
INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
/*
* We need to detach from the view early when shutting down
* the server to break the following vicious circle:
*
* - The resolver will not shut down until the view refcount is zero
* - The view refcount does not go to zero until all clients detach
* - The client does not detach from the view until references is zero
* - references does not go to zero until the resolver has shut down
*
* Keep the view attached until any outstanding updates complete.
*/
if (client->nupdates == 0 &&
client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
dns_view_detach(&client->view);
if (client->state == NS_CLIENTSTATE_WORKING ||
client->state == NS_CLIENTSTATE_RECURSING)
{
INSIST(client->newstate <= NS_CLIENTSTATE_READING);
/*
* Let the update processing complete.
*/
if (client->nupdates > 0)
return (true);
/*
* We are trying to abort request processing.
*/
if (client->nsends > 0) {
isc_socket_t *sock;
if (TCP_CLIENT(client))
sock = client->tcpsocket;
else
sock = client->udpsocket;
isc_socket_cancel(sock, client->task,
ISC_SOCKCANCEL_SEND);
}
if (! (client->nsends == 0 && client->nrecvs == 0 &&
isc_refcount_current(&client->references) == 0))
{
/*
* Still waiting for I/O cancel completion.
* or lingering references.