📄 dispatch.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: dispatch.c,v 1.101.2.6.2.10 2004/09/01 04:27:41 marka Exp $ */#include <config.h>#include <stdlib.h>#include <isc/entropy.h>#include <isc/lfsr.h>#include <isc/mem.h>#include <isc/mutex.h>#include <isc/print.h>#include <isc/string.h>#include <isc/task.h>#include <isc/util.h>#include <dns/acl.h>#include <dns/dispatch.h>#include <dns/events.h>#include <dns/log.h>#include <dns/message.h>#include <dns/portlist.h>#include <dns/tcpmsg.h>#include <dns/types.h>typedef ISC_LIST(dns_dispentry_t) dns_displist_t;typedef struct dns_qid { unsigned int magic; unsigned int qid_nbuckets; /* hash table size */ unsigned int qid_increment; /* id increment on collision */ isc_mutex_t lock; isc_lfsr_t qid_lfsr1; /* state generator info */ isc_lfsr_t qid_lfsr2; /* state generator info */ dns_displist_t *qid_table; /* the table itself */} dns_qid_t;struct dns_dispatchmgr { /* Unlocked. */ unsigned int magic; isc_mem_t *mctx; dns_acl_t *blackhole; dns_portlist_t *portlist; /* Locked by "lock". */ isc_mutex_t lock; unsigned int state; ISC_LIST(dns_dispatch_t) list; /* locked by buffer lock */ dns_qid_t *qid; isc_mutex_t buffer_lock; unsigned int buffers; /* allocated buffers */ unsigned int buffersize; /* size of each buffer */ unsigned int maxbuffers; /* max buffers */ /* Locked internally. */ isc_mutex_t pool_lock; isc_mempool_t *epool; /* memory pool for events */ isc_mempool_t *rpool; /* memory pool for replies */ isc_mempool_t *dpool; /* dispatch allocations */ isc_mempool_t *bpool; /* memory pool for buffers */ isc_entropy_t *entropy; /* entropy source */};#define MGR_SHUTTINGDOWN 0x00000001U#define MGR_IS_SHUTTINGDOWN(l) (((l)->state & MGR_SHUTTINGDOWN) != 0)#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0)struct dns_dispentry { unsigned int magic; dns_dispatch_t *disp; dns_messageid_t id; unsigned int bucket; isc_sockaddr_t host; isc_task_t *task; isc_taskaction_t action; void *arg; isc_boolean_t item_out; ISC_LIST(dns_dispatchevent_t) items; ISC_LINK(dns_dispentry_t) link;};#define INVALID_BUCKET (0xffffdead)struct dns_dispatch { /* Unlocked. */ unsigned int magic; /* magic */ dns_dispatchmgr_t *mgr; /* dispatch manager */ isc_task_t *task; /* internal task */ isc_socket_t *socket; /* isc socket attached to */ isc_sockaddr_t local; /* local address */ unsigned int maxrequests; /* max requests */ isc_event_t *ctlevent; /* Locked by mgr->lock. */ ISC_LINK(dns_dispatch_t) link; /* Locked by "lock". */ isc_mutex_t lock; /* locks all below */ isc_sockettype_t socktype; unsigned int attributes; unsigned int refcount; /* number of users */ dns_dispatchevent_t *failsafe_ev; /* failsafe cancel event */ unsigned int shutting_down : 1, shutdown_out : 1, connected : 1, tcpmsg_valid : 1, recv_pending : 1; /* is a recv() pending? */ isc_result_t shutdown_why; unsigned int requests; /* how many requests we have */ unsigned int tcpbuffers; /* allocated buffers */ dns_tcpmsg_t tcpmsg; /* for tcp streams */ dns_qid_t *qid;};#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ')#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC)#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p')#define VALID_RESPONSE(e) ISC_MAGIC_VALID((e), RESPONSE_MAGIC)#define DISPATCH_MAGIC ISC_MAGIC('D', 'i', 's', 'p')#define VALID_DISPATCH(e) ISC_MAGIC_VALID((e), DISPATCH_MAGIC)#define DNS_DISPATCHMGR_MAGIC ISC_MAGIC('D', 'M', 'g', 'r')#define VALID_DISPATCHMGR(e) ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC)#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \ (disp)->qid : (disp)->mgr->qid/* * Statics. */static dns_dispentry_t *bucket_search(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t, unsigned int);static isc_boolean_t destroy_disp_ok(dns_dispatch_t *);static void destroy_disp(isc_task_t *task, isc_event_t *event);static void udp_recv(isc_task_t *, isc_event_t *);static void tcp_recv(isc_task_t *, isc_event_t *);static void startrecv(dns_dispatch_t *);static dns_messageid_t dns_randomid(dns_qid_t *);static isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t);static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len);static void *allocate_udp_buffer(dns_dispatch_t *disp);static inline void free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev);static inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp);static void do_cancel(dns_dispatch_t *disp);static dns_dispentry_t *linear_first(dns_qid_t *disp);static dns_dispentry_t *linear_next(dns_qid_t *disp, dns_dispentry_t *resp);static void dispatch_free(dns_dispatch_t **dispp);static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, unsigned int maxrequests, unsigned int attributes, dns_dispatch_t **dispp);static isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr);static void destroy_mgr(dns_dispatchmgr_t **mgrp);static isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, unsigned int increment, dns_qid_t **qidp);static void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp);#define LVL(x) ISC_LOG_DEBUG(x)static voidmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);static voidmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) { char msgbuf[2048]; va_list ap; if (! isc_log_wouldlog(dns_lctx, level)) return; va_start(ap, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); va_end(ap); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, "dispatchmgr %p: %s", mgr, msgbuf);}static voiddispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4);static voiddispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) { char msgbuf[2048]; va_list ap; if (! isc_log_wouldlog(dns_lctx, level)) return; va_start(ap, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); va_end(ap); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, "dispatch %p: %s", disp, msgbuf);}static voidrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);static voidrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, int level, const char *fmt, ...){ char msgbuf[2048]; char peerbuf[256]; va_list ap; if (! isc_log_wouldlog(dns_lctx, level)) return; va_start(ap, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); va_end(ap); if (VALID_RESPONSE(resp)) { isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, "dispatch %p response %p %s: %s", disp, resp, peerbuf, msgbuf); } else { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, "dispatch %p req/resp %p: %s", disp, resp, msgbuf); }}static voidreseed_lfsr(isc_lfsr_t *lfsr, void *arg){ dns_dispatchmgr_t *mgr = arg; isc_result_t result; isc_uint32_t val; REQUIRE(VALID_DISPATCHMGR(mgr)); if (mgr->entropy != NULL) { result = isc_entropy_getdata(mgr->entropy, &val, sizeof(val), NULL, 0); INSIST(result == ISC_R_SUCCESS); lfsr->count = (val & 0x1f) + 32; lfsr->state = val; return; } lfsr->count = (random() & 0x1f) + 32; /* From 32 to 63 states */ lfsr->state = random();}/* * Return an unpredictable message ID. */static dns_messageid_tdns_randomid(dns_qid_t *qid) { isc_uint32_t id; id = isc_lfsr_generate32(&qid->qid_lfsr1, &qid->qid_lfsr2); return (dns_messageid_t)(id & 0xFFFF);}/* * Return a hash of the destination and message id. */static isc_uint32_tdns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id) { unsigned int ret; ret = isc_sockaddr_hash(dest, ISC_TRUE); ret ^= id; ret %= qid->qid_nbuckets; INSIST(ret < qid->qid_nbuckets); return (ret);}/* * Find the first entry in 'qid'. Returns NULL if there are no entries. */static dns_dispentry_t *linear_first(dns_qid_t *qid) { dns_dispentry_t *ret; unsigned int bucket; bucket = 0; while (bucket < qid->qid_nbuckets) { ret = ISC_LIST_HEAD(qid->qid_table[bucket]); if (ret != NULL) return (ret); bucket++; } return (NULL);}/* * Find the next entry after 'resp' in 'qid'. Return NULL if there are * no more entries. */static dns_dispentry_t *linear_next(dns_qid_t *qid, dns_dispentry_t *resp) { dns_dispentry_t *ret; unsigned int bucket; ret = ISC_LIST_NEXT(resp, link); if (ret != NULL) return (ret); bucket = resp->bucket; bucket++; while (bucket < qid->qid_nbuckets) { ret = ISC_LIST_HEAD(qid->qid_table[bucket]); if (ret != NULL) return (ret); bucket++; } return (NULL);}/* * The dispatch must be locked. */static isc_boolean_tdestroy_disp_ok(dns_dispatch_t *disp){ if (disp->refcount != 0) return (ISC_FALSE); if (disp->recv_pending != 0) return (ISC_FALSE); if (disp->shutting_down == 0) return (ISC_FALSE); return (ISC_TRUE);}/* * Called when refcount reaches 0 (and safe to destroy). * * The dispatcher must not be locked. * The manager must be locked. */static voiddestroy_disp(isc_task_t *task, isc_event_t *event) { dns_dispatch_t *disp; dns_dispatchmgr_t *mgr; isc_boolean_t killmgr; INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL); UNUSED(task); disp = event->ev_arg; mgr = disp->mgr; LOCK(&mgr->lock); ISC_LIST_UNLINK(mgr->list, disp, link); dispatch_log(disp, LVL(90), "shutting down; detaching from sock %p, task %p", disp->socket, disp->task); isc_socket_detach(&disp->socket); isc_task_detach(&disp->task); isc_event_free(&event); dispatch_free(&disp); killmgr = destroy_mgr_ok(mgr); UNLOCK(&mgr->lock); if (killmgr) destroy_mgr(&mgr);}/* * Find an entry for query ID 'id' and socket address 'dest' in 'qid'. * Return NULL if no such entry exists. */static dns_dispentry_t *bucket_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, unsigned int bucket){ dns_dispentry_t *res; REQUIRE(bucket < qid->qid_nbuckets); res = ISC_LIST_HEAD(qid->qid_table[bucket]); while (res != NULL) { if ((res->id == id) && isc_sockaddr_equal(dest, &res->host)) return (res); res = ISC_LIST_NEXT(res, link); } return (NULL);}static voidfree_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { INSIST(buf != NULL && len != 0); switch (disp->socktype) { case isc_sockettype_tcp: INSIST(disp->tcpbuffers > 0); disp->tcpbuffers--; isc_mem_put(disp->mgr->mctx, buf, len); break; case isc_sockettype_udp: LOCK(&disp->mgr->buffer_lock); INSIST(disp->mgr->buffers > 0); INSIST(len == disp->mgr->buffersize); disp->mgr->buffers--; isc_mempool_put(disp->mgr->bpool, buf); UNLOCK(&disp->mgr->buffer_lock); break; default: INSIST(0); break; }}static void *allocate_udp_buffer(dns_dispatch_t *disp) { void *temp; LOCK(&disp->mgr->buffer_lock); temp = isc_mempool_get(disp->mgr->bpool); if (temp != NULL) disp->mgr->buffers++; UNLOCK(&disp->mgr->buffer_lock); return (temp);}static inline voidfree_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { if (disp->failsafe_ev == ev) { INSIST(disp->shutdown_out == 1); disp->shutdown_out = 0; return; } isc_mempool_put(disp->mgr->epool, ev);}static inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp) { dns_dispatchevent_t *ev; ev = isc_mempool_get(disp->mgr->epool); if (ev == NULL) return (NULL); ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0, NULL, NULL, NULL, NULL, NULL); return (ev);}/* * General flow: * * If I/O result == CANCELED or error, free the buffer. * * If query, free the buffer, restart. * * If response: * Allocate event, fill in details. * If cannot allocate, free buffer, restart. * find target. If not found, free buffer, restart. * if event queue is not empty, queue. else, send. * restart. */static voidudp_recv(isc_task_t *task, isc_event_t *ev_in) { isc_socketevent_t *ev = (isc_socketevent_t *)ev_in; dns_dispatch_t *disp = ev_in->ev_arg; dns_messageid_t id; isc_result_t dres; isc_buffer_t source; unsigned int flags; dns_dispentry_t *resp; dns_dispatchevent_t *rev; unsigned int bucket; isc_boolean_t killit; isc_boolean_t queue_response; dns_dispatchmgr_t *mgr; dns_qid_t *qid; isc_netaddr_t netaddr; int match; UNUSED(task); LOCK(&disp->lock); mgr = disp->mgr; qid = mgr->qid; dispatch_log(disp, LVL(90), "got packet: requests %d, buffers %d, recvs %d", disp->requests, disp->mgr->buffers, disp->recv_pending); if (ev->ev_type == ISC_SOCKEVENT_RECVDONE) { /* * Unless the receive event was imported from a listening * interface, in which case the event type is * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending. */ INSIST(disp->recv_pending != 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -