dispatch.c revision 4423c99613db1399dbb5c51e86ef0d351a1418c2
2f99b54e8ec8e908ea894bc808d1c18a5f51a850Automatic Updater * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * Copyright (C) 1999-2003 Internet Software Consortium.
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * Permission to use, copy, modify, and distribute this software for any
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson * purpose with or without fee is hereby granted, provided that the above
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson * copyright notice and this permission notice appear in all copies.
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * PERFORMANCE OF THIS SOFTWARE.
28ad0be64ee756013c0f6a474fc447ee613ee0d1Evan Hunt/* $Id: dispatch.c,v 1.121 2005/02/23 01:06:37 marka Exp $ */
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencetypedef ISC_LIST(dns_dispentry_t) dns_displist_t;
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrencetypedef struct dns_qid {
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson unsigned int qid_nbuckets; /* hash table size */
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence unsigned int qid_increment; /* id increment on collision */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_lfsr_t qid_lfsr1; /* state generator info */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_lfsr_t qid_lfsr2; /* state generator info */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson dns_displist_t *qid_table; /* the table itself */
18d0b5e54be891a1aa938c165b6d439859121ec8Mark Andrews /* Unlocked. */
18d0b5e54be891a1aa938c165b6d439859121ec8Mark Andrews unsigned int magic;
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Locked by "lock". */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt unsigned int state;
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson /* locked by buffer lock */
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence unsigned int buffersize; /* size of each buffer */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson unsigned int maxbuffers; /* max buffers */
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence /* Locked internally. */
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence isc_mempool_t *epool; /* memory pool for events */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_mempool_t *rpool; /* memory pool for replies */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_mempool_t *dpool; /* dispatch allocations */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_mempool_t *bpool; /* memory pool for buffers */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson isc_entropy_t *entropy; /* entropy source */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson#define MGR_IS_SHUTTINGDOWN(l) (((l)->state & MGR_SHUTTINGDOWN) != 0)
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0)
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt unsigned int bucket;
1aba9fe67899522364a9dbc3ee5a14da081f0314Evan Hunt /* Unlocked. */
1aba9fe67899522364a9dbc3ee5a14da081f0314Evan Hunt isc_socket_t *socket; /* isc socket attached to */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson /* Locked by mgr->lock. */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt /* Locked by "lock". */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson unsigned int refcount; /* number of users */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson dns_dispatchevent_t *failsafe_ev; /* failsafe cancel event */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson recv_pending : 1; /* is a recv() pending? */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson unsigned int requests; /* how many requests we have */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson unsigned int tcpbuffers; /* allocated buffers */
7693d4de8fca501dfe6989a7f30d8d3c86fe096aAndreas Gustafsson dns_tcpmsg_t tcpmsg; /* for tcp streams */
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC)
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p')
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews#define VALID_RESPONSE(e) ISC_MAGIC_VALID((e), RESPONSE_MAGIC)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews#define DISPATCH_MAGIC ISC_MAGIC('D', 'i', 's', 'p')
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews#define VALID_DISPATCH(e) ISC_MAGIC_VALID((e), DISPATCH_MAGIC)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews#define DNS_DISPATCHMGR_MAGIC ISC_MAGIC('D', 'M', 'g', 'r')
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews#define VALID_DISPATCHMGR(e) ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC)
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic dns_dispentry_t *bucket_search(dns_qid_t *, isc_sockaddr_t *,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt dns_messageid_t, unsigned int);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic isc_boolean_t destroy_disp_ok(dns_dispatch_t *);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic void destroy_disp(isc_task_t *task, isc_event_t *event);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic void *allocate_udp_buffer(dns_dispatch_t *disp);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewsstatic inline void free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic dns_dispentry_t *linear_first(dns_qid_t *disp);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic dns_dispentry_t *linear_next(dns_qid_t *disp,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt unsigned int maxrequests,
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt unsigned int attributes,
2f012d936b5ccdf6520c96a4de23721dc58a2221Automatic Updaterstatic isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr);
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntstatic isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafssonstatic void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp);
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafssonmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...)
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) {
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Huntdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...)
fc7043d7d1294478c9988c10af9a7fb8fd810338Evan Huntdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) {
1fa2ce7eaef0c17d554495220565b681639b2ce5Mark Andrews DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewsrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
8bb77cd31b7518fb5d2a6a9d75e16e4abd59df61Andreas Gustafssonrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf));
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafsson isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews result = isc_entropy_getdata(mgr->entropy, &val, sizeof(val),
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews lfsr->count = (random() & 0x1f) + 32; /* From 32 to 63 states */
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Return an unpredictable message ID.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews id = isc_lfsr_generate32(&qid->qid_lfsr1, &qid->qid_lfsr2);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Return a hash of the destination and message id.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewsdns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews unsigned int ret;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Find the first entry in 'qid'. Returns NULL if there are no entries.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews unsigned int bucket;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Find the next entry after 'resp' in 'qid'. Return NULL if there are
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * no more entries.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewslinear_next(dns_qid_t *qid, dns_dispentry_t *resp) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews unsigned int bucket;
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * The dispatch must be locked.
febaa091847ab004f40500cc475a819f2c73fcddAndreas Gustafsson * Called when refcount reaches 0 (and safe to destroy).
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * The dispatcher must not be locked.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * The manager must be locked.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewsdestroy_disp(isc_task_t *task, isc_event_t *event) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews "shutting down; detaching from sock %p, task %p",
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Find an entry for query ID 'id' and socket address 'dest' in 'qid'.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Return NULL if no such entry exists.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrewsbucket_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews unsigned int bucket)
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews if ((res->id == id) && isc_sockaddr_equal(dest, &res->host))
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafssonfree_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) {
6017f424ee3c02d7f22132c77576ea38542fa949Andreas Gustafssonallocate_udp_buffer(dns_dispatch_t *disp) {
aa23a35d81a9618a40c4a9b44be48009553e4777Andreas Gustafsson temp = isc_mempool_get(disp->mgr->bpool);
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrewsstatic inline void
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrewsfree_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) {
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * General flow:
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * If I/O result == CANCELED or error, free the buffer.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * If query, free the buffer, restart.
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * If response:
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Allocate event, fill in details.
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson * If cannot allocate, free buffer, restart.
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * find target. If not found, free buffer, restart.
262c39b2366bf79062f7f86b218947523dd1cbacEvan Hunt * if event queue is not empty, queue. else, send.
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafssonudp_recv(isc_task_t *task, isc_event_t *ev_in) {
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt unsigned int bucket;
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews "got packet: requests %d, buffers %d, recvs %d",
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews disp->requests, disp->mgr->buffers, disp->recv_pending);
604419a812b491cd35fb6fad129c3c39da7200a1Mark Andrews * Unless the receive event was imported from a listening
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrews * interface, in which case the event type is
69f3cb5abcb38f105c653c7b3df7cec33b87b292Mark Andrews * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending.
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson * This dispatcher is shutting down.
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson free_buffer(disp, ev->region.base, ev->region.length);
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson free_buffer(disp, ev->region.base, ev->region.length);
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson "odd socket result in udp_recv(): %s",
c7e266b7e5675e12d1ca3cc929f24b3e86d41f8eEvan Hunt * If this is from a blackholed address, drop it.
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence isc_netaddr_fromsockaddr(&netaddr, &ev->address);
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson dns_acl_match(&netaddr, NULL, disp->mgr->blackhole,
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson if (isc_log_wouldlog(dns_lctx, LVL(10))) {
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson "blackholed packet from %s",
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson free_buffer(disp, ev->region.base, ev->region.length);
1a69a1a78cfaa86f3b68bbc965232b7876d4da2aDavid Lawrence * Peek into the buffer to see what we can see.
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson isc_buffer_init(&source, ev->region.base, ev->region.length);
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson dres = dns_message_peekheader(&source, &id, &flags);
6eccf5bd07eb9abf65cc08fec4a8fc97b62c0e1bBrian Wellington free_buffer(disp, ev->region.base, ev->region.length);
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson dispatch_log(disp, LVL(10), "got garbage packet");
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson "got valid DNS message header, /QR %c, id %u",
a1747570262ed336c213aaf6bd31bc91993a46deAndreas Gustafsson ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id);
goto restart;
goto unlock;
goto unlock;
if (queue_response) {
unsigned int flags;
unsigned int bucket;
int level;
case ISC_R_CANCELED:
case ISC_R_EOF:
case ISC_R_CONNECTIONRESET:
goto logit;
if (killit)
goto restart;
goto restart;
goto unlock;
goto unlock;
if (queue_response) {
case isc_sockettype_udp:
case isc_sockettype_tcp:
INSIST(0);
static isc_boolean_t
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
static isc_result_t
return (result);
#ifndef ISC_ALLOW_MAPPED
return (result);
return (ISC_R_SUCCESS);
return (ISC_R_NOMEMORY);
goto deallocate;
goto kill_lock;
goto kill_buffer_lock;
goto kill_pool_lock;
goto kill_epool;
goto kill_rpool;
return (ISC_R_SUCCESS);
return (result);
static isc_result_t
return (ISC_R_SUCCESS);
return (ISC_R_NOMEMORY);
goto cleanup;
return (ISC_R_SUCCESS);
return (ISC_R_NOMEMORY);
if (killit)
static isc_boolean_t
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
static isc_boolean_t
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
static isc_result_t
goto out;
out:
return (result);
static isc_result_t
return (ISC_R_NOMEMORY);
return (ISC_R_NOMEMORY);
return (ISC_R_UNEXPECTED);
for (i = 0; i < buckets; i++)
return (ISC_R_SUCCESS);
static isc_result_t
return (ISC_R_NOMEMORY);
goto deallocate;
goto kill_lock;
return (ISC_R_SUCCESS);
return (res);
return (result);
goto deallocate_dispatch;
goto kill_socket;
sizeof(isc_event_t));
goto kill_task;
return (ISC_R_SUCCESS);
return (result);
unsigned int buffersize,
return (result);
return (ISC_R_SUCCESS);
return (result);
return (ISC_R_SUCCESS);
static isc_result_t
unsigned int maxrequests,
unsigned int attributes,
return (result);
goto deallocate_dispatch;
goto getsocket;
goto kill_socket;
sizeof(isc_event_t));
goto kill_task;
return (ISC_R_SUCCESS);
return (result);
if (killit)
unsigned int bucket;
return (ISC_R_SHUTTINGDOWN);
return (ISC_R_QUOTA);
if (!ok) {
return (ISC_R_NOMORE);
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
unsigned int bucket;
if (killit)
goto unlock;
return (ISC_R_SUCCESS);
return (ISC_R_NOTIMPLEMENTED);
void *buf;