client.c
来自「非常好的dns解析软件」· C语言 代码 · 共 2,439 行 · 第 1/5 页
C
2,439 行
/* * Copyright (C) 2004-2006 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.219.18.20 2006/07/22 01:02:36 marka Exp $ */#include <config.h>#include <isc/formatcheck.h>#include <isc/mutex.h>#include <isc/once.h>#include <isc/platform.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/peer.h>#include <dns/rcode.h>#include <dns/rdata.h>#include <dns/rdataclass.h>#include <dns/rdatalist.h>#include <dns/rdataset.h>#include <dns/resolver.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 ***//*! \file * Client Routines * * 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 4096#ifdef ISC_PLATFORM_USETHREADS#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. */#else#define NMCTXS 0/*%< * If named with built without thread, simply share manager's context. Using * a separate context in this case would simply waste memory. */#endif/*% nameserver client manager structure */struct 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 */#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_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. */#ifndef NS_CLIENT_DROPPORT#define NS_CLIENT_DROPPORT 1#endifunsigned int ns_client_requests;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) { REQUIRE(NS_CLIENT_VALID(client)); LOCK(&client->manager->lock); 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_killoldestquery(ns_client_t *client) { ns_client_t *oldest; REQUIRE(NS_CLIENT_VALID(client)); LOCK(&client->manager->lock); 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; } 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. Ensure that * the client is on the active list and not the * recursing list. */ LOCK(&client->manager->lock); if (client->list == &client->manager->recursing) { ISC_LIST_UNLINK(*client->list, client, link); ISC_LIST_APPEND(client->manager->active, client, link); client->list = &client->manager->active; } UNLOCK(&client->manager->lock); 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?