diff options
Diffstat (limited to 'lib/dns/dispatch.c')
-rw-r--r-- | lib/dns/dispatch.c | 580 |
1 files changed, 392 insertions, 188 deletions
diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index 3ecf1006..2d0cb394 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -1,21 +1,21 @@ /* * Copyright (C) 1999, 2000 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. + * + * 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: dispatch.c,v 1.57.2.4 2000/09/08 22:16:49 gson Exp $ */ +/* $Id: dispatch.c,v 1.75 2000/11/10 03:08:57 gson Exp $ */ #include <config.h> @@ -30,6 +30,7 @@ #include <isc/task.h> #include <isc/util.h> +#include <dns/acl.h> #include <dns/dispatch.h> #include <dns/events.h> #include <dns/log.h> @@ -37,21 +38,43 @@ #include <dns/tcpmsg.h> #include <dns/types.h> +typedef ISC_LIST(dns_dispentry_t) dns_displist_t; + +typedef struct dns_qid { + unsigned int magic; + isc_mem_t *mctx; + 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; /* 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 request/reply */ isc_mempool_t *dpool; /* dispatch allocations */ + isc_mempool_t *bpool; /* memory pool for buffers */ isc_entropy_t *entropy; /* entropy source */ }; @@ -77,8 +100,6 @@ struct dns_dispentry { #define INVALID_BUCKET (0xffffdead) -typedef ISC_LIST(dns_dispentry_t) dns_displist_t; - struct dns_dispatch { /* Unlocked. */ unsigned int magic; /* magic */ @@ -86,9 +107,8 @@ struct dns_dispatch { isc_task_t *task; /* internal task */ isc_socket_t *socket; /* isc socket attached to */ isc_sockaddr_t local; /* local address */ - unsigned int buffersize; /* size of each buffer */ unsigned int maxrequests; /* max requests */ - unsigned int maxbuffers; /* max buffers */ + dns_acl_t *blackhole; /* Locked by mgr->lock. */ ISC_LINK(dns_dispatch_t) link; @@ -98,7 +118,6 @@ struct dns_dispatch { isc_sockettype_t socktype; unsigned int attributes; unsigned int refcount; /* number of users */ - isc_mempool_t *bpool; /* memory pool for buffers */ dns_dispatchevent_t *failsafe_ev; /* failsafe cancel event */ unsigned int recvs; /* recv() calls outstanding */ unsigned int recvs_wanted; /* recv() calls wanted */ @@ -108,17 +127,16 @@ struct dns_dispatch { tcpmsg_valid : 1; isc_result_t shutdown_why; unsigned int requests; /* how many requests we have */ - unsigned int buffers; /* allocated buffers */ + unsigned int tcpbuffers; /* allocated buffers */ ISC_LIST(dns_dispentry_t) rq_handlers; /* request handler list */ ISC_LIST(dns_dispatchevent_t) rq_events; /* holder for rq events */ dns_tcpmsg_t tcpmsg; /* for tcp streams */ - isc_lfsr_t qid_lfsr1; /* state generator info */ - isc_lfsr_t qid_lfsr2; /* state generator info */ - unsigned int qid_nbuckets; /* hash table size */ - unsigned int qid_increment; /* id increment on collision */ - dns_displist_t *qid_table; /* the table itself */ + dns_qid_t *qid; }; +#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ') +#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC) + #define REQUEST_MAGIC ISC_MAGIC('D', 'r', 'q', 's') #define VALID_REQUEST(e) ISC_MAGIC_VALID((e), REQUEST_MAGIC) @@ -131,18 +149,20 @@ struct dns_dispatch { #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_dispatch_t *, isc_sockaddr_t *, +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(dns_dispatch_t **); static void udp_recv(isc_task_t *, isc_event_t *); static void tcp_recv(isc_task_t *, isc_event_t *); static inline void startrecv(dns_dispatch_t *); -static isc_uint32_t dns_randomid(dns_dispatch_t *); -static isc_uint32_t dns_hash(dns_dispatch_t *, isc_sockaddr_t *, isc_uint32_t); +static isc_uint32_t dns_randomid(dns_qid_t *); +static isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, isc_uint32_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); @@ -150,23 +170,22 @@ static inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp); static void do_next_request(dns_dispatch_t *disp, dns_dispentry_t *resp); static void do_next_response(dns_dispatch_t *disp, dns_dispentry_t *resp); static void do_cancel(dns_dispatch_t *disp, dns_dispentry_t *resp); -static dns_dispentry_t *linear_first(dns_dispatch_t *disp); -static dns_dispentry_t *linear_next(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 buffersize, - unsigned int maxbuffers, unsigned int maxrequests, - unsigned int buckets, - unsigned int increment, 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) @@ -194,7 +213,7 @@ dispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) { if (! isc_log_wouldlog(dns_lctx, level)) return; - + va_start(ap, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); va_end(ap); @@ -223,12 +242,12 @@ request_log(dns_dispatch_t *disp, dns_dispentry_t *resp, isc_sockaddr_format(&resp->host, peerbuf, sizeof peerbuf); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, - "dispatch %p request %p %s: %s", disp, resp, + "dispatch %p response %p %s: %s", disp, resp, peerbuf, msgbuf); } else if (VALID_REQUEST(resp)) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, level, - "dispatch %p response %p: %s", disp, resp, + "dispatch %p request %p: %s", disp, resp, msgbuf); } else { isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, @@ -241,11 +260,12 @@ request_log(dns_dispatch_t *disp, dns_dispentry_t *resp, static void reseed_lfsr(isc_lfsr_t *lfsr, void *arg) { - dns_dispatch_t *disp = (dns_dispatch_t *)arg; - dns_dispatchmgr_t *mgr = disp->mgr; + 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); @@ -263,10 +283,10 @@ reseed_lfsr(isc_lfsr_t *lfsr, void *arg) * Return an unpredictable message ID. */ static isc_uint32_t -dns_randomid(dns_dispatch_t *disp) { +dns_randomid(dns_qid_t *qid) { isc_uint32_t id; - id = isc_lfsr_generate32(&disp->qid_lfsr1, &disp->qid_lfsr2); + id = isc_lfsr_generate32(&qid->qid_lfsr1, &qid->qid_lfsr2); return (id & 0x0000ffffU); } @@ -275,27 +295,27 @@ dns_randomid(dns_dispatch_t *disp) { * Return a hash of the destination and message id. */ static isc_uint32_t -dns_hash(dns_dispatch_t *disp, isc_sockaddr_t *dest, isc_uint32_t id) { +dns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, isc_uint32_t id) { unsigned int ret; ret = isc_sockaddr_hash(dest, ISC_TRUE); ret ^= (id & 0x0000ffff); /* important to mask off garbage bits */ - ret %= disp->qid_nbuckets; + ret %= qid->qid_nbuckets; - INSIST(ret < disp->qid_nbuckets); + INSIST(ret < qid->qid_nbuckets); return (ret); } static dns_dispentry_t * -linear_first(dns_dispatch_t *disp) { +linear_first(dns_qid_t *qid) { dns_dispentry_t *ret; unsigned int bucket; bucket = 0; - while (bucket < disp->qid_nbuckets) { - ret = ISC_LIST_HEAD(disp->qid_table[bucket]); + while (bucket < qid->qid_nbuckets) { + ret = ISC_LIST_HEAD(qid->qid_table[bucket]); if (ret != NULL) return (ret); bucket++; @@ -305,7 +325,7 @@ linear_first(dns_dispatch_t *disp) { } static dns_dispentry_t * -linear_next(dns_dispatch_t *disp, dns_dispentry_t *resp) { +linear_next(dns_qid_t *qid, dns_dispentry_t *resp) { dns_dispentry_t *ret; unsigned int bucket; @@ -314,8 +334,8 @@ linear_next(dns_dispatch_t *disp, dns_dispentry_t *resp) { return (ret); bucket = resp->bucket; - while (bucket < disp->qid_nbuckets) { - ret = ISC_LIST_HEAD(disp->qid_table[bucket]); + while (bucket < qid->qid_nbuckets) { + ret = ISC_LIST_HEAD(qid->qid_table[bucket]); if (ret != NULL) return (ret); bucket++; @@ -372,14 +392,14 @@ destroy_disp(dns_dispatch_t **dispp) { static dns_dispentry_t * -bucket_search(dns_dispatch_t *disp, isc_sockaddr_t *dest, dns_messageid_t id, +bucket_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, unsigned int bucket) { dns_dispentry_t *res; - REQUIRE(bucket < disp->qid_nbuckets); + REQUIRE(bucket < qid->qid_nbuckets); - res = ISC_LIST_HEAD(disp->qid_table[bucket]); + res = ISC_LIST_HEAD(qid->qid_table[bucket]); while (res != NULL) { if ((res->id == id) && isc_sockaddr_equal(dest, &res->host)) @@ -393,16 +413,21 @@ bucket_search(dns_dispatch_t *disp, isc_sockaddr_t *dest, dns_messageid_t id, static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { INSIST(buf != NULL && len != 0); - INSIST(disp->buffers > 0); - disp->buffers--; + 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: - INSIST(len == disp->buffersize); - isc_mempool_put(disp->bpool, buf); + 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); @@ -414,10 +439,12 @@ static void * allocate_udp_buffer(dns_dispatch_t *disp) { void *temp; - temp = isc_mempool_get(disp->bpool); + LOCK(&disp->mgr->buffer_lock); + temp = isc_mempool_get(disp->mgr->bpool); if (temp != NULL) - disp->buffers++; + disp->mgr->buffers++; + UNLOCK(&disp->mgr->buffer_lock); return (temp); } @@ -439,6 +466,10 @@ 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); } @@ -481,16 +512,20 @@ udp_recv(isc_task_t *task, isc_event_t *ev_in) { isc_boolean_t queue_request; 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->buffers, disp->recvs); + disp->requests, disp->mgr->buffers, disp->recvs); INSIST(disp->recvs > 0); disp->recvs--; @@ -542,6 +577,27 @@ udp_recv(isc_task_t *task, isc_event_t *ev_in) { } /* + * If this is from a blackholed address, drop it. + */ + isc_netaddr_fromsockaddr(&netaddr, &ev->address); + if (disp->blackhole != NULL && + dns_acl_match(&netaddr, NULL, disp->blackhole, + NULL, &match, NULL) == ISC_R_SUCCESS && + match > 0) + { + if (isc_log_wouldlog(dns_lctx, LVL(10))) { + char netaddrstr[ISC_NETADDR_FORMATSIZE]; + isc_netaddr_format(&netaddr, netaddrstr, + sizeof(netaddrstr)); + dispatch_log(disp, LVL(10), + "blackholed packet from %s", + netaddrstr); + } + free_buffer(disp, ev->region.base, ev->region.length); + goto restart; + } + + /* * Peek into the buffer to see what we can see. */ isc_buffer_init(&source, ev->region.base, ev->region.length); @@ -580,8 +636,10 @@ udp_recv(isc_task_t *task, isc_event_t *ev_in) { /* query */ } else { /* response */ - bucket = dns_hash(disp, &ev->address, id); - resp = bucket_search(disp, &ev->address, id, bucket); + bucket = dns_hash(qid, &ev->address, id); + LOCK(&qid->lock); + resp = bucket_search(qid, &ev->address, id, bucket); + UNLOCK(&qid->lock); dispatch_log(disp, LVL(90), "search for response in bucket %d: %s", bucket, (resp == NULL ? "NOT FOUND" : "FOUND")); @@ -589,9 +647,9 @@ udp_recv(isc_task_t *task, isc_event_t *ev_in) { if (resp == NULL) { free_buffer(disp, ev->region.base, ev->region.length); goto restart; - } + } queue_response = resp->item_out; - rev = allocate_event(disp); + rev = allocate_event(resp->disp); if (rev == NULL) { free_buffer(disp, ev->region.base, ev->region.length); goto restart; @@ -671,16 +729,18 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { isc_boolean_t killit; isc_boolean_t queue_request; isc_boolean_t queue_response; + dns_qid_t *qid; UNUSED(task); REQUIRE(VALID_DISPATCH(disp)); mgr = disp->mgr; + qid = disp->qid; dispatch_log(disp, LVL(90), "got TCP packet: requests %d, buffers %d, recvs %d", - disp->requests, disp->buffers, disp->recvs); + disp->requests, disp->tcpbuffers, disp->recvs); LOCK(&disp->lock); @@ -718,7 +778,7 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { } /* - * The event is statically allocated in the tcpmsg + * The event is statically allocated in the tcpmsg * structure, and destroy_disp() frees the tcpmsg, so we must * free the event *before* calling destroy_disp(). */ @@ -770,6 +830,9 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { queue_request = ISC_FALSE; queue_response = ISC_FALSE; if ((flags & DNS_MESSAGEFLAG_QR) == 0) { + /* + * Query. + */ resp = ISC_LIST_HEAD(disp->rq_handlers); while (resp != NULL) { if (resp->item_out == ISC_FALSE) @@ -781,11 +844,14 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { rev = allocate_event(disp); if (rev == NULL) goto restart; - /* query */ } else { - /* response */ - bucket = dns_hash(disp, &tcpmsg->address, id); - resp = bucket_search(disp, &tcpmsg->address, id, bucket); + /* + * Response. + */ + bucket = dns_hash(qid, &tcpmsg->address, id); + LOCK(&qid->lock); + resp = bucket_search(qid, &tcpmsg->address, id, bucket); + UNLOCK(&qid->lock); dispatch_log(disp, LVL(90), "search for response in bucket %d: %s", bucket, (resp == NULL ? "NOT FOUND" : "FOUND")); @@ -804,7 +870,7 @@ tcp_recv(isc_task_t *task, isc_event_t *ev_in) { * Send the event off. */ dns_tcpmsg_keepbuffer(tcpmsg, &rev->buffer); - disp->buffers++; + disp->tcpbuffers++; rev->result = ISC_R_SUCCESS; rev->id = id; rev->addr = tcpmsg->address; @@ -853,7 +919,7 @@ startrecv(dns_dispatch_t *disp) { if (disp->recvs >= wanted) return; - if (disp->buffers >= disp->maxbuffers) + if (disp->mgr->buffers >= disp->mgr->maxbuffers) return; while (disp->recvs < wanted) { @@ -862,7 +928,7 @@ startrecv(dns_dispatch_t *disp) { * UDP reads are always maximal. */ case isc_sockettype_udp: - region.length = disp->buffersize; + region.length = disp->mgr->buffersize; region.base = allocate_udp_buffer(disp); if (region.base == NULL) return; @@ -933,18 +999,26 @@ destroy_mgr(dns_dispatchmgr_t **mgrp) { mctx = mgr->mctx; mgr->magic = 0; - mgr->mctx = 0; - isc_mutex_destroy(&mgr->lock); + mgr->mctx = NULL; + DESTROYLOCK(&mgr->lock); mgr->state = 0; isc_mempool_destroy(&mgr->epool); isc_mempool_destroy(&mgr->rpool); isc_mempool_destroy(&mgr->dpool); + isc_mempool_destroy(&mgr->bpool); - isc_mutex_destroy(&mgr->pool_lock); + DESTROYLOCK(&mgr->pool_lock); if (mgr->entropy != NULL) isc_entropy_detach(&mgr->entropy); + if (mgr->qid != NULL) + qid_destroy(mctx, &mgr->qid); + + DESTROYLOCK(&mgr->buffer_lock); + + if (mgr->blackhole != NULL) + dns_acl_detach(&mgr->blackhole); isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); isc_mem_detach(&mctx); @@ -994,14 +1068,20 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, mgr->mctx = NULL; isc_mem_attach(mctx, &mgr->mctx); + mgr->blackhole = NULL; + result = isc_mutex_init(&mgr->lock); if (result != ISC_R_SUCCESS) goto deallocate; - result = isc_mutex_init(&mgr->pool_lock); + result = isc_mutex_init(&mgr->buffer_lock); if (result != ISC_R_SUCCESS) goto kill_lock; + result = isc_mutex_init(&mgr->pool_lock); + if (result != ISC_R_SUCCESS) + goto kill_buffer_lock; + mgr->epool = NULL; if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t), &mgr->epool) != ISC_R_SUCCESS) { @@ -1035,11 +1115,16 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_mempool_setfreemax(mgr->dpool, 1024); isc_mempool_associatelock(mgr->dpool, &mgr->pool_lock); - mgr->magic = DNS_DISPATCHMGR_MAGIC; + mgr->buffers = 0; + mgr->buffersize = 0; + mgr->maxbuffers = 0; + mgr->bpool = NULL; + mgr->entropy = NULL; + mgr->qid = NULL; mgr->state = 0; ISC_LIST_INIT(mgr->list); + mgr->magic = DNS_DISPATCHMGR_MAGIC; - mgr->entropy = NULL; if (entropy != NULL) isc_entropy_attach(entropy, &mgr->entropy); @@ -1055,9 +1140,11 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, kill_epool: isc_mempool_destroy(&mgr->epool); kill_pool_lock: - isc_mutex_destroy(&mgr->pool_lock); + DESTROYLOCK(&mgr->pool_lock); + kill_buffer_lock: + DESTROYLOCK(&mgr->buffer_lock); kill_lock: - isc_mutex_destroy(&mgr->lock); + DESTROYLOCK(&mgr->lock); deallocate: isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); isc_mem_detach(&mgr->mctx); @@ -1065,6 +1152,84 @@ dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, return (result); } +void +dns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole) { + REQUIRE(VALID_DISPATCHMGR(mgr)); + REQUIRE(mgr->blackhole == NULL); + dns_acl_attach(blackhole, &mgr->blackhole); +} + +isc_result_t +dns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr, dns_acl_t **blackholep) { + REQUIRE(VALID_DISPATCHMGR(mgr)); + REQUIRE(blackholep != NULL && *blackholep == NULL); + + if (mgr->blackhole == NULL) + return (ISC_R_NOTFOUND); + dns_acl_attach(mgr->blackhole, blackholep); + return (ISC_R_SUCCESS); +} + +static isc_result_t +dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, + unsigned int buffersize, unsigned int maxbuffers, + unsigned int buckets, unsigned int increment) +{ + isc_result_t result; + + REQUIRE(VALID_DISPATCHMGR(mgr)); + REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); + REQUIRE(maxbuffers > 0); + REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ + REQUIRE(increment > buckets); + + /* + * Keep some number of items around. This should be a config + * option. For now, keep 8, but later keep at least two even + * if the caller wants less. This allows us to ensure certain + * things, like an event can be "freed" and the next allocation + * will always succeed. + * + * Note that if limits are placed on anything here, we use one + * event internally, so the actual limit should be "wanted + 1." + * + * XXXMLG + */ + + if (maxbuffers < 8) + maxbuffers = 8; + + LOCK(&mgr->buffer_lock); + if (mgr->bpool != NULL) { + isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); + mgr->maxbuffers = maxbuffers; + UNLOCK(&mgr->buffer_lock); + return (ISC_R_SUCCESS); + } + + if (isc_mempool_create(mgr->mctx, buffersize, + &mgr->bpool) != ISC_R_SUCCESS) { + return (ISC_R_NOMEMORY); + } + + isc_mempool_setname(mgr->bpool, "dispmgr_bpool"); + isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); + isc_mempool_associatelock(mgr->bpool, &mgr->pool_lock); + + result = qid_allocate(mgr, buckets, increment, &mgr->qid); + if (result != ISC_R_SUCCESS) + goto cleanup; + + mgr->buffersize = buffersize; + mgr->maxbuffers = maxbuffers; + UNLOCK(&mgr->buffer_lock); + return (ISC_R_SUCCESS); + + cleanup: + isc_mempool_destroy(&mgr->bpool); + UNLOCK(&mgr->buffer_lock); + return (ISC_R_NOMEMORY); +} void dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { @@ -1084,7 +1249,7 @@ dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { UNLOCK(&mgr->lock); mgr_log(mgr, LVL(90), "destroy: killit=%d", killit); - + if (killit) destroy_mgr(&mgr); } @@ -1153,24 +1318,90 @@ dispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local, return (result); } +static isc_result_t +qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, + unsigned int increment, dns_qid_t **qidp) +{ + dns_qid_t *qid; + unsigned int i; + + REQUIRE(VALID_DISPATCHMGR(mgr)); + REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ + REQUIRE(increment > buckets); + REQUIRE(qidp != NULL && *qidp == NULL); + + qid = isc_mem_get(mgr->mctx, sizeof(*qid)); + if (qid == NULL) + return (ISC_R_NOMEMORY); + + qid->qid_table = isc_mem_get(mgr->mctx, + buckets * sizeof(dns_displist_t)); + if (qid->qid_table == NULL) { + isc_mem_put(mgr->mctx, qid, sizeof(*qid)); + return (ISC_R_NOMEMORY); + } + + if (isc_mutex_init(&qid->lock) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init failed"); + isc_mem_put(mgr->mctx, qid->qid_table, + buckets * sizeof(dns_displist_t)); + isc_mem_put(mgr->mctx, qid, sizeof(*qid)); + return (ISC_R_UNEXPECTED); + } + + for (i = 0 ; i < buckets ; i++) + ISC_LIST_INIT(qid->qid_table[i]); + + qid->qid_nbuckets = buckets; + qid->qid_increment = increment; + qid->magic = QID_MAGIC; + + /* + * Initialize to a 32-bit LFSR. Both of these are from Applied + * Cryptography. + * + * lfsr1: + * x^32 + x^7 + x^5 + x^3 + x^2 + x + 1 + * + * lfsr2: + * x^32 + x^7 + x^6 + x^2 + 1 + */ + isc_lfsr_init(&qid->qid_lfsr1, 0, 32, 0x80000057U, + 0, reseed_lfsr, mgr); + isc_lfsr_init(&qid->qid_lfsr2, 0, 32, 0x800000c2U, + 0, reseed_lfsr, mgr); + *qidp = qid; + return (ISC_R_SUCCESS); +} + +static void +qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) { + dns_qid_t *qid; + + REQUIRE(qidp != NULL); + qid = *qidp; + + REQUIRE(VALID_QID(qid)); + + *qidp = NULL; + qid->magic = 0; + isc_mem_put(mctx, qid->qid_table, + qid->qid_nbuckets * sizeof(dns_displist_t)); + DESTROYLOCK(&qid->lock); + isc_mem_put(mctx, qid, sizeof(*qid)); +} + /* * Allocate and set important limits. */ static isc_result_t -dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int buffersize, - unsigned int maxbuffers, unsigned int maxrequests, - unsigned int buckets, unsigned int increment, +dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests, dns_dispatch_t **dispp) { - unsigned int i; dns_dispatch_t *disp; isc_result_t res; REQUIRE(VALID_DISPATCHMGR(mgr)); - REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); - REQUIRE(maxbuffers > 0); - REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ - REQUIRE(increment > buckets); REQUIRE(dispp != NULL && *dispp == NULL); /* @@ -1184,9 +1415,7 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int buffersize, disp->magic = 0; disp->mgr = mgr; - disp->buffersize = buffersize; disp->maxrequests = maxrequests; - disp->maxbuffers = maxbuffers; disp->attributes = 0; ISC_LINK_INIT(disp, link); disp->refcount = 1; @@ -1198,72 +1427,26 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int buffersize, disp->tcpmsg_valid = 0; disp->shutdown_why = ISC_R_UNEXPECTED; disp->requests = 0; - disp->buffers = 0; + disp->tcpbuffers = 0; + disp->qid = NULL; ISC_LIST_INIT(disp->rq_handlers); ISC_LIST_INIT(disp->rq_events); - disp->qid_table = isc_mem_get(mgr->mctx, - buckets * sizeof(dns_displist_t)); - if (disp->qid_table == NULL) { - res = ISC_R_NOMEMORY; - goto deallocate; - } - - for (i = 0 ; i < buckets ; i++) - ISC_LIST_INIT(disp->qid_table[i]); - - disp->qid_nbuckets = buckets; - disp->qid_increment = increment; - if (isc_mutex_init(&disp->lock) != ISC_R_SUCCESS) { res = ISC_R_UNEXPECTED; UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init failed"); - goto deallocate_qidtable; - } - - disp->bpool = NULL; - if (isc_mempool_create(mgr->mctx, buffersize, - &disp->bpool) != ISC_R_SUCCESS) { - res = ISC_R_NOMEMORY; - goto kill_lock; + goto deallocate; } - isc_mempool_setmaxalloc(disp->bpool, maxbuffers); - isc_mempool_setname(disp->bpool, "disp_bpool"); - - /* - * Keep some number of items around. This should be a config - * option. For now, keep 8, but later keep at least two even - * if the caller wants less. This allows us to ensure certain - * things, like an event can be "freed" and the next allocation - * will always succeed. - * - * Note that if limits are placed on anything here, we use one - * event internally, so the actual limit should be "wanted + 1." - * - * XXXMLG - */ - isc_mempool_setfreemax(disp->bpool, 8); disp->failsafe_ev = allocate_event(disp); if (disp->failsafe_ev == NULL) { res = ISC_R_NOMEMORY; - goto kill_bpool; + goto kill_lock; } - /* - * Initialize to a 32-bit LFSR. Both of these are from Applied - * Cryptography. - * - * lfsr1: - * x^32 + x^7 + x^5 + x^3 + x^2 + x + 1 - * - * lfsr2: - * x^32 + x^7 + x^6 + x^2 + 1 - */ - isc_lfsr_init(&disp->qid_lfsr1, 0, 32, 0x80000057U, - 0, reseed_lfsr, disp); - isc_lfsr_init(&disp->qid_lfsr2, 0, 32, 0x800000c2U, - 0, reseed_lfsr, disp); + disp->blackhole = NULL; + if (mgr->blackhole != NULL) + dns_acl_attach(mgr->blackhole, &disp->blackhole); disp->magic = DISPATCH_MAGIC; @@ -1273,13 +1456,8 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int buffersize, /* * error returns */ - kill_bpool: - isc_mempool_destroy(&disp->bpool); kill_lock: - isc_mutex_destroy(&disp->lock); - deallocate_qidtable: - isc_mem_put(mgr->mctx, disp->qid_table, - disp->qid_nbuckets * sizeof(dns_displist_t)); + DESTROYLOCK(&disp->lock); deallocate: isc_mempool_put(mgr->dpool, disp); @@ -1320,23 +1498,23 @@ dispatch_free(dns_dispatch_t **dispp) ev = ISC_LIST_HEAD(disp->rq_events); } - INSIST(disp->buffers == 0); + INSIST(disp->tcpbuffers == 0); INSIST(disp->requests == 0); INSIST(disp->recvs == 0); isc_mempool_put(mgr->epool, disp->failsafe_ev); disp->failsafe_ev = NULL; - isc_mempool_destroy(&disp->bpool); - isc_mutex_destroy(&disp->lock); - isc_mem_put(mgr->mctx, disp->qid_table, - disp->qid_nbuckets * sizeof(dns_displist_t)); + if (disp->qid != NULL) + qid_destroy(mgr->mctx, &disp->qid); disp->mgr = NULL; + DESTROYLOCK(&disp->lock); + if (disp->blackhole != NULL) + dns_acl_detach(&disp->blackhole); disp->magic = 0; isc_mempool_put(mgr->dpool, disp); } - isc_result_t dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, isc_taskmgr_t *taskmgr, unsigned int buffersize, @@ -1347,26 +1525,33 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, isc_result_t result; dns_dispatch_t *disp; + UNUSED(maxbuffers); + UNUSED(buffersize); + REQUIRE(VALID_DISPATCHMGR(mgr)); REQUIRE(isc_socket_gettype(sock) == isc_sockettype_tcp); REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0); + REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0); attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ LOCK(&mgr->lock); /* - * dispatch_allocate() checks mgr, buffersize, maxbuffers, - * buckets, and increment for us. + * dispatch_allocate() checks mgr for us. + * qid_allocate() checks buckets and increment for us. */ disp = NULL; - result = dispatch_allocate(mgr, buffersize, maxbuffers, maxrequests, - buckets, increment, &disp); + result = dispatch_allocate(mgr, maxrequests, &disp); if (result != ISC_R_SUCCESS) { UNLOCK(&mgr->lock); return (result); } + result = qid_allocate(mgr, buckets, increment, &disp->qid); + if (result != ISC_R_SUCCESS) + goto deallocate_dispatch; + disp->socktype = isc_sockettype_tcp; disp->socket = NULL; isc_socket_attach(sock, &disp->socket); @@ -1383,8 +1568,6 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, dns_tcpmsg_init(mgr->mctx, disp->socket, &disp->tcpmsg); disp->tcpmsg_valid = 1; - attributes &= ~DNS_DISPATCHATTR_UDP; - attributes |= DNS_DISPATCHATTR_TCP; disp->attributes = attributes; /* @@ -1405,6 +1588,7 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, */ kill_socket: isc_socket_detach(&disp->socket); + deallocate_dispatch: dispatch_free(&disp); UNLOCK(&mgr->lock); @@ -1435,6 +1619,11 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, REQUIRE(dispp != NULL && *dispp == NULL); REQUIRE((attributes & DNS_DISPATCHATTR_TCP) == 0); + result = dns_dispatchmgr_setudp(mgr, buffersize, maxbuffers, + buckets, increment); + if (result != ISC_R_SUCCESS) + return (result); + LOCK(&mgr->lock); /* @@ -1445,11 +1634,6 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, if (result == ISC_R_SUCCESS) { disp->refcount++; - if (disp->maxbuffers < maxbuffers) { - isc_mempool_setmaxalloc(disp->bpool, maxbuffers); - disp->maxbuffers = maxbuffers; - } - if (disp->maxrequests < maxrequests) disp->maxrequests = maxrequests; @@ -1465,8 +1649,7 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, * Nope, create one. */ result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr, - buffersize, maxbuffers, maxrequests, - buckets, increment, attributes, &disp); + maxrequests, attributes, &disp); if (result != ISC_R_SUCCESS) { UNLOCK(&mgr->lock); return (result); @@ -1483,9 +1666,8 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, - isc_sockaddr_t *localaddr, unsigned int buffersize, - unsigned int maxbuffers, unsigned int maxrequests, - unsigned int buckets, unsigned int increment, + isc_sockaddr_t *localaddr, + unsigned int maxrequests, unsigned int attributes, dns_dispatch_t **dispp) { @@ -1494,12 +1676,10 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_socket_t *sock; /* - * dispatch_allocate() checks mgr, buffersize, maxbuffers, - * buckets, and increment for us. + * dispatch_allocate() checks mgr for us. */ disp = NULL; - result = dispatch_allocate(mgr, buffersize, maxbuffers, maxrequests, - buckets, increment, &disp); + result = dispatch_allocate(mgr, maxrequests, &disp); if (result != ISC_R_SUCCESS) return (result); @@ -1617,6 +1797,7 @@ dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, dns_messageid_t id; int i; isc_boolean_t ok; + dns_qid_t *qid; REQUIRE(VALID_DISPATCH(disp)); REQUIRE(task != NULL); @@ -1639,18 +1820,21 @@ dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, /* * Try somewhat hard to find an unique ID. */ - id = dns_randomid(disp); - bucket = dns_hash(disp, dest, id); + qid = DNS_QID(disp); + LOCK(&qid->lock); + id = dns_randomid(qid); + bucket = dns_hash(qid, dest, id); ok = ISC_FALSE; for (i = 0 ; i < 64 ; i++) { - if (bucket_search(disp, dest, id, bucket) == NULL) { + if (bucket_search(qid, dest, id, bucket) == NULL) { ok = ISC_TRUE; break; } - id += disp->qid_increment; + id += qid->qid_increment; id &= 0x0000ffff; - bucket = dns_hash(disp, dest, id); + bucket = dns_hash(qid, dest, id); } + UNLOCK(&qid->lock); if (!ok) { UNLOCK(&disp->lock); @@ -1677,7 +1861,9 @@ dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, ISC_LIST_INIT(res->items); ISC_LINK_INIT(res, link); res->magic = RESPONSE_MAGIC; - ISC_LIST_APPEND(disp->qid_table[bucket], res, link); + LOCK(&qid->lock); + ISC_LIST_APPEND(qid->qid_table[bucket], res, link); + UNLOCK(&qid->lock); request_log(disp, res, LVL(90), "attached to task %p", res->task); @@ -1698,7 +1884,7 @@ void dns_dispatch_starttcp(dns_dispatch_t *disp) { REQUIRE(VALID_DISPATCH(disp)); - + dispatch_log(disp, LVL(90), "starttcp %p", disp->task); LOCK(&disp->lock); @@ -1719,6 +1905,7 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, isc_boolean_t killit; unsigned int n; isc_eventlist_t events; + dns_qid_t *qid; REQUIRE(resp != NULL); REQUIRE(VALID_RESPONSE(*resp)); @@ -1731,6 +1918,8 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, mgr = disp->mgr; REQUIRE(VALID_DISPATCHMGR(mgr)); + qid = DNS_QID(disp); + if (sockevent != NULL) { REQUIRE(*sockevent != NULL); ev = *sockevent; @@ -1755,7 +1944,9 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp, bucket = res->bucket; - ISC_LIST_UNLINK(disp->qid_table[bucket], res, link); + LOCK(&qid->lock); + ISC_LIST_UNLINK(qid->qid_table[bucket], res, link); + UNLOCK(&qid->lock); if (ev == NULL && res->item_out) { /* @@ -1852,8 +2043,7 @@ dns_dispatch_addrequest(dns_dispatch_t *disp, res->arg = arg; res->item_out = ISC_FALSE; ISC_LIST_INIT(res->items); - ISC_LINK_INIT(res, link); - ISC_LIST_APPEND(disp->rq_handlers, res, link); + ISC_LIST_APPENDUNSAFE(disp->rq_handlers, res, link); request_log(disp, res, LVL(90), "attaching task %p", res->task); @@ -2056,10 +2246,13 @@ do_next_request(dns_dispatch_t *disp, dns_dispentry_t *resp) { static void do_cancel(dns_dispatch_t *disp, dns_dispentry_t *resp) { dns_dispatchevent_t *ev; + dns_qid_t *qid; if (disp->shutdown_out == 1) return; + qid = DNS_QID(disp); + /* * If no target given, find the first request handler. If * there are packets waiting for any handler, however, don't @@ -2080,15 +2273,20 @@ do_cancel(dns_dispatch_t *disp, dns_dispentry_t *resp) { * Search for the first response handler without packets outstanding. */ if (resp == NULL) { - resp = linear_first(disp); - if (resp == NULL) /* no first item? */ + LOCK(&qid->lock); + resp = linear_first(qid); + if (resp == NULL) { + /* no first item? */ + UNLOCK(&qid->lock); return; + } do { if (resp->item_out == ISC_FALSE) break; - resp = linear_next(disp, resp); + resp = linear_next(qid, resp); } while (resp != NULL); + UNLOCK(&qid->lock); } /* @@ -2108,7 +2306,7 @@ do_cancel(dns_dispatch_t *disp, dns_dispentry_t *resp) { ev->buffer.length = 0; disp->shutdown_out = 1; request_log(disp, resp, LVL(10), - "cancel: failsafe event %p -> task %p", + "cancel: failsafe event %p -> task %p", ev, resp->task); resp->item_out = ISC_TRUE; isc_task_send(resp->task, (isc_event_t **)&ev); @@ -2145,6 +2343,12 @@ void dns_dispatch_changeattributes(dns_dispatch_t *disp, unsigned int attributes, unsigned int mask) { + REQUIRE(VALID_DISPATCH(disp)); + + /* XXXMLG + * Should check for valid attributes here! + */ + LOCK(&disp->lock); disp->attributes &= ~mask; disp->attributes |= (attributes & mask); |