📄 client.c
字号:
/* * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *//* $Id: client.c,v 1.176.2.13 2003/07/18 06:14:30 marka Exp $ */#include <config.h>#include <isc/formatcheck.h>#include <isc/mutex.h>#include <isc/once.h>#include <isc/print.h>#include <isc/stdio.h>#include <isc/string.h>#include <isc/task.h>#include <isc/timer.h>#include <isc/util.h>#include <dns/db.h>#include <dns/dispatch.h>#include <dns/events.h>#include <dns/message.h>#include <dns/rdata.h>#include <dns/rdataclass.h>#include <dns/rdatalist.h>#include <dns/rdataset.h>#include <dns/tsig.h>#include <dns/view.h>#include <dns/zone.h>#include <named/interfacemgr.h>#include <named/log.h>#include <named/notify.h>#include <named/server.h>#include <named/update.h>/*** *** Client ***//* * Important note! * * All client state changes, other than that from idle to listening, occur * as a result of events. This guarantees serialization and avoids the * need for locking. * * If a routine is ever created that allows someone other than the client's * task to change the client, then the client will have to be locked. */#define NS_CLIENT_TRACE#ifdef NS_CLIENT_TRACE#define CTRACE(m) ns_client_log(client, \ NS_LOGCATEGORY_CLIENT, \ NS_LOGMODULE_CLIENT, \ ISC_LOG_DEBUG(3), \ "%s", (m))#define MTRACE(m) isc_log_write(ns_g_lctx, \ NS_LOGCATEGORY_GENERAL, \ NS_LOGMODULE_CLIENT, \ ISC_LOG_DEBUG(3), \ "clientmgr @%p: %s", manager, (m))#else#define CTRACE(m) ((void)(m))#define MTRACE(m) ((void)(m))#endif#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 4096struct ns_clientmgr { /* Unlocked. */ unsigned int magic; isc_mem_t * mctx; isc_taskmgr_t * taskmgr; isc_timermgr_t * timermgr; isc_mutex_t lock; /* Locked by lock. */ isc_boolean_t exiting; client_list_t active; /* Active clients */ client_list_t inactive; /* To be recycled */};#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_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. */static void client_read(ns_client_t *client);static void client_accept(ns_client_t *client);static void client_udprecv(ns_client_t *client);static void clientmgr_destroy(ns_clientmgr_t *manager);static isc_boolean_t exit_check(ns_client_t *client);static void ns_client_endrequest(ns_client_t *client);static void ns_client_checkactive(ns_client_t *client);static void client_start(isc_task_t *task, isc_event_t *event);static void client_request(isc_task_t *task, isc_event_t *event);static void ns_client_dumpmessage(ns_client_t *client, const char *reason);voidns_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, ISC_FALSE); client->timerset = ISC_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. */ }}/* * Check for a deactivation or shutdown request and take appropriate * action. Returns ISC_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 isc_boolean_texit_check(ns_client_t *client) { ns_clientmgr_t *locked_manager = NULL; ns_clientmgr_t *destroy_manager = NULL; REQUIRE(NS_CLIENT_VALID(client)); if (client->state <= client->newstate) return (ISC_FALSE); /* Business as usual. */ INSIST(client->newstate < NS_CLIENTSTATE_WORKING); /* * 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 * */ if (client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL) dns_view_detach(&client->view); if (client->state == NS_CLIENTSTATE_WORKING) { INSIST(client->newstate <= NS_CLIENTSTATE_READING); /* * We are trying to abort request processing. */ if (client->nsends > 0) { isc_socket_t *socket; if (TCP_CLIENT(client)) socket = client->tcpsocket; else socket = client->udpsocket; isc_socket_cancel(socket, client->task, ISC_SOCKCANCEL_SEND); } if (! (client->nsends == 0 && client->nrecvs == 0 && client->references == 0)) { /* * Still waiting for I/O cancel completion. * or lingering references. */ return (ISC_TRUE); } /* * I/O cancel is complete. Burn down all state * related to the current request. */ ns_client_endrequest(client); client->state = NS_CLIENTSTATE_READING; INSIST(client->recursionquota == NULL); if (NS_CLIENTSTATE_READING == client->newstate) { client_read(client); client->newstate = NS_CLIENTSTATE_MAX; return (ISC_TRUE); /* We're done. */ } } 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); if (! client->nreads == 0) { /* Still waiting for read cancel completion. */ return (ISC_TRUE); } if (client->tcpmsg_valid) { dns_tcpmsg_invalidate(&client->tcpmsg); client->tcpmsg_valid = ISC_FALSE; } if (client->tcpsocket != NULL) { CTRACE("closetcp"); isc_socket_detach(&client->tcpsocket); } if (client->tcpquota != NULL) isc_quota_detach(&client->tcpquota); if (client->timerset) { (void) isc_timer_reset(client->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); client->timerset = ISC_FALSE; } client->peeraddr_valid = ISC_FALSE; client->state = NS_CLIENTSTATE_READY; INSIST(client->recursionquota == NULL); /* * Now the client is 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 force it to go inactive if not. */ ns_client_checkactive(client); if (NS_CLIENTSTATE_READY == client->newstate) { if (TCP_CLIENT(client)) { client_accept(client); } else client_udprecv(client); client->newstate = NS_CLIENTSTATE_MAX; return (ISC_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); if (! (client->naccepts == 0)) { /* Still waiting for accept cancel completion. */ return (ISC_TRUE); } /* Accept cancel is complete. */ if (client->nrecvs > 0) isc_socket_cancel(client->udpsocket, client->task, ISC_SOCKCANCEL_RECV); if (! (client->nrecvs == 0)) { /* Still waiting for recv cancel completion. */ return (ISC_TRUE); } /* Recv cancel is complete. */ if (client->nctls > 0) { /* Still waiting for control event to be delivered */ return (ISC_TRUE); } /* Deactivate the client. */ if (client->interface) ns_interface_detach(&client->interface); INSIST(client->naccepts == 0); INSIST(client->recursionquota == NULL); if (client->tcplistener != NULL) isc_socket_detach(&client->tcplistener); if (client->udpsocket != NULL) isc_socket_detach(&client->udpsocket); if (client->dispatch != NULL) dns_dispatch_detach(&client->dispatch); client->attributes = 0; client->mortal = ISC_FALSE; LOCK(&client->manager->lock); /* * 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. */ locked_manager = client->manager; ISC_LIST_UNLINK(*client->list, client, link); ISC_LIST_APPEND(client->manager->inactive, client, link); client->list = &client->manager->inactive; client->state = NS_CLIENTSTATE_INACTIVE; INSIST(client->recursionquota == NULL); if (client->state == client->newstate) { client->newstate = NS_CLIENTSTATE_MAX; goto unlock; } } 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); 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->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); } dns_message_destroy(&client->message); if (client->manager != NULL) { ns_clientmgr_t *manager = client->manager; if (locked_manager == NULL) { LOCK(&manager->lock); locked_manager = manager; } ISC_LIST_UNLINK(*client->list, client, link); client->list = NULL; if (manager->exiting && ISC_LIST_EMPTY(manager->active) && ISC_LIST_EMPTY(manager->inactive)) destroy_manager = manager; } /* * 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; isc_mem_put(client->mctx, client, sizeof(*client)); goto unlock; } unlock: if (locked_manager != NULL) { UNLOCK(&locked_manager->lock); locked_manager = NULL; } /* * Only now is it safe to destroy the client manager (if needed), * because we have accessed its lock for the last time. */ if (destroy_manager != NULL) clientmgr_destroy(destroy_manager); return (ISC_TRUE);}/* * The client's task has received the client's control event * as part of the startup process. */static voidclient_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)) { client_accept(client); } else { client_udprecv(client); }}/* * The client's task has received a shutdown event. */static voidclient_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; } client->newstate = NS_CLIENTSTATE_FREED; (void)exit_check(client);}static voidns_client_endrequest(ns_client_t *client) { INSIST(client->naccepts == 0); INSIST(client->nreads == 0); INSIST(client->nsends == 0); INSIST(client->nrecvs == 0); INSIST(client->state == NS_CLIENTSTATE_WORKING); CTRACE("endrequest"); if (client->next != NULL) { (client->next)(client); client->next = NULL; } if (client->view != NULL) dns_view_detach(&client->view); if (client->opt != NULL) { INSIST(dns_rdataset_isassociated(client->opt)); dns_rdataset_disassociate(client->opt); dns_message_puttemprdataset(client->message, &client->opt); } client->udpsize = 512; client->extflags = 0; dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE); if (client->recursionquota != NULL) isc_quota_detach(&client->recursionquota); /* * Clear all client attributes that are specific to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -