#include <dns/view.h>
#include <dns/zone.h>
+#include <ns/client.h>
#include <ns/interfacemgr.h>
#include <ns/log.h>
#include <ns/notify.h>
#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] */
#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.
*/
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) {
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);
}
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, "a);
- } else {
- result = isc_quota_attach(&client->sctx->tcpquota, "a);
- }
- 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.
- */
- return (true);
- }
-
- /*
- * I/O cancel is complete. Burn down all state
- * related to the current request. Ensure that
- * the client is no longer on the recursing list.
- *
- * We need to check whether the client is still linked,
- * because it may already have been removed from the
- * recursing list by ns_client_killoldestquery()
- */
- if (client->state == NS_CLIENTSTATE_RECURSING) {
- LOCK(&manager->reclock);
- if (ISC_LINK_LINKED(client, rlink))
- ISC_LIST_UNLINK(manager->recursing,
- client, rlink);
- UNLOCK(&manager->reclock);
- }
- ns_client_endrequest(client);
-
- client->state = NS_CLIENTSTATE_READING;
- INSIST(client->recursionquota == NULL);
-
- if (NS_CLIENTSTATE_READING == client->newstate) {
- INSIST(client->tcpconn != NULL);
- if (!client->tcpconn->pipelined) {
- client_read(client, false);
- client->newstate = NS_CLIENTSTATE_MAX;
- return (true); /* We're done. */
- } else if (client->mortal) {
- client->newstate = NS_CLIENTSTATE_INACTIVE;
- } else
- return (false);
- }
- }
-
- if (client->state == NS_CLIENTSTATE_READING) {
- /*
- * We are trying to abort the current TCP connection,
- * if any.
- */
- INSIST(client->recursionquota == NULL);
- INSIST(client->newstate <= NS_CLIENTSTATE_READY);
-
- if (client->nreads > 0) {
- dns_tcpmsg_cancelread(&client->tcpmsg);
- /* Still waiting for read cancel completion? */
- if (client->nreads > 0) {
- return (true);
- }
- }
-
- if (client->tcpmsg_valid) {
- dns_tcpmsg_invalidate(&client->tcpmsg);
- client->tcpmsg_valid = false;
- }
-
- /*
- * Soon the client will be ready to accept a new TCP
- * connection or UDP request, but we may have enough
- * clients doing that already. Check whether this client
- * needs to remain active and allow it go inactive if
- * not.
- *
- * UDP clients always go inactive at this point, but a TCP
- * client may need to stay active and return to READY
- * state if no other clients are available to listen
- * for TCP requests on this interface.
- *
- * Regardless, if we're going to FREED state, that means
- * the system is shutting down and we don't need to
- * retain clients.
- */
- if (client->mortal && TCP_CLIENT(client) &&
- client->newstate != NS_CLIENTSTATE_FREED &&
- (client->sctx->options & NS_SERVER_CLIENTTEST) == 0 &&
- isc_refcount_current(&client->interface->ntcpaccepting) == 0)
- {
- /* Nobody else is accepting */
- client->mortal = false;
- client->newstate = NS_CLIENTSTATE_READY;
- }
-
- /*
- * Detach from TCP connection and TCP client quota,
- * if appropriate. If this is the last reference to
- * the TCP connection in our pipeline group, the
- * TCP quota slot will be released.
- */
- if (client->tcpconn) {
- tcpconn_detach(client);
- }
-
- if (client->tcpsocket != NULL) {
- CTRACE("closetcp");
- isc_socket_detach(&client->tcpsocket);
- mark_tcp_active(client, false);
- }
-
- if (client->timerset) {
- (void)isc_timer_reset(client->timer,
- isc_timertype_inactive,
- NULL, NULL, true);
- client->timerset = false;
- }
-
- client->peeraddr_valid = false;
-
- client->state = NS_CLIENTSTATE_READY;
-
- /*
- * We don't need the client; send it to the inactive
- * queue for recycling.
- */
- if (client->mortal) {
- if (client->newstate > NS_CLIENTSTATE_INACTIVE) {
- client->newstate = NS_CLIENTSTATE_INACTIVE;
- }
- }
-
- if (NS_CLIENTSTATE_READY == client->newstate) {
- if (TCP_CLIENT(client)) {
- client_accept(client);
- } else {
- client_udprecv(client);
- }
- client->newstate = NS_CLIENTSTATE_MAX;
- return (true);
- }
- }
-
- if (client->state == NS_CLIENTSTATE_READY) {
- INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
-
- /*
- * We are trying to enter the inactive state.
- */
- if (client->naccepts > 0) {
- isc_socket_cancel(client->tcplistener, client->task,
- ISC_SOCKCANCEL_ACCEPT);
- /* Still waiting for accept cancel completion? */
- if (client->naccepts > 0) {
- return (true);
- }
- }
-
- /* Accept cancel is complete. */
- if (client->nrecvs > 0) {
- isc_socket_cancel(client->udpsocket, client->task,
- ISC_SOCKCANCEL_RECV);
- /* Still waiting for recv cancel completion? */
- if (client->nrecvs > 0) {
- return (true);
- }
- }
-
- /* Still waiting for control event to be delivered */
- if (client->nctls > 0) {
- return (true);
- }
-
- INSIST(client->naccepts == 0);
- INSIST(client->recursionquota == NULL);
- if (client->tcplistener != NULL) {
- isc_socket_detach(&client->tcplistener);
- mark_tcp_active(client, false);
- }
- if (client->udpsocket != NULL) {
- isc_socket_detach(&client->udpsocket);
- }
-
- /* Deactivate the client. */
- if (client->interface != NULL) {
- ns_interface_detach(&client->interface);
- }
-
- if (client->dispatch != NULL) {
- dns_dispatch_detach(&client->dispatch);
- }
-
- client->attributes = 0;
- client->mortal = false;
- client->sendcb = NULL;
-
- if (client->keytag != NULL) {
- isc_mem_put(client->mctx, client->keytag,
- client->keytag_len);
- client->keytag_len = 0;
- }
-
- /*
- * Put the client on the inactive list. If we are aiming for
- * the "freed" state, it will be removed from the inactive
- * list shortly, and we need to keep the manager locked until
- * that has been done, lest the manager decide to reactivate
- * the dying client inbetween.
- */
- client->state = NS_CLIENTSTATE_INACTIVE;
- INSIST(client->recursionquota == NULL);
-
- if (client->state == client->newstate) {
- client->newstate = NS_CLIENTSTATE_MAX;
- if ((client->sctx->options &
- NS_SERVER_CLIENTTEST) == 0 &&
- manager != NULL && !manager->exiting)
- {
- ISC_QUEUE_PUSH(manager->inactive, client,
- ilink);
- }
- if (client->needshutdown) {
- isc_task_shutdown(client->task);
- }
- return (true);
- }
- }
-
- if (client->state == NS_CLIENTSTATE_INACTIVE) {
- INSIST(client->newstate == NS_CLIENTSTATE_FREED);
- /*
- * We are trying to free the client.
- *
- * When "shuttingdown" is true, either the task has received
- * its shutdown event or no shutdown event has ever been
- * set up. Thus, we have no outstanding shutdown
- * event at this point.
- */
- REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
-
- INSIST(client->recursionquota == NULL);
- INSIST(!ISC_QLINK_LINKED(client, ilink));
-
- if (manager != NULL) {
- LOCK(&manager->listlock);
- ISC_LIST_UNLINK(manager->clients, client, link);
- LOCK(&manager->lock);
- if (manager->exiting &&
- ISC_LIST_EMPTY(manager->clients))
- destroy_manager = true;
- UNLOCK(&manager->lock);
- UNLOCK(&manager->listlock);
- }
-
- ns_query_free(client);
- isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
- isc_event_free((isc_event_t **)&client->sendevent);
- isc_event_free((isc_event_t **)&client->recvevent);
- isc_timer_detach(&client->timer);
- if (client->delaytimer != NULL)
- isc_timer_detach(&client->delaytimer);
-
- if (client->tcpbuf != NULL)
- isc_mem_put(client->mctx, client->tcpbuf,
- TCP_BUFFER_SIZE);
- if (client->opt != NULL) {
- INSIST(dns_rdataset_isassociated(client->opt));
- dns_rdataset_disassociate(client->opt);
- dns_message_puttemprdataset(client->message,
- &client->opt);
- }
- if (client->keytag != NULL) {
- isc_mem_put(client->mctx, client->keytag,
- client->keytag_len);
- client->keytag_len = 0;
- }
-
- dns_message_destroy(&client->message);
-
- /*
- * Detaching the task must be done after unlinking from
- * the manager's lists because the manager accesses
- * client->task.
- */
- if (client->task != NULL)
- isc_task_detach(&client->task);
-
- CTRACE("free");
- client->magic = 0;
-
- /*
- * Check that there are no other external references to
- * the memory context.
- */
- if ((client->sctx->options & NS_SERVER_CLIENTTEST) != 0 &&
- isc_mem_references(client->mctx) != 1)
- {
- isc_mem_stats(client->mctx, stderr);
- INSIST(0);
- ISC_UNREACHABLE();
- }
-
- /*
- * Destroy the fetchlock mutex that was created in
- * ns_query_init().
- */
- isc_mutex_destroy(&client->query.fetchlock);
-
- if (client->sctx != NULL)
- ns_server_detach(&client->sctx);
-
- isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
- }
-
- if (destroy_manager && manager != NULL)
- clientmgr_destroy(manager);
-
- return (true);
-}
-
-/*%
- * The client's task has received the client's control event
- * as part of the startup process.
- */
-static void
-client_start(isc_task_t *task, isc_event_t *event) {
- ns_client_t *client = (ns_client_t *) event->ev_arg;
-
- INSIST(task == client->task);
-
- UNUSED(task);
-
- INSIST(client->nctls == 1);
- client->nctls--;
-
- if (exit_check(client))
- return;
-
- if (TCP_CLIENT(client)) {
- if (client->tcpconn != NULL) {
- client_read(client, false);
- } else {
- client_accept(client);
- }
- } else {
- client_udprecv(client);
- }
-}
-
-/*%
- * The client's task has received a shutdown event.
- */
-static void
-client_shutdown(isc_task_t *task, isc_event_t *event) {
- ns_client_t *client;
-
- REQUIRE(event != NULL);
- REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
- client = event->ev_arg;
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(task == client->task);
-
- UNUSED(task);
-
- CTRACE("shutdown");
-
- isc_event_free(&event);
-
- if (client->shutdown != NULL) {
- (client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
- client->shutdown = NULL;
- client->shutdown_arg = NULL;
- }
-
- if (ISC_QLINK_LINKED(client, ilink))
- ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
-
- client->newstate = NS_CLIENTSTATE_FREED;
- client->needshutdown = false;
- (void)exit_check(client);
+ UNUSED(client);
+ UNUSED(seconds);
+ /* XXXWPK TODO use netmgr to set timeout */
}
static void
CTRACE("endrequest");
- if (client->next != NULL) {
- (client->next)(client);
- client->next = NULL;
+ LOCK(&client->manager->reclock);
+ if (ISC_LINK_LINKED(client, rlink)) {
+ ISC_LIST_UNLINK(client->manager->recursing, client, rlink);
+ }
+ UNLOCK(&client->manager->reclock);
+
+ if (client->cleanup != NULL) {
+ (client->cleanup)(client);
+ client->cleanup = NULL;
}
if (client->view != NULL) {
dns_ecs_init(&client->ecs);
dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
+ /*
+ * Clean up from recursion - normally this would be done in
+ * fetch_callback(), but if we're shutting down and canceling then
+ * it might not have happened.
+ */
if (client->recursionquota != NULL) {
isc_quota_detach(&client->recursionquota);
ns_stats_decrement(client->sctx->nsstats,
ns_statscounter_recursclients);
}
+
/*
- * Clear all client attributes that are specific to
- * the request; that's all except the TCP flag.
+ * Clear all client attributes that are specific to the request
*/
- client->attributes &= NS_CLIENTATTR_TCP;
+ client->attributes = 0;
#ifdef ENABLE_AFL
if (client->sctx->fuzznotify != NULL &&
(client->sctx->fuzztype == isc_fuzz_client ||
client->sctx->fuzznotify();
}
#endif /* ENABLE_AFL */
-
}
void
-ns_client_next(ns_client_t *client, isc_result_t result) {
- int newstate;
-
+ns_client_drop(ns_client_t *client, isc_result_t result) {
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
- client->state == NS_CLIENTSTATE_RECURSING ||
- client->state == NS_CLIENTSTATE_READING);
-
- CTRACE("next");
+ client->state == NS_CLIENTSTATE_RECURSING);
- if (result != ISC_R_SUCCESS)
+ CTRACE("drop");
+ if (result != ISC_R_SUCCESS) {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"request failed: %s", isc_result_totext(result));
-
- /*
- * An error processing a TCP request may have left
- * the connection out of sync. To be safe, we always
- * sever the connection when result != ISC_R_SUCCESS.
- */
- if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
- newstate = NS_CLIENTSTATE_READING;
- else
- newstate = NS_CLIENTSTATE_READY;
-
- if (client->newstate > newstate)
- client->newstate = newstate;
- (void)exit_check(client);
+ }
}
-
static void
-client_senddone(isc_task_t *task, isc_event_t *event) {
- ns_client_t *client;
- isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ ns_client_t *client = cbarg;
- REQUIRE(sevent != NULL);
- REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
- client = sevent->ev_arg;
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(task == client->task);
- REQUIRE(sevent == client->sendevent);
-
- UNUSED(task);
+ REQUIRE(client->handle == handle);
CTRACE("senddone");
-
- if (sevent->result != ISC_R_SUCCESS)
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
- "error sending response: %s",
- isc_result_totext(sevent->result));
-
- INSIST(client->nsends > 0);
- client->nsends--;
-
- if (client->tcpbuf != NULL) {
- INSIST(TCP_CLIENT(client));
- isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
- client->tcpbuf = NULL;
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "send failed: %s", isc_result_totext(result));
}
- ns_client_next(client, ISC_R_SUCCESS);
+ isc_nmhandle_unref(handle);
}
/*%
static isc_result_t
client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
isc_buffer_t *tcpbuffer, uint32_t length,
- unsigned char *sendbuf, unsigned char **datap)
+ unsigned char **datap)
{
unsigned char *data;
uint32_t bufsize;
isc_result_t result;
- INSIST(datap != NULL);
- INSIST((tcpbuffer == NULL && length != 0) ||
- (tcpbuffer != NULL && length == 0));
+ REQUIRE(datap != NULL);
+ REQUIRE((tcpbuffer == NULL && length != 0) ||
+ (tcpbuffer != NULL && length == 0));
if (TCP_CLIENT(client)) {
INSIST(client->tcpbuf == NULL);
- if (length + 2 > TCP_BUFFER_SIZE) {
+ if (length + 2 > NS_CLIENT_TCP_BUFFER_SIZE) {
result = ISC_R_NOSPACE;
goto done;
}
- client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
+ client->tcpbuf = isc_mem_get(client->mctx,
+ NS_CLIENT_TCP_BUFFER_SIZE);
data = client->tcpbuf;
if (tcpbuffer != NULL) {
- isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
- isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
+ isc_buffer_init(tcpbuffer, data,
+ NS_CLIENT_TCP_BUFFER_SIZE);
+ isc_buffer_init(buffer, data,
+ NS_CLIENT_TCP_BUFFER_SIZE);
} else {
- isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
+ isc_buffer_init(buffer, data,
+ NS_CLIENT_TCP_BUFFER_SIZE);
INSIST(length <= 0xffff);
- isc_buffer_putuint16(buffer, (uint16_t)length);
}
} else {
- data = sendbuf;
+ data = client->sendbuf;
if ((client->attributes & NS_CLIENTATTR_HAVECOOKIE) == 0) {
if (client->view != NULL)
bufsize = client->view->nocookieudp;
bufsize = client->udpsize;
if (bufsize > client->udpsize)
bufsize = client->udpsize;
- if (bufsize > SEND_BUFFER_SIZE)
- bufsize = SEND_BUFFER_SIZE;
+ if (bufsize > NS_CLIENT_SEND_BUFFER_SIZE)
+ bufsize = NS_CLIENT_SEND_BUFFER_SIZE;
if (length > bufsize) {
result = ISC_R_NOSPACE;
goto done;
static isc_result_t
client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
- struct in6_pktinfo *pktinfo;
- isc_result_t result;
isc_region_t r;
- isc_sockaddr_t *address;
- isc_socket_t *sock;
- isc_netaddr_t netaddr;
- int match;
- unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
-
- if (TCP_CLIENT(client)) {
- sock = client->tcpsocket;
- address = NULL;
- } else {
- dns_aclenv_t *env =
- ns_interfacemgr_getaclenv(client->interface->mgr);
-
- sock = client->udpsocket;
- address = &client->peeraddr;
-
- isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
- if (client->sctx->blackholeacl != NULL &&
- (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
- env, &match, NULL) == ISC_R_SUCCESS) &&
- match > 0)
- {
- return (DNS_R_BLACKHOLED);
- }
- sockflags |= ISC_SOCKFLAG_NORETRY;
- }
-
- if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
- (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
- pktinfo = &client->pktinfo;
- else
- pktinfo = NULL;
-
- if (client->dispatch != NULL) {
- isc_dscp_t dscp = dns_dispatch_getdscp(client->dispatch);
- if (dscp != -1) {
- client->dscp = dscp;
- }
- }
-
- if (client->dscp == -1) {
- client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
- client->sendevent->dscp = 0;
- } else {
- client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
- client->sendevent->dscp = client->dscp;
- }
isc_buffer_usedregion(buffer, &r);
- /*
- * If this is a UDP client and the IPv6 packet can't be
- * encapsulated without generating a PTB on a 1500 octet
- * MTU link force fragmentation at 1280 if it is a IPv6
- * response.
- */
- client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_USEMINMTU;
- if (!TCP_CLIENT(client) && r.length > 1432)
- client->sendevent->attributes |= ISC_SOCKEVENTATTR_USEMINMTU;
-
- CTRACE("sendto");
+ INSIST(client->handle != NULL);
- result = isc_socket_sendto2(sock, &r, client->task,
- address, pktinfo,
- client->sendevent, sockflags);
- if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
- client->nsends++;
- if (result == ISC_R_SUCCESS)
- client_senddone(client->task,
- (isc_event_t *)client->sendevent);
- result = ISC_R_SUCCESS;
- }
- return (result);
+ return (isc_nm_send(client->handle, &r, client_senddone, client));
}
void
isc_buffer_t buffer;
isc_region_t r;
isc_region_t *mr;
- unsigned char sendbuf[SEND_BUFFER_SIZE];
REQUIRE(NS_CLIENT_VALID(client));
goto done;
}
- result = client_allocsendbuf(client, &buffer, NULL, mr->length,
- sendbuf, &data);
+ result = client_allocsendbuf(client, &buffer, NULL, mr->length, &data);
if (result != ISC_R_SUCCESS)
goto done;
done:
if (client->tcpbuf != NULL) {
- isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ isc_mem_put(client->mctx, client->tcpbuf,
+ NS_CLIENT_TCP_BUFFER_SIZE);
client->tcpbuf = NULL;
}
- ns_client_next(client, result);
+
+ ns_client_drop(client, result);
}
-static void
-client_send(ns_client_t *client) {
+void
+ns_client_send(ns_client_t *client) {
isc_result_t result;
unsigned char *data;
- isc_buffer_t buffer;
- isc_buffer_t tcpbuffer;
+ isc_buffer_t buffer = { .magic = 0 };
+ isc_buffer_t tcpbuffer = { .magic = 0 };
isc_region_t r;
dns_compress_t cctx;
bool cleanup_cctx = false;
- unsigned char sendbuf[SEND_BUFFER_SIZE];
unsigned int render_opts;
unsigned int preferred_glue;
bool opt_included = false;
isc_region_t zr;
#endif /* HAVE_DNSTAP */
+ /*
+ * XXXWPK TODO
+ * Delay the response according to the -T delay option
+ */
+
REQUIRE(NS_CLIENT_VALID(client));
+ /*
+ * We need to do it to make sure the client and handle
+ * won't disappear from under us with client_senddone.
+ */
- env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
CTRACE("send");
/*
* XXXRTH The following doesn't deal with TCP buffer resizing.
*/
- result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
- sendbuf, &data);
+ result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0, &data);
if (result != ISC_R_SUCCESS)
goto done;
client->sendcb(&buffer);
} else if (TCP_CLIENT(client)) {
isc_buffer_usedregion(&buffer, &r);
- isc_buffer_putuint16(&tcpbuffer, (uint16_t) r.length);
isc_buffer_add(&tcpbuffer, r.length);
#ifdef HAVE_DNSTAP
if (client->view != NULL) {
/* don't count the 2-octet length header */
respsize = isc_buffer_usedlength(&tcpbuffer) - 2;
+
+ isc_nmhandle_ref(client->handle);
result = client_sendpkg(client, &tcpbuffer);
switch (isc_sockaddr_pf(&client->peeraddr)) {
#endif /* HAVE_DNSTAP */
respsize = isc_buffer_usedlength(&buffer);
+
+ isc_nmhandle_ref(client->handle);
result = client_sendpkg(client, &buffer);
switch (isc_sockaddr_pf(&client->peeraddr)) {
ns_stats_increment(client->sctx->nsstats,
ns_statscounter_truncatedresp);
- if (result == ISC_R_SUCCESS)
+ if (result == ISC_R_SUCCESS) {
return;
+ }
done:
if (client->tcpbuf != NULL) {
- isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ isc_mem_put(client->mctx, client->tcpbuf,
+ NS_CLIENT_TCP_BUFFER_SIZE);
client->tcpbuf = NULL;
}
if (cleanup_cctx)
dns_compress_invalidate(&cctx);
-
- ns_client_next(client, result);
-}
-
-/*
- * Completes the sending of a delayed client response.
- */
-static void
-client_delay(isc_task_t *task, isc_event_t *event) {
- ns_client_t *client;
-
- REQUIRE(event != NULL);
- REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
- event->ev_type == ISC_TIMEREVENT_IDLE);
- client = event->ev_arg;
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(task == client->task);
- REQUIRE(client->delaytimer != NULL);
-
- UNUSED(task);
-
- CTRACE("client_delay");
-
- isc_event_free(&event);
- isc_timer_detach(&client->delaytimer);
-
- client_send(client);
- ns_client_detach(&client);
-}
-
-void
-ns_client_send(ns_client_t *client) {
- /*
- * Delay the response according to the -T delay option
- */
- if (client->sctx->delay != 0) {
- ns_client_t *dummy = NULL;
- isc_result_t result;
- isc_interval_t interval;
-
- /*
- * Replace ourselves if we have not already been replaced.
- */
- if (!client->mortal) {
- result = ns_client_replace(client);
- if (result != ISC_R_SUCCESS)
- goto nodelay;
- }
-
- ns_client_attach(client, &dummy);
- if (client->sctx->delay >= 1000)
- isc_interval_set(&interval, client->sctx->delay / 1000,
- (client->sctx->delay % 1000) * 1000000);
- else
- isc_interval_set(&interval, 0,
- client->sctx->delay * 1000000);
- result = isc_timer_create(client->manager->timermgr,
- isc_timertype_once, NULL, &interval,
- client->task, client_delay,
- client, &client->delaytimer);
- if (result == ISC_R_SUCCESS)
- return;
-
- ns_client_detach(&dummy);
- }
-
- nodelay:
- client_send(client);
}
#if NS_CLIENT_DROPPORT
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
"dropped error (%.*s) response: suspicious port",
(int)isc_buffer_usedlength(&b), buf);
- ns_client_next(client, ISC_R_SUCCESS);
+ ns_client_drop(client, ISC_R_SUCCESS);
return;
}
#endif
ns_statscounter_ratedropped);
ns_stats_increment(client->sctx->nsstats,
ns_statscounter_dropped);
- ns_client_next(client, DNS_R_DROP);
+ ns_client_drop(client, DNS_R_DROP);
return;
}
}
*/
result = dns_message_reply(message, false);
if (result != ISC_R_SUCCESS) {
- ns_client_next(client, result);
+ ns_client_drop(client, result);
return;
}
}
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"possible error packet loop, "
"FORMERR dropped");
- ns_client_next(client, result);
+ ns_client_drop(client, result);
return;
}
client->formerrcache.addr = client->peeraddr;
REQUIRE(opt != NULL && *opt == NULL);
REQUIRE(message != NULL);
- env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
view = client->view;
resolver = (view != NULL) ? view->resolver : NULL;
if (resolver != NULL)
case DNS_OPT_TCP_KEEPALIVE:
if (!USEKEEPALIVE(client))
ns_stats_increment(
- client->sctx->nsstats,
- ns_statscounter_keepaliveopt);
+ client->sctx->nsstats,
+ ns_statscounter_keepaliveopt);
client->attributes |=
NS_CLIENTATTR_USEKEEPALIVE;
isc_buffer_forward(&optbuf, optlen);
}
}
- ns_stats_increment(client->sctx->nsstats, ns_statscounter_edns0in);
- client->attributes |= NS_CLIENTATTR_WANTOPT;
+ ns_stats_increment(client->sctx->nsstats, ns_statscounter_edns0in);
+ client->attributes |= NS_CLIENTATTR_WANTOPT;
+
+ return (result);
+}
+
+void
+ns__client_reset_cb(void *client0) {
+ ns_client_t *client = client0;
+
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "reset client");
+
+ ns_client_endrequest(client);
+ if (client->tcpbuf != NULL) {
+ isc_mem_put(client->mctx, client->tcpbuf,
+ NS_CLIENT_TCP_BUFFER_SIZE);
+ }
+
+ if (client->keytag != NULL) {
+ isc_mem_put(client->mctx, client->keytag,
+ client->keytag_len);
+ client->keytag_len = 0;
+ }
+
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+}
+
+void
+ns__client_put_cb(void *client0) {
+ ns_client_t *client = client0;
+
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "freeing client");
+
+ /*
+ * Call this first because it requires a valid client.
+ */
+ ns_query_free(client);
+
+ client->magic = 0;
+ client->shuttingdown = true;
+
+ if (client->manager != NULL) {
+ clientmgr_detach(&client->manager);
+ }
+
+ isc_mem_put(client->mctx, client->recvbuf, NS_CLIENT_RECV_BUFFER_SIZE);
+ if (client->opt != NULL) {
+ INSIST(dns_rdataset_isassociated(client->opt));
+ dns_rdataset_disassociate(client->opt);
+ dns_message_puttemprdataset(client->message,
+ &client->opt);
+ }
+
+ dns_message_destroy(&client->message);
+
+ /*
+ * Detaching the task must be done after unlinking from
+ * the manager's lists because the manager accesses
+ * client->task.
+ */
+ if (client->task != NULL) {
+ isc_task_detach(&client->task);
+ }
+
+ /*
+ * Destroy the fetchlock mutex that was created in
+ * ns_query_init().
+ */
+ isc_mutex_destroy(&client->query.fetchlock);
- return (result);
+ if (client->sctx != NULL) {
+ ns_server_detach(&client->sctx);
+ }
+
+ if (client->mctx != NULL) {
+ isc_mem_detach(&client->mctx);
+ }
}
/*
* or tcpmsg (TCP case).
*/
void
-ns__client_request(isc_task_t *task, isc_event_t *event) {
+ns__client_request(isc_nmhandle_t *handle, isc_region_t *region, void *arg) {
ns_client_t *client;
- isc_socketevent_t *sevent;
+ bool newclient = false;
+ ns_clientmgr_t *mgr;
+ ns_interface_t *ifp;
isc_result_t result;
isc_result_t sigresult = ISC_R_SUCCESS;
isc_buffer_t *buffer;
#ifdef HAVE_DNSTAP
dns_dtmsgtype_t dtmsgtype;
#endif
+ ifp = (ns_interface_t *) arg;
- REQUIRE(event != NULL);
- client = event->ev_arg;
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(task == client->task);
+ mgr = ifp->clientmgr;
+ REQUIRE(VALID_MANAGER(mgr));
+
+ client = isc_nmhandle_getdata(handle);
+ if (client == NULL) {
+ client = isc_nmhandle_getextra(handle);
+
+ result = ns__client_setup(client, mgr, true);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "allocate new client");
+ } else {
+ result = ns__client_setup(client, NULL, false);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+ }
+
+ client->state = NS_CLIENTSTATE_READY;
+ client->dscp = ifp->dscp;
+
+ isc_task_pause(client->task);
+ if (client->handle == NULL) {
+ isc_nmhandle_setdata(handle, client,
+ ns__client_reset_cb, ns__client_put_cb);
+ client->handle = handle;
+ }
+ if (isc_nmhandle_is_stream(handle)) {
+ client->attributes |= NS_CLIENTATTR_TCP;
+ unsigned int curr_tcpquota =
+ isc_quota_getused(&client->sctx->tcpquota);
+ ns_stats_update_if_greater(client->sctx->nsstats,
+ ns_statscounter_tcphighwater,
+ curr_tcpquota);
+ }
INSIST(client->recursionquota == NULL);
- INSIST(client->state == (TCP_CLIENT(client) ?
- NS_CLIENTSTATE_READING :
- NS_CLIENTSTATE_READY));
+ INSIST(client->state == NS_CLIENTSTATE_READY);
ns_client_requests++;
- if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
- INSIST(!TCP_CLIENT(client));
- sevent = (isc_socketevent_t *)event;
- REQUIRE(sevent == client->recvevent);
- isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
- isc_buffer_add(&tbuffer, sevent->n);
- buffer = &tbuffer;
- result = sevent->result;
- if (result == ISC_R_SUCCESS) {
- client->peeraddr = sevent->address;
- client->peeraddr_valid = true;
- }
- if ((sevent->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) {
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(90),
- "received DSCP %d", sevent->dscp);
- if (client->dscp == -1)
- client->dscp = sevent->dscp;
- }
- if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
- client->attributes |= NS_CLIENTATTR_PKTINFO;
- client->pktinfo = sevent->pktinfo;
- }
- if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
- client->attributes |= NS_CLIENTATTR_MULTICAST;
- client->nrecvs--;
- } else {
- INSIST(TCP_CLIENT(client));
- INSIST(client->tcpconn != NULL);
- REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
- REQUIRE(event->ev_sender == &client->tcpmsg);
- buffer = &client->tcpmsg.buffer;
- result = client->tcpmsg.result;
- INSIST(client->nreads == 1);
- /*
- * client->peeraddr was set when the connection was accepted.
- */
- client->nreads--;
- }
+ isc_buffer_init(&tbuffer, region->base, region->length);
+ isc_buffer_add(&tbuffer, region->length);
+ buffer = &tbuffer;
+
+ client->peeraddr = isc_nmhandle_peeraddr(client->handle);
+
+ client->peeraddr_valid = true;
reqsize = isc_buffer_usedlength(buffer);
- /* don't count the length header */
- if (TCP_CLIENT(client))
- reqsize -= 2;
- if (exit_check(client)) {
- return;
- }
- client->state = client->newstate = NS_CLIENTSTATE_WORKING;
+ client->state = NS_CLIENTSTATE_WORKING;
- isc_task_getcurrenttimex(task, &client->requesttime);
+ TIME_NOW(&client->requesttime);
client->tnow = client->requesttime;
client->now = isc_time_seconds(&client->tnow);
- if (result != ISC_R_SUCCESS) {
- if (TCP_CLIENT(client)) {
- ns_client_next(client, result);
- } else {
- if (result != ISC_R_CANCELED)
- isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT,
- ISC_LOG_ERROR,
- "UDP client handler shutting "
- "down due to fatal receive "
- "error: %s",
- isc_result_totext(result));
- isc_task_shutdown(client->task);
- }
- return;
- }
-
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
#if NS_CLIENT_DROPPORT
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
"dropped request: suspicious port");
- ns_client_next(client, ISC_R_SUCCESS);
+ isc_task_unpause(client->task);
return;
}
#endif
* Check the blackhole ACL for UDP only, since TCP is done in
* client_newconn.
*/
- env = ns_interfacemgr_getaclenv(client->interface->mgr);
- if (!TCP_CLIENT(client)) {
+ env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
+ if (newclient) {
if (client->sctx->blackholeacl != NULL &&
(dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
env, &match, NULL) == ISC_R_SUCCESS) &&
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
"blackholed UDP datagram");
- ns_client_next(client, ISC_R_SUCCESS);
+ isc_task_unpause(client->task);
return;
}
}
- /*
- * Silently drop multicast requests for the present.
- * XXXMPA revisit this as mDNS spec was published.
- */
- if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
- "dropping multicast request");
- ns_client_next(client, DNS_R_REFUSED);
- return;
- }
-
result = dns_message_peekheader(buffer, &id, &flags);
if (result != ISC_R_SUCCESS) {
/*
* There isn't enough header to determine whether
* this was a request or a response. Drop it.
*/
- ns_client_next(client, result);
+ isc_task_unpause(client->task);
return;
}
* If it's a TCP response, discard it here.
*/
if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
- if (TCP_CLIENT(client)) {
- CTRACE("unexpected response");
- ns_client_next(client, DNS_R_FORMERR);
- return;
- } else {
- dns_dispatch_importrecv(client->dispatch, event);
- ns_client_next(client, ISC_R_SUCCESS);
- return;
- }
+ CTRACE("unexpected response");
+ isc_task_unpause(client->task);
+ return;
}
/*
result = DNS_R_FORMERR;
}
ns_client_error(client, result);
+ isc_task_unpause(client->task);
return;
}
/*
- * Pipeline TCP query processing.
+ * Disable pipelined TCP query processing if necessary.
*/
if (TCP_CLIENT(client) &&
- client->message->opcode != dns_opcode_query)
+ (client->message->opcode != dns_opcode_query ||
+ (client->sctx->keepresporder != NULL &&
+ dns_acl_allowed(&netaddr, NULL,
+ client->sctx->keepresporder, env))))
{
- client->tcpconn->pipelined = false;
- }
- if (TCP_CLIENT(client) && client->tcpconn->pipelined) {
- /*
- * We're pipelining. Replace the client; the
- * replacement can read the TCP socket looking
- * for new messages and this one can process the
- * current message asynchronously.
- *
- * There will now be at least three clients using this
- * TCP socket - one accepting new connections,
- * one reading an existing connection to get new
- * messages, and one answering the message already
- * received.
- */
- result = ns_client_replace(client);
- if (result != ISC_R_SUCCESS) {
- client->tcpconn->pipelined = false;
- }
+ isc_nm_tcpdns_sequential(handle);
}
dns_opcodestats_increment(client->sctx->opcodestats,
client->message->rcode = dns_rcode_noerror;
- /* RFC1123 section 6.1.3.2 */
- if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
- client->message->flags &= ~DNS_MESSAGEFLAG_RD;
-
/*
* Deal with EDNS.
*/
- if ((client->sctx->options & NS_SERVER_NOEDNS) != 0)
+ if ((client->sctx->options & NS_SERVER_NOEDNS) != 0) {
opt = NULL;
- else
+ } else {
opt = dns_message_getopt(client->message);
+ }
client->ecs.source = 0;
client->ecs.scope = 0;
*/
if ((client->sctx->options & NS_SERVER_EDNSFORMERR) != 0) {
ns_client_error(client, DNS_R_FORMERR);
+ isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_EDNSNOTIMP) != 0) {
ns_client_error(client, DNS_R_NOTIMP);
+ isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_EDNSREFUSED) != 0) {
ns_client_error(client, DNS_R_REFUSED);
+ isc_task_unpause(client->task);
return;
}
* Are we dropping all EDNS queries?
*/
if ((client->sctx->options & NS_SERVER_DROPEDNS) != 0) {
- ns_client_next(client, ISC_R_SUCCESS);
+ ns_client_drop(client, ISC_R_SUCCESS);
+ isc_task_unpause(client->task);
return;
}
result = process_opt(client, opt);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
+ isc_task_unpause(client->task);
return;
+ }
}
if (client->message->rdclass == 0) {
result = dns_message_reply(client->message, true);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
+ isc_task_unpause(client->task);
return;
}
- if (notimp)
+
+ if (notimp) {
client->message->rcode = dns_rcode_notimp;
+ }
+
ns_client_send(client);
+ isc_task_unpause(client->task);
return;
}
+
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"message class could not be determined");
ns_client_dumpmessage(client,
"message class could not be determined");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
+ isc_task_unpause(client->task);
return;
}
* receiving socket (this needs a system call and can be heavy).
* For IPv6 UDP queries, we get this from the pktinfo structure (if
* supported).
+ *
* If all the attempts fail (this can happen due to memory shortage,
* etc), we regard this as an error for safety.
*/
- if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
+ if ((client->manager->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
isc_netaddr_fromsockaddr(&client->destaddr,
- &client->interface->addr);
+ &client->manager->interface->addr);
else {
- isc_sockaddr_t sockaddr;
- result = ISC_R_FAILURE;
-
- if (TCP_CLIENT(client))
- result = isc_socket_getsockname(client->tcpsocket,
- &sockaddr);
- if (result == ISC_R_SUCCESS)
- isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
- if (result != ISC_R_SUCCESS &&
- client->interface->addr.type.sa.sa_family == AF_INET6 &&
- (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
- /*
- * XXXJT technically, we should convert the receiving
- * interface ID to a proper scope zone ID. However,
- * due to the fact there is no standard API for this,
- * we only handle link-local addresses and use the
- * interface index as link ID. Despite the assumption,
- * it should cover most typical cases.
- */
- isc_netaddr_fromin6(&client->destaddr,
- &client->pktinfo.ipi6_addr);
- if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
- isc_netaddr_setzone(&client->destaddr,
- client->pktinfo.ipi6_ifindex);
- result = ISC_R_SUCCESS;
- }
- if (result != ISC_R_SUCCESS) {
- UNEXPECTED_ERROR(__FILE__, __LINE__,
- "failed to get request's "
- "destination: %s",
- isc_result_totext(result));
- ns_client_next(client, ISC_R_SUCCESS);
- return;
- }
+ isc_sockaddr_t sockaddr =
+ isc_nmhandle_localaddr(client->handle);
+ isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
}
isc_sockaddr_fromnetaddr(&client->destsockaddr, &client->destaddr, 0);
"no matching view in class '%s'", classname);
ns_client_dumpmessage(client, "no matching view in class");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
+ isc_task_unpause(client->task);
return;
}
"request has invalid signature: %s (%s)",
isc_result_totext(result), tsigrcode);
}
+
/*
* Accept update messages signed by unknown keys so that
* update forwarding works transparently through slaves
* that don't have all the same keys as the master.
*/
if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
- client->message->opcode == dns_opcode_update)) {
+ client->message->opcode == dns_opcode_update))
+ {
ns_client_error(client, sigresult);
+ isc_task_unpause(client->task);
return;
}
}
ns_client_checkaclsilent(client, &client->destaddr,
client->view->cacheonacl,
true) == ISC_R_SUCCESS)
+ {
ra = true;
+ }
if (ra == true) {
client->attributes |= NS_CLIENTATTR_RA;
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
+ isc_nmhandle_ref(client->handle);
ns_query_start(client);
break;
case dns_opcode_update:
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
ns_client_settimeout(client, 60);
+ isc_nmhandle_ref(client->handle);
ns_update_start(client, sigresult);
break;
case dns_opcode_notify:
CTRACE("notify");
ns_client_settimeout(client, 60);
+ isc_nmhandle_ref(client->handle);
ns_notify_start(client);
break;
case dns_opcode_iquery:
ns_client_error(client, DNS_R_NOTIMP);
break;
default:
- CTRACE("unknown opcode");
- ns_client_error(client, DNS_R_NOTIMP);
- }
-}
-
-static void
-client_timeout(isc_task_t *task, isc_event_t *event) {
- ns_client_t *client;
-
- REQUIRE(event != NULL);
- REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
- event->ev_type == ISC_TIMEREVENT_IDLE);
- client = event->ev_arg;
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(task == client->task);
- REQUIRE(client->timer != NULL);
-
- UNUSED(task);
-
- CTRACE("timeout");
-
- isc_event_free(&event);
-
- if (client->shutdown != NULL) {
- (client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
- client->shutdown = NULL;
- client->shutdown_arg = NULL;
- }
-
- if (client->newstate > NS_CLIENTSTATE_READY)
- client->newstate = NS_CLIENTSTATE_READY;
- (void)exit_check(client);
-}
-
-static isc_result_t
-get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
- isc_mem_t *clientmctx;
-#if NMCTXS > 0
- unsigned int nextmctx;
-#endif
-
- MTRACE("clientmctx");
-
- /*
- * Caller must be holding the manager lock.
- */
- if ((manager->sctx->options & NS_SERVER_CLIENTTEST) != 0) {
- isc_mem_create(mctxp);
- isc_mem_setname(*mctxp, "client", NULL);
- return (ISC_R_SUCCESS);
- }
-#if NMCTXS > 0
- nextmctx = manager->nextmctx++;
- if (manager->nextmctx == NMCTXS)
- manager->nextmctx = 0;
-
- INSIST(nextmctx < NMCTXS);
-
- clientmctx = manager->mctxpool[nextmctx];
- if (clientmctx == NULL) {
- isc_mem_create(&clientmctx);
- isc_mem_setname(clientmctx, "client", NULL);
-
- manager->mctxpool[nextmctx] = clientmctx;
- }
-#else
- clientmctx = manager->mctx;
-#endif
-
- isc_mem_attach(clientmctx, mctxp);
-
- return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
- ns_client_t *client;
- isc_result_t result;
- isc_mem_t *mctx = NULL;
-
- /*
- * Caller must be holding the manager lock.
- *
- * Note: creating a client does not add the client to the
- * manager's client list or set the client's manager pointer.
- * The caller is responsible for that.
- */
-
- REQUIRE(clientp != NULL && *clientp == NULL);
-
- result = get_clientmctx(manager, &mctx);
- if (result != ISC_R_SUCCESS)
- return (result);
-
- client = isc_mem_get(mctx, sizeof(*client));
- client->mctx = mctx;
-
- client->sctx = NULL;
- ns_server_attach(manager->sctx, &client->sctx);
-
- client->task = NULL;
- result = isc_task_create(manager->taskmgr, 0, &client->task);
- if (result != ISC_R_SUCCESS)
- goto cleanup_client;
- isc_task_setname(client->task, "client", client);
-
- client->timer = NULL;
- result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
- NULL, NULL, client->task, client_timeout,
- client, &client->timer);
- if (result != ISC_R_SUCCESS)
- goto cleanup_task;
- client->timerset = false;
-
- client->delaytimer = NULL;
-
- client->message = NULL;
- result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
- &client->message);
- if (result != ISC_R_SUCCESS)
- goto cleanup_timer;
-
- /* XXXRTH Hardwired constants */
-
- client->sendevent = isc_socket_socketevent(client->mctx, client,
- ISC_SOCKEVENT_SENDDONE,
- client_senddone, client);
- if (client->sendevent == NULL) {
- result = ISC_R_NOMEMORY;
- goto cleanup_message;
- }
-
- client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
-
- client->recvevent = isc_socket_socketevent(client->mctx, client,
- ISC_SOCKEVENT_RECVDONE,
- ns__client_request, client);
- if (client->recvevent == NULL) {
- result = ISC_R_NOMEMORY;
- goto cleanup_recvbuf;
- }
-
- client->magic = NS_CLIENT_MAGIC;
- client->manager = NULL;
- client->state = NS_CLIENTSTATE_INACTIVE;
- client->newstate = NS_CLIENTSTATE_MAX;
- client->naccepts = 0;
- client->nreads = 0;
- client->nsends = 0;
- client->nrecvs = 0;
- client->nupdates = 0;
- client->nctls = 0;
- isc_refcount_init(&client->references, 0);
- client->attributes = 0;
- client->view = NULL;
- client->dispatch = NULL;
- client->udpsocket = NULL;
- client->tcplistener = NULL;
- client->tcpsocket = NULL;
- client->tcpmsg_valid = false;
- client->tcpbuf = NULL;
- client->opt = NULL;
- client->udpsize = 512;
- client->dscp = -1;
- client->extflags = 0;
- client->ednsversion = -1;
- client->next = NULL;
- client->shutdown = NULL;
- client->shutdown_arg = NULL;
- client->signer = NULL;
- dns_name_init(&client->signername, NULL);
- client->mortal = false;
- client->sendcb = NULL;
- client->tcpconn = NULL;
- client->recursionquota = NULL;
- client->interface = NULL;
- client->peeraddr_valid = false;
- dns_ecs_init(&client->ecs);
- client->needshutdown = ((client->sctx->options &
- NS_SERVER_CLIENTTEST) != 0);
- client->tcpactive = false;
-
- ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
- NS_EVENT_CLIENTCONTROL, client_start, client, client,
- NULL, NULL);
- /*
- * Initialize FORMERR cache to sentinel value that will not match
- * any actual FORMERR response.
- */
- isc_sockaddr_any(&client->formerrcache.addr);
- client->formerrcache.time = 0;
- client->formerrcache.id = 0;
- ISC_LINK_INIT(client, link);
- ISC_LINK_INIT(client, rlink);
- ISC_QLINK_INIT(client, ilink);
- client->keytag = NULL;
- client->keytag_len = 0;
- client->rcode_override = -1; /* not set */
-
- /*
- * We call the init routines for the various kinds of client here,
- * after we have created an otherwise valid client, because some
- * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
- */
- result = ns_query_init(client);
- if (result != ISC_R_SUCCESS)
- goto cleanup_recvevent;
-
- result = isc_task_onshutdown(client->task, client_shutdown, client);
- if (result != ISC_R_SUCCESS)
- goto cleanup_query;
-
- CTRACE("create");
-
- *clientp = client;
-
- return (ISC_R_SUCCESS);
-
- cleanup_query:
- ns_query_free(client);
-
- cleanup_recvevent:
- isc_event_free((isc_event_t **)&client->recvevent);
-
- cleanup_recvbuf:
- isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
-
- isc_event_free((isc_event_t **)&client->sendevent);
-
- client->magic = 0;
-
- cleanup_message:
- dns_message_destroy(&client->message);
-
- cleanup_timer:
- isc_timer_detach(&client->timer);
-
- cleanup_task:
- isc_task_detach(&client->task);
-
- cleanup_client:
- if (client->sctx != NULL)
- ns_server_detach(&client->sctx);
- isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
-
- return (result);
-}
-
-static void
-client_read(ns_client_t *client, bool newconn) {
- isc_result_t result;
-
- CTRACE("read");
-
- result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
- ns__client_request, client);
- if (result != ISC_R_SUCCESS)
- goto fail;
-
- /*
- * Set a timeout to limit the amount of time we will wait
- * for a request on this TCP connection.
- */
- read_settimeout(client, newconn);
-
- client->state = client->newstate = NS_CLIENTSTATE_READING;
- INSIST(client->nreads == 0);
- INSIST(client->recursionquota == NULL);
- client->nreads++;
-
- return;
- fail:
- ns_client_next(client, result);
-}
-
-static void
-client_newconn(isc_task_t *task, isc_event_t *event) {
- isc_result_t result;
- ns_client_t *client = event->ev_arg;
- isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
- dns_aclenv_t *env;
- uint32_t old;
-
- REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
- REQUIRE(NS_CLIENT_VALID(client));
- REQUIRE(client->task == task);
-
- env = ns_interfacemgr_getaclenv(client->interface->mgr);
-
- UNUSED(task);
-
- INSIST(client->state == NS_CLIENTSTATE_READY);
-
- /*
- * The accept() was successful and we're now establishing a new
- * connection. We need to make note of it in the client and
- * interface objects so client objects can do the right thing
- * when going inactive in exit_check() (see comments in
- * client_accept() for details).
- */
- INSIST(client->naccepts == 1);
- client->naccepts--;
-
- old = isc_refcount_decrement(&client->interface->ntcpaccepting);
- INSIST(old > 0);
-
- /*
- * We must take ownership of the new socket before the exit
- * check to make sure it gets destroyed if we decide to exit.
- */
- if (nevent->result == ISC_R_SUCCESS) {
- client->tcpsocket = nevent->newsocket;
- isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
- client->state = NS_CLIENTSTATE_READING;
- INSIST(client->recursionquota == NULL);
-
- (void)isc_socket_getpeername(client->tcpsocket,
- &client->peeraddr);
- client->peeraddr_valid = true;
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
- "new TCP connection");
- } else {
- /*
- * XXXRTH What should we do? We're trying to accept but
- * it didn't work. If we just give up, then TCP
- * service may eventually stop.
- *
- * For now, we just go idle.
- *
- * Going idle is probably the right thing if the
- * I/O was canceled.
- */
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
- "accept failed: %s",
- isc_result_totext(nevent->result));
- tcpconn_detach(client);
+ CTRACE("unknown opcode");
+ ns_client_error(client, DNS_R_NOTIMP);
}
- if (exit_check(client))
- goto freeevent;
-
- if (nevent->result == ISC_R_SUCCESS) {
- int match;
- isc_netaddr_t netaddr;
+ isc_task_unpause(client->task);
+}
- isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+static void
+get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
+ isc_mem_t *clientmctx;
+#if CLIENT_NMCTXS > 0
+ unsigned int nextmctx;
+#endif
- if (client->sctx->blackholeacl != NULL &&
- (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
- env, &match, NULL) == ISC_R_SUCCESS) &&
- match > 0)
- {
- ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
- "blackholed connection attempt");
- client->newstate = NS_CLIENTSTATE_READY;
- (void)exit_check(client);
- goto freeevent;
- }
+ MTRACE("clientmctx");
- INSIST(client->tcpmsg_valid == false);
- dns_tcpmsg_init(client->mctx, client->tcpsocket,
- &client->tcpmsg);
- client->tcpmsg_valid = true;
+#if CLIENT_NMCTXS > 0
+ LOCK(&manager->lock);
+ if (isc_nm_tid()>=0) {
+ nextmctx = isc_nm_tid();
+ } else {
+ nextmctx = manager->nextmctx++;
+ if (manager->nextmctx == CLIENT_NMCTXS)
+ manager->nextmctx = 0;
- /*
- * Let a new client take our place immediately, before
- * we wait for a request packet. If we don't,
- * telnetting to port 53 (once per CPU) will
- * deny service to legitimate TCP clients.
- */
- result = ns_client_replace(client);
- if (result == ISC_R_SUCCESS &&
- (client->sctx->keepresporder == NULL ||
- !dns_acl_allowed(&netaddr, NULL,
- client->sctx->keepresporder, env)))
- {
- client->tcpconn->pipelined = true;
- }
+ INSIST(nextmctx < CLIENT_NMCTXS);
+ }
- client_read(client, true);
+ clientmctx = manager->mctxpool[nextmctx];
+ if (clientmctx == NULL) {
+ isc_mem_create(&clientmctx);
+ isc_mem_setname(clientmctx, "client", NULL);
+ manager->mctxpool[nextmctx] = clientmctx;
}
+ UNLOCK(&manager->lock);
+#else
+ clientmctx = manager->mctx;
+#endif
- freeevent:
- isc_event_free(&event);
+ isc_mem_attach(clientmctx, mctxp);
}
-static void
-client_accept(ns_client_t *client) {
+isc_result_t
+ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) {
isc_result_t result;
- CTRACE("accept");
/*
- * Set up a new TCP connection. This means try to attach to the
- * TCP client quota (tcp-clients), but fail if we're over quota.
+ * Caller must be holding the manager lock.
+ *
+ * Note: creating a client does not add the client to the
+ * manager's client list or set the client's manager pointer.
+ * The caller is responsible for that.
*/
- result = tcpconn_init(client, false);
- if (result != ISC_R_SUCCESS) {
- bool exit;
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
- "TCP client quota reached: %s",
- isc_result_totext(result));
-
- /*
- * We have exceeded the system-wide TCP client quota. But,
- * we can't just block this accept in all cases, because if
- * we did, a heavy TCP load on other interfaces might cause
- * this interface to be starved, with no clients able to
- * accept new connections.
- *
- * So, we check here to see if any other clients are
- * already servicing TCP queries on this interface (whether
- * accepting, reading, or processing). If we find that at
- * least one client other than this one is active, then
- * it's okay *not* to call accept - we can let this
- * client go inactive and another will take over when it's
- * done.
- *
- * If there aren't enough active clients on the interface,
- * then we can be a little bit flexible about the quota.
- * We'll allow *one* extra client through to ensure we're
- * listening on every interface; we do this by setting the
- * 'force' option to tcpconn_init().
- *
- * (Note: In practice this means that the real TCP client
- * quota is tcp-clients plus the number of listening
- * interfaces plus 1.)
- */
- exit = (isc_refcount_current(&client->interface->ntcpactive) >
- (client->tcpactive ? 1U : 0U));
- if (exit) {
- client->newstate = NS_CLIENTSTATE_INACTIVE;
- (void)exit_check(client);
- return;
- }
+ REQUIRE(NS_CLIENT_VALID(client) || (new && client != NULL));
+ REQUIRE(VALID_MANAGER(mgr) || !new);
- result = tcpconn_init(client, true);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- }
+ if (new) {
+ *client = (ns_client_t) {
+ .magic = 0
+ };
- /* TCP high-water stats update. */
- unsigned int curr_tcpquota = isc_quota_getused(&client->sctx->tcpquota);
- ns_stats_update_if_greater(client->sctx->nsstats,
- ns_statscounter_tcphighwater,
- curr_tcpquota);
+ get_clientmctx(mgr, &client->mctx);
+ clientmgr_attach(mgr, &client->manager);
+ ns_server_attach(mgr->sctx, &client->sctx);
+ result = isc_task_create(mgr->taskmgr, 20, &client->task);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ result = dns_message_create(client->mctx,
+ DNS_MESSAGE_INTENTPARSE,
+ &client->message);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
- /*
- * If this client was set up using get_client() or get_worker(),
- * then TCP is already marked active. However, if it was restarted
- * from exit_check(), it might not be, so we take care of it now.
- */
- mark_tcp_active(client, true);
- result = isc_socket_accept(client->tcplistener, client->task,
- client_newconn, client);
- if (result != ISC_R_SUCCESS) {
+ client->recvbuf = isc_mem_get(client->mctx,
+ NS_CLIENT_RECV_BUFFER_SIZE);
/*
- * XXXRTH What should we do? We're trying to accept but
- * it didn't work. If we just give up, then TCP
- * service may eventually stop.
- *
- * For now, we just go idle.
+ * Set magic earlier than usual because ns_query_init()
+ * and the functions it calls will require it.
*/
- UNEXPECTED_ERROR(__FILE__, __LINE__,
- "isc_socket_accept() failed: %s",
- isc_result_totext(result));
-
- tcpconn_detach(client);
- mark_tcp_active(client, false);
- return;
+ client->magic = NS_CLIENT_MAGIC;
+ result = ns_query_init(client);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ } else {
+ ns_clientmgr_t *oldmgr = client->manager;
+ ns_server_t *sctx = client->sctx;
+ isc_task_t *task = client->task;
+ unsigned char *recvbuf = client->recvbuf;
+ dns_message_t *message = client->message;
+ isc_mem_t *oldmctx = client->mctx;
+ ns_query_t query = client->query;
+
+ *client = (ns_client_t) {
+ .magic = 0,
+ .mctx = oldmctx,
+ .manager = oldmgr,
+ .sctx = sctx,
+ .task = task,
+ .recvbuf = recvbuf,
+ .message = message,
+ .query = query
+ };
}
- /*
- * The client's 'naccepts' counter indicates that this client has
- * called accept() and is waiting for a new connection. It should
- * never exceed 1.
- */
- INSIST(client->naccepts == 0);
- client->naccepts++;
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ client->udpsize = 512;
+ client->dscp = -1;
+ client->ednsversion = -1;
+ dns_name_init(&client->signername, NULL);
+ dns_ecs_init(&client->ecs);
+ isc_sockaddr_any(&client->formerrcache.addr);
+ client->formerrcache.time = 0;
+ client->formerrcache.id = 0;
+ ISC_LINK_INIT(client, rlink);
+ client->rcode_override = -1; /* not set */
- /*
- * The interface's 'ntcpaccepting' counter is incremented when
- * any client calls accept(), and decremented in client_newconn()
- * once the connection is established.
- *
- * When the client object is shutting down after handling a TCP
- * request (see exit_check()), if this value is at least one, that
- * means another client has called accept() and is waiting to
- * establish the next connection. That means the client may be
- * be free to become inactive; otherwise it may need to start
- * listening for connections itself to prevent the interface
- * going dead.
- */
- isc_refcount_increment0(&client->interface->ntcpaccepting);
-}
+ client->magic = NS_CLIENT_MAGIC;
-static void
-client_udprecv(ns_client_t *client) {
- isc_result_t result;
- isc_region_t r;
+ CTRACE("client_setup");
- CTRACE("udprecv");
+ return (ISC_R_SUCCESS);
- r.base = client->recvbuf;
- r.length = RECV_BUFFER_SIZE;
- result = isc_socket_recv2(client->udpsocket, &r, 1,
- client->task, client->recvevent, 0);
- if (result != ISC_R_SUCCESS) {
- UNEXPECTED_ERROR(__FILE__, __LINE__,
- "isc_socket_recv2() failed: %s",
- isc_result_totext(result));
- /*
- * This cannot happen in the current implementation, since
- * isc_socket_recv2() cannot fail if flags == 0.
- *
- * If this does fail, we just go idle.
- */
- return;
+ cleanup:
+ if (client->recvbuf != NULL) {
+ isc_mem_put(client->mctx, client->recvbuf,
+ NS_CLIENT_RECV_BUFFER_SIZE);
}
- INSIST(client->nrecvs == 0);
- client->nrecvs++;
-}
-
-void
-ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
- uint32_t oldrefs;
- REQUIRE(NS_CLIENT_VALID(source));
- REQUIRE(targetp != NULL && *targetp == NULL);
- oldrefs = isc_refcount_increment(&source->references);
- ns_client_log(source, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
- "ns_client_attach: ref = %d", oldrefs+1);
- *targetp = source;
-}
+ if (client->message != NULL) {
+ dns_message_destroy(&client->message);
+ }
-void
-ns_client_detach(ns_client_t **clientp) {
- int32_t oldrefs;
- ns_client_t *client = *clientp;
- oldrefs = isc_refcount_decrement(&client->references);
- INSIST(oldrefs > 0);
+ if (client->task != NULL) {
+ isc_task_detach(&client->task);
+ }
- *clientp = NULL;
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
- "ns_client_detach: ref = %d", oldrefs-1);
- (void)exit_check(client);
+ return (result);
}
bool
ns_client_shuttingdown(ns_client_t *client) {
- return (client->newstate == NS_CLIENTSTATE_FREED);
+ return (client->shuttingdown);
}
-isc_result_t
-ns_client_replace(ns_client_t *client) {
- isc_result_t result;
- bool tcp;
+/***
+ *** Client Manager
+ ***/
- CTRACE("replace");
+static void
+clientmgr_attach(ns_clientmgr_t *source, ns_clientmgr_t **targetp) {
+ int32_t oldrefs;
- REQUIRE(client != NULL);
- REQUIRE(client->manager != NULL);
+ REQUIRE(VALID_MANAGER(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
- tcp = TCP_CLIENT(client);
- if (tcp && client->tcpconn != NULL && client->tcpconn->pipelined) {
- result = get_worker(client->manager, client->interface,
- client->tcpsocket, client);
- } else {
- result = get_client(client->manager, client->interface,
- client->dispatch, tcp);
+ oldrefs = isc_refcount_increment(&source->references);
+ isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "clientmgr @%p attach: %d", source, oldrefs + 1);
- }
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
+ *targetp = source;
+}
- /*
- * The responsibility for listening for new requests is hereby
- * transferred to the new client. Therefore, the old client
- * should refrain from listening for any more requests.
- */
- client->mortal = true;
+static void
+clientmgr_detach(ns_clientmgr_t **mp) {
+ ns_clientmgr_t *mgr = *mp;
+ int32_t oldrefs;
+ oldrefs = isc_refcount_decrement(&mgr->references);
+ INSIST(oldrefs > 0);
- return (ISC_R_SUCCESS);
-}
+ isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "clientmgr @%p detach: %d", mgr, oldrefs - 1);
+ if (oldrefs == 1) {
+ clientmgr_destroy(mgr);
+ }
-/***
- *** Client Manager
- ***/
+ *mp = NULL;
+}
static void
clientmgr_destroy(ns_clientmgr_t *manager) {
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
int i;
#endif
- REQUIRE(ISC_LIST_EMPTY(manager->clients));
-
MTRACE("clientmgr_destroy");
-#if NMCTXS > 0
- for (i = 0; i < NMCTXS; i++) {
+ manager->magic = 0;
+
+#if CLIENT_NMCTXS > 0
+ for (i = 0; i < CLIENT_NMCTXS; i++) {
if (manager->mctxpool[i] != NULL)
isc_mem_detach(&manager->mctxpool[i]);
}
#endif
- ISC_QUEUE_DESTROY(manager->inactive);
+ if (manager->interface != NULL) {
+ ns_interface_detach(&manager->interface);
+ }
isc_mutex_destroy(&manager->lock);
- isc_mutex_destroy(&manager->listlock);
isc_mutex_destroy(&manager->reclock);
if (manager->excl != NULL)
isc_task_detach(&manager->excl);
+ for (i = 0; i < CLIENT_NTASKS; i++) {
+ if (manager->taskpool[i] != NULL) {
+ isc_task_detach(&manager->taskpool[i]);
+ }
+ }
+ isc_mem_put(manager->mctx, manager->taskpool,
+ CLIENT_NTASKS * sizeof(isc_task_t *));
ns_server_detach(&manager->sctx);
- manager->magic = 0;
isc_mem_put(manager->mctx, manager, sizeof(*manager));
}
isc_result_t
ns_clientmgr_create(isc_mem_t *mctx, ns_server_t *sctx, isc_taskmgr_t *taskmgr,
- isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
+ isc_timermgr_t *timermgr, ns_interface_t *interface,
+ ns_clientmgr_t **managerp)
{
ns_clientmgr_t *manager;
isc_result_t result;
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
int i;
#endif
manager = isc_mem_get(mctx, sizeof(*manager));
+ *manager = (ns_clientmgr_t) { .magic = 0 };
isc_mutex_init(&manager->lock);
- isc_mutex_init(&manager->listlock);
isc_mutex_init(&manager->reclock);
manager->excl = NULL;
manager->mctx = mctx;
manager->taskmgr = taskmgr;
manager->timermgr = timermgr;
- manager->exiting = false;
+ ns_interface_attach(interface, &manager->interface);
+
+ manager->exiting = false;
+ manager->taskpool =
+ isc_mem_get(mctx, CLIENT_NTASKS*sizeof(isc_task_t *));
+ for (i = 0; i < CLIENT_NTASKS; i++) {
+ manager->taskpool[i] = NULL;
+ isc_task_create(manager->taskmgr, 20, &manager->taskpool[i]);
+ }
+ isc_refcount_init(&manager->references, 1);
manager->sctx = NULL;
ns_server_attach(sctx, &manager->sctx);
- ISC_LIST_INIT(manager->clients);
ISC_LIST_INIT(manager->recursing);
- ISC_QUEUE_INIT(manager->inactive, ilink);
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
manager->nextmctx = 0;
- for (i = 0; i < NMCTXS; i++)
+ for (i = 0; i < CLIENT_NMCTXS; i++)
manager->mctxpool[i] = NULL; /* will be created on-demand */
#endif
manager->magic = MANAGER_MAGIC;
cleanup_reclock:
isc_mutex_destroy(&manager->reclock);
- isc_mutex_destroy(&manager->listlock);
isc_mutex_destroy(&manager->lock);
isc_mem_put(manager->mctx, manager, sizeof(*manager));
ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
isc_result_t result;
ns_clientmgr_t *manager;
- ns_client_t *client;
- bool need_destroy = false, unlock = false;
+ bool unlock = false;
+ int32_t oldrefs;
REQUIRE(managerp != NULL);
manager = *managerp;
MTRACE("destroy");
+ /* XXXWPK TODO we need to pause netmgr here */
/*
* Check for success because we may already be task-exclusive
* at this point. Only if we succeed at obtaining an exclusive
* lock now will we need to relinquish it later.
*/
result = isc_task_beginexclusive(manager->excl);
- if (result == ISC_R_SUCCESS)
+ if (result == ISC_R_SUCCESS) {
unlock = true;
+ }
manager->exiting = true;
- for (client = ISC_LIST_HEAD(manager->clients);
- client != NULL;
- client = ISC_LIST_NEXT(client, link))
- isc_task_shutdown(client->task);
-
- if (ISC_LIST_EMPTY(manager->clients))
- need_destroy = true;
-
- if (unlock)
+ if (unlock) {
isc_task_endexclusive(manager->excl);
-
- if (need_destroy)
- clientmgr_destroy(manager);
-
- *managerp = NULL;
-}
-
-static isc_result_t
-get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
- dns_dispatch_t *disp, bool tcp)
-{
- isc_result_t result = ISC_R_SUCCESS;
- isc_event_t *ev;
- ns_client_t *client;
- MTRACE("get client");
-
- REQUIRE(manager != NULL);
-
- if (manager->exiting)
- return (ISC_R_SHUTTINGDOWN);
-
- /*
- * Allocate a client. First try to get a recycled one;
- * if that fails, make a new one.
- */
- client = NULL;
- if ((manager->sctx->options & NS_SERVER_CLIENTTEST) == 0) {
- ISC_QUEUE_POP(manager->inactive, ilink, client);
- }
-
- if (client != NULL) {
- MTRACE("recycle");
- } else {
- MTRACE("create new");
-
- LOCK(&manager->lock);
- result = client_create(manager, &client);
- UNLOCK(&manager->lock);
- if (result != ISC_R_SUCCESS)
- return (result);
-
- LOCK(&manager->listlock);
- ISC_LIST_APPEND(manager->clients, client, link);
- UNLOCK(&manager->listlock);
- }
-
- client->manager = manager;
- ns_interface_attach(ifp, &client->interface);
- client->state = NS_CLIENTSTATE_READY;
- client->sctx = manager->sctx;
- INSIST(client->recursionquota == NULL);
-
- client->dscp = ifp->dscp;
- client->rcode_override = -1; /* not set */
-
- if (tcp) {
- mark_tcp_active(client, true);
-
- client->attributes |= NS_CLIENTATTR_TCP;
- isc_socket_attach(ifp->tcpsocket,
- &client->tcplistener);
-
- } else {
- isc_socket_t *sock;
-
- dns_dispatch_attach(disp, &client->dispatch);
- sock = dns_dispatch_getsocket(client->dispatch);
- isc_socket_attach(sock, &client->udpsocket);
- }
-
- INSIST(client->nctls == 0);
- client->nctls++;
- ev = &client->ctlevent;
- isc_task_send(client->task, &ev);
-
- return (result);
-}
-
-static isc_result_t
-get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock,
- ns_client_t *oldclient)
-{
- isc_result_t result = ISC_R_SUCCESS;
- isc_event_t *ev;
- ns_client_t *client;
- MTRACE("get worker");
-
- REQUIRE(manager != NULL);
- REQUIRE(oldclient != NULL);
-
- if (manager->exiting)
- return (ISC_R_SHUTTINGDOWN);
-
- /*
- * Allocate a client. First try to get a recycled one;
- * if that fails, make a new one.
- */
- client = NULL;
- if ((manager->sctx->options & NS_SERVER_CLIENTTEST) == 0)
- ISC_QUEUE_POP(manager->inactive, ilink, client);
-
- if (client != NULL)
- MTRACE("recycle");
- else {
- MTRACE("create new");
-
- LOCK(&manager->lock);
- result = client_create(manager, &client);
- UNLOCK(&manager->lock);
- if (result != ISC_R_SUCCESS)
- return (result);
-
- LOCK(&manager->listlock);
- ISC_LIST_APPEND(manager->clients, client, link);
- UNLOCK(&manager->listlock);
- }
-
- client->manager = manager;
- ns_interface_attach(ifp, &client->interface);
- client->newstate = client->state = NS_CLIENTSTATE_WORKING;
- INSIST(client->recursionquota == NULL);
- client->sctx = manager->sctx;
-
- client->dscp = ifp->dscp;
-
- client->attributes |= NS_CLIENTATTR_TCP;
- client->mortal = true;
- client->sendcb = NULL;
- client->rcode_override = -1; /* not set */
-
- tcpconn_attach(oldclient, client);
- mark_tcp_active(client, true);
-
- isc_socket_attach(ifp->tcpsocket, &client->tcplistener);
- isc_socket_attach(sock, &client->tcpsocket);
- isc_socket_setname(client->tcpsocket, "worker-tcp", NULL);
- (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr);
- client->peeraddr_valid = true;
-
- INSIST(client->tcpmsg_valid == false);
- dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg);
- client->tcpmsg_valid = true;
-
- INSIST(client->nctls == 0);
- client->nctls++;
- ev = &client->ctlevent;
- isc_task_send(client->task, &ev);
-
- return (result);
-}
-
-isc_result_t
-ns__clientmgr_getclient(ns_clientmgr_t *manager, ns_interface_t *ifp,
- bool tcp, ns_client_t **clientp)
-{
- isc_result_t result = ISC_R_SUCCESS;
- ns_client_t *client;
- MTRACE("getclient");
-
- REQUIRE(VALID_MANAGER(manager));
- REQUIRE(clientp != NULL && *clientp == NULL);
-
- if (manager->exiting)
- return (ISC_R_SHUTTINGDOWN);
-
- client = NULL;
- ISC_QUEUE_POP(manager->inactive, ilink, client);
- if (client != NULL)
- MTRACE("getclient (recycle)");
- else {
- MTRACE("getclient (create)");
-
- LOCK(&manager->lock);
- result = client_create(manager, &client);
- UNLOCK(&manager->lock);
- if (result != ISC_R_SUCCESS)
- return (result);
-
- LOCK(&manager->listlock);
- ISC_LIST_APPEND(manager->clients, client, link);
- UNLOCK(&manager->listlock);
- }
-
- client->manager = manager;
- ns_interface_attach(ifp, &client->interface);
- client->state = NS_CLIENTSTATE_READY;
- INSIST(client->recursionquota == NULL);
-
- client->dscp = ifp->dscp;
- isc_refcount_increment(&client->references);
-
- if (tcp) {
- client->attributes |= NS_CLIENTATTR_TCP;
}
- *clientp = client;
-
- return (result);
-}
-
-isc_result_t
-ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
- ns_interface_t *ifp, bool tcp)
-{
- isc_result_t result = ISC_R_SUCCESS;
- unsigned int disp;
-
- REQUIRE(VALID_MANAGER(manager));
- REQUIRE(n > 0);
-
- MTRACE("createclients");
-
- for (disp = 0; disp < n; disp++) {
- result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
- if (result != ISC_R_SUCCESS)
- break;
+ oldrefs = isc_refcount_decrement(&manager->references);
+ if (oldrefs == 1) {
+ clientmgr_destroy(manager);
}
- return (result);
+ *managerp = NULL;
}
isc_sockaddr_t *
dns_acl_t *acl, bool default_allow)
{
isc_result_t result;
- dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(client->manager->interface->mgr);
isc_netaddr_t tmpnetaddr;
int match;
inc_stats(client, counter);
ns_client_send(client);
+ isc_nmhandle_unref(client->handle);
}
static void
log_queryerror(client, result, line, loglevel);
ns_client_error(client, result);
+ isc_nmhandle_unref(client->handle);
}
static void
query_next(ns_client_t *client, isc_result_t result) {
- if (result == DNS_R_DUPLICATE)
+ if (result == DNS_R_DUPLICATE) {
inc_stats(client, ns_statscounter_duplicate);
- else if (result == DNS_R_DROP)
+ } else if (result == DNS_R_DROP) {
inc_stats(client, ns_statscounter_dropped);
- else
+ } else {
inc_stats(client, ns_statscounter_failure);
- ns_client_next(client, result);
+ }
+ ns_client_drop(client, result);
+ isc_nmhandle_unref(client->handle);
}
static inline void
*/
for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
dbversion != NULL;
- dbversion = dbversion_next) {
+ dbversion = dbversion_next)
+ {
dbversion_next = ISC_LIST_NEXT(dbversion, link);
dns_db_closeversion(dbversion->db, &dbversion->version,
false);
}
static void
-query_next_callback(ns_client_t *client) {
+query_cleanup(ns_client_t *client) {
query_reset(client, false);
}
REQUIRE((void*)(*eventp) == (void *)(*deventp));
+ CTRACE(ISC_LOG_DEBUG(3), "free_devent");
+
if (devent->fetch != NULL) {
dns_resolver_destroyfetch(&devent->fetch);
}
if (devent->sigrdataset != NULL) {
ns_client_putrdataset(client, &devent->sigrdataset);
}
+
/*
* If the two pointers are the same then leave the setting of
* (*deventp) to NULL to isc_event_free.
*/
- if ((void *)eventp != (void *)deventp)
+ if ((void *)eventp != (void *)deventp) {
(*deventp) = NULL;
+ }
isc_event_free(eventp);
}
REQUIRE(NS_CLIENT_VALID(client));
REQUIRE(task == client->task);
+ CTRACE(ISC_LOG_DEBUG(3), "prefetch_done");
+
LOCK(&client->query.fetchlock);
if (client->query.prefetch != NULL) {
INSIST(devent->fetch == client->query.prefetch);
client->query.prefetch = NULL;
}
UNLOCK(&client->query.fetchlock);
+
free_devent(client, &event, &devent);
- ns_client_detach(&client);
+ isc_nmhandle_unref(client->handle);
}
static void
isc_result_t result;
isc_sockaddr_t *peeraddr;
dns_rdataset_t *tmprdataset;
- ns_client_t *dummy = NULL;
unsigned int options;
+ CTRACE(ISC_LOG_DEBUG(3), "query_prefetch");
+
if (client->query.prefetch != NULL ||
client->view->prefetch_trigger == 0U ||
rdataset->ttl > client->view->prefetch_trigger ||
(rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
+ {
return;
+ }
if (client->recursionquota == NULL) {
result = isc_quota_attach(&client->sctx->recursionquota,
&client->recursionquota);
- if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
- ns_stats_increment(client->sctx->nsstats,
- ns_statscounter_recursclients);
- }
- if (result == ISC_R_SUCCESS && !client->mortal &&
- !TCP(client))
- {
- result = ns_client_replace(client);
- }
if (result != ISC_R_SUCCESS) {
return;
}
}
tmprdataset = ns_client_newrdataset(client);
- if (tmprdataset == NULL)
+ if (tmprdataset == NULL) {
return;
- if (!TCP(client))
+ }
+
+ if (!TCP(client)) {
peeraddr = &client->peeraddr;
- else
+ } else {
peeraddr = NULL;
- ns_client_attach(client, &dummy);
+ }
+
+ isc_nmhandle_ref(client->handle);
options = client->query.fetchoptions | DNS_FETCHOPT_PREFETCH;
result = dns_resolver_createfetch(client->view->resolver,
qname, rdataset->type, NULL, NULL,
&client->query.prefetch);
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
- ns_client_detach(&dummy);
+ isc_nmhandle_unref(client->handle);
}
+
dns_rdataset_clearprefetch(rdataset);
ns_stats_increment(client->sctx->nsstats,
ns_statscounter_prefetch);
isc_result_t result;
isc_sockaddr_t *peeraddr;
dns_rdataset_t *tmprdataset;
- ns_client_t *dummy = NULL;
unsigned int options;
+ CTRACE(ISC_LOG_DEBUG(3), "query_rpzfetch");
+
if (client->query.prefetch != NULL)
return;
if (client->recursionquota == NULL) {
result = isc_quota_attach(&client->sctx->recursionquota,
&client->recursionquota);
- if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
- ns_stats_increment(client->sctx->nsstats,
- ns_statscounter_recursclients);
- }
- if (result == ISC_R_SUCCESS && !client->mortal &&
- !TCP(client))
- {
- result = ns_client_replace(client);
- }
if (result != ISC_R_SUCCESS) {
return;
}
}
tmprdataset = ns_client_newrdataset(client);
- if (tmprdataset == NULL)
+ if (tmprdataset == NULL) {
return;
- if (!TCP(client))
+ }
+
+ if (!TCP(client)) {
peeraddr = &client->peeraddr;
- else
+ } else {
peeraddr = NULL;
- ns_client_attach(client, &dummy);
+ }
+
options = client->query.fetchoptions;
+ isc_nmhandle_ref(client->handle);
result = dns_resolver_createfetch(client->view->resolver, qname, type,
NULL, NULL, NULL, peeraddr,
client->message->id, options, 0,
&client->query.prefetch);
if (result != ISC_R_SUCCESS) {
ns_client_putrdataset(client, &tmprdataset);
- ns_client_detach(&dummy);
+ isc_nmhandle_unref(client->handle);
}
}
dns_rdataset_t *sigrdataset)
{
isc_netaddr_t netaddr;
- dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(client->manager->interface->mgr);
dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
unsigned int flags = 0;
unsigned int i, count;
/* Set this first so CCTRACE will work */
qctx->client = client;
+
dns_view_attach(client->view, &qctx->view);
CCTRACE(ISC_LOG_DEBUG(3), "qctx_init");
CALL_HOOK_NORETURN(NS_QUERY_QCTX_DESTROYED, qctx);
dns_view_detach(&qctx->view);
- if (qctx->detach_client) {
- ns_client_detach(&qctx->client);
- }
}
/*%
REQUIRE(task == client->task);
REQUIRE(RECURSING(client));
+ CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
+
LOCK(&client->query.fetchlock);
if (client->query.fetch != NULL) {
/*
UNLOCK(&client->query.fetchlock);
INSIST(client->query.fetch == NULL);
- client->query.attributes &= ~NS_QUERYATTR_RECURSING;
SAVE(fetch, devent->fetch);
+ /*
+ * We're done recursing, detach from quota and unlink from
+ * the manager's recursing-clients list.
+ */
+
+ if (client->recursionquota != NULL) {
+ isc_quota_detach(&client->recursionquota);
+ ns_stats_decrement(client->sctx->nsstats,
+ ns_statscounter_recursclients);
+ }
+
+ LOCK(&client->manager->reclock);
+ if (ISC_LINK_LINKED(client, rlink)) {
+ ISC_LIST_UNLINK(client->manager->recursing, client, rlink);
+ }
+ UNLOCK(&client->manager->reclock);
+
+ client->query.attributes &= ~NS_QUERYATTR_RECURSING;
+ client->state = NS_CLIENTSTATE_WORKING;
+
/*
* If this client is shutting down, or this transaction
* has timed out, do not resume the find.
} else {
query_next(client, ISC_R_CANCELED);
}
- /*
- * This may destroy the client.
- */
- ns_client_detach(&client);
} else {
query_ctx_t qctx;
}
dns_resolver_destroyfetch(&fetch);
+ isc_nmhandle_unref(client->handle);
}
/*%
recparam_update(&client->query.recparam, qtype, qname, qdomain);
- if (!resuming)
+ if (!resuming) {
inc_stats(client, ns_statscounter_recursion);
+ }
/*
* We are about to recurse, which means that this client will
}
ns_client_killoldestquery(client);
}
- if (result == ISC_R_SUCCESS && !client->mortal &&
- !TCP(client)) {
- result = ns_client_replace(client);
- if (result != ISC_R_SUCCESS) {
- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_QUERY,
- ISC_LOG_WARNING,
- "ns_client_replace() failed: %s",
- isc_result_totext(result));
- isc_quota_detach(&client->recursionquota);
- ns_stats_decrement(client->sctx->nsstats,
- ns_statscounter_recursclients);
- }
- }
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
return (result);
+ }
+
ns_client_recursing(client);
}
peeraddr = &client->peeraddr;
}
+ isc_nmhandle_ref(client->handle);
result = dns_resolver_createfetch(client->view->resolver,
qname, qtype, qdomain, nameservers,
NULL, peeraddr, client->message->id,
client, rdataset, sigrdataset,
&client->query.fetch);
if (result != ISC_R_SUCCESS) {
+ isc_nmhandle_unref(client->handle);
ns_client_putrdataset(client, &rdataset);
if (sigrdataset != NULL) {
ns_client_putrdataset(client, &sigrdataset);
char tbuf[DNS_RDATATYPE_FORMATSIZE];
#endif
+ CCTRACE(ISC_LOG_DEBUG(3), "query_resume");
+
CALL_HOOK(NS_QUERY_RESUME_BEGIN, qctx);
qctx->want_restart = false;
query_checkrpz(query_ctx_t *qctx, isc_result_t result) {
isc_result_t rresult;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_checkrpz");
+
rresult = rpz_rewrite(qctx->client, qctx->qtype,
result, qctx->resuming,
qctx->rdataset, qctx->sigrdataset);
dns_rdatatype_t onetype = 0; /* type to use for minimal-any */
isc_buffer_t b;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_respond_any");
+
CALL_HOOK(NS_QUERY_RESPOND_ANY_BEGIN, qctx);
result = dns_db_allrdatasets(qctx->db, qctx->node,
query_getexpire(query_ctx_t *qctx) {
dns_zone_t *raw = NULL, *mayberaw;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_getexpire");
+
if (qctx->zone == NULL || !qctx->is_zone ||
qctx->qtype != dns_rdatatype_soa ||
qctx->client->query.restarts != 0 ||
dns_rdataset_t **sigrdatasetp = NULL;
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_addanswer");
+
CALL_HOOK(NS_QUERY_ADDANSWER_BEGIN, qctx);
if (qctx->dns64) {
query_respond(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_respond");
+
/*
* Check to see if the AAAA RRset has non-excluded addresses
* in it. If not look for a A RRset.
static isc_result_t
query_dns64(query_ctx_t *qctx) {
ns_client_t *client = qctx->client;
- dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(client->manager->interface->mgr);
dns_name_t *name, *mname;
dns_rdata_t *dns64_rdata;
dns_rdata_t rdata = DNS_RDATA_INIT;
query_notfound(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_notfound");
+
CALL_HOOK(NS_QUERY_NOTFOUND_BEGIN, qctx);
INSIST(!qctx->is_zone);
query_delegation(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_delegation");
+
CALL_HOOK(NS_QUERY_DELEGATION_BEGIN, qctx);
qctx->authoritative = false;
isc_result_t result;
dns_name_t *qname = qctx->client->query.qname;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_delegation_recurse");
+
if (!RECURSIONOK(qctx->client)) {
return (ISC_R_COMPLETE);
}
query_nodata(query_ctx_t *qctx, isc_result_t res) {
isc_result_t result = res;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_nodata");
+
CALL_HOOK(NS_QUERY_NODATA_BEGIN, qctx);
#ifdef dns64_bis_return_excluded_addresses
isc_result_t
query_sign_nodata(query_ctx_t *qctx) {
isc_result_t result;
+
+ CCTRACE(ISC_LOG_DEBUG(3), "query_sign_nodata");
+
/*
* Look for a NSEC3 record if we don't have a NSEC record.
*/
uint32_t ttl;
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_nxdomain");
+
CALL_HOOK(NS_QUERY_NXDOMAIN_BEGIN, qctx);
INSIST(qctx->is_zone || REDIRECT(qctx->client));
query_redirect(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_redirect");
+
result = redirect(qctx->client, qctx->fname, qctx->rdataset,
&qctx->node, &qctx->db, &qctx->version,
qctx->type);
dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
dns_rdataset_t **sigrdatasetp;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_synthwildcard");
+
/*
* We want the answer to be first, so save the
* NOQNAME proof's name now or else discard it.
isc_result_t result;
dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_synthnxdomain");
+
/*
* Detemine the correct TTL to use for the SOA and RRSIG
*/
isc_result_t result = ISC_R_SUCCESS;
unsigned int dboptions = qctx->client->query.dboptions;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_coveringnsec");
+
dns_rdataset_init(&rdataset);
dns_rdataset_init(&sigrdataset);
result == DNS_R_NCACHENXRRSET ||
result == DNS_R_NXDOMAIN);
+ CCTRACE(ISC_LOG_DEBUG(3), "query_ncache");
+
CALL_HOOK(NS_QUERY_NCACHE_BEGIN, qctx);
qctx->authoritative = false;
query_zerottl_refetch(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_zerottl_refetch");
+
if (qctx->is_zone || qctx->resuming || STALE(qctx->rdataset) ||
qctx->rdataset->ttl != 0 || !RECURSIONOK(qctx->client))
{
NULL, NULL, qctx->resuming);
if (result == ISC_R_SUCCESS) {
CALL_HOOK(NS_QUERY_ZEROTTL_RECURSE, qctx);
- qctx->client->query.attributes |=
- NS_QUERYATTR_RECURSING;
+ qctx->client->query.attributes |= NS_QUERYATTR_RECURSING;
if (qctx->dns64) {
qctx->client->query.attributes |=
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_cname_t cname;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_cname");
+
CALL_HOOK(NS_QUERY_CNAME_BEGIN, qctx);
result = query_zerottl_refetch(qctx);
isc_result_t result;
unsigned int nlabels;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_dname");
+
CALL_HOOK(NS_QUERY_DNAME_BEGIN, qctx);
/*
query_prepresponse(query_ctx_t *qctx) {
isc_result_t result;
+ CCTRACE(ISC_LOG_DEBUG(3), "query_prepresponse");
+
CALL_HOOK(NS_QUERY_PREP_RESPONSE_BEGIN, qctx);
if (WANTDNSSEC(qctx->client) &&
query_setup_sortlist(query_ctx_t *qctx) {
isc_netaddr_t netaddr;
ns_client_t *client = qctx->client;
- dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+ dns_aclenv_t *env =
+ ns_interfacemgr_getaclenv(client->manager->interface->mgr);
const void *order_arg = NULL;
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
isc_result_t result;
dns_message_t *message;
dns_rdataset_t *rdataset;
- ns_client_t *qclient;
dns_rdatatype_t qtype;
unsigned int saved_extflags;
unsigned int saved_flags;
CTRACE(ISC_LOG_DEBUG(3), "ns_query_start");
- /*
- * Test only.
- */
- if (((client->sctx->options & NS_SERVER_CLIENTTEST) != 0) &&
- !TCP(client))
- {
- result = ns_client_replace(client);
- if (result == ISC_R_SHUTTINGDOWN) {
- ns_client_next(client, result);
- return;
- } else if (result != ISC_R_SUCCESS) {
- query_error(client, result, __LINE__);
- return;
- }
- }
-
/*
* Ensure that appropriate cleanups occur.
*/
- client->next = query_next_callback;
+ client->cleanup = query_cleanup;
if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
* section.
*/
query_error(client, DNS_R_FORMERR, __LINE__);
- } else
+ } else {
query_error(client, result, __LINE__);
+ }
return;
}
result = dns_tkey_processquery(client->message,
client->sctx->tkeyctx,
client->view->dynamickeys);
- if (result == ISC_R_SUCCESS)
+ if (result == ISC_R_SUCCESS) {
query_send(client);
- else
+ } else {
query_error(client, result, __LINE__);
+ }
return;
default: /* TSIG, etc. */
query_error(client, DNS_R_FORMERR, __LINE__);
{
client->query.dboptions |= DNS_DBFIND_PENDINGOK;
client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
- } else if (!client->view->enablevalidation)
+ } else if (!client->view->enablevalidation) {
client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+ }
if (client->view->qminimization) {
client->query.fetchoptions |= DNS_FETCHOPT_QMINIMIZE |
if (WANTDNSSEC(client) || WANTAD(client))
message->flags |= DNS_MESSAGEFLAG_AD;
- qclient = NULL;
- ns_client_attach(client, &qclient);
- (void)query_setup(qclient, qtype);
+ (void)query_setup(client, qtype);
}