📄 client.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * 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 ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC 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.4.22 2004/07/23 02:56:51 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/rcode.h>#include <dns/resolver.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 recursing; /* Recursing 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_recursing(ns_client_t *client, isc_boolean_t killoldest) { ns_client_t *oldest; REQUIRE(NS_CLIENT_VALID(client)); LOCK(&client->manager->lock); if (killoldest) { oldest = ISC_LIST_HEAD(client->manager->recursing); if (oldest != NULL) { ns_query_cancel(oldest); ISC_LIST_UNLINK(*oldest->list, oldest, link); ISC_LIST_APPEND(client->manager->active, oldest, link); oldest->list = &client->manager->active; } } ISC_LIST_UNLINK(*client->list, client, link); ISC_LIST_APPEND(client->manager->recursing, client, link); client->list = &client->manager->recursing; UNLOCK(&client->manager->lock);}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 * * 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) { INSIST(client->newstate <= NS_CLIENTSTATE_READING); /* * Let the update processing complete. */ if (client->nupdates > 0) return (ISC_TRUE); /* * 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) && ISC_LIST_EMPTY(manager->recursing)) 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -