dispatch.c revision 180477
1135446Strhodes/* 2170222Sdougb * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18180477Sdougb/* $Id: dispatch.c,v 1.116.18.19.12.1 2008/05/22 21:28:06 each Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <stdlib.h> 25171577Sdougb#include <sys/types.h> 26171577Sdougb#include <unistd.h> 27135446Strhodes 28135446Strhodes#include <isc/entropy.h> 29135446Strhodes#include <isc/mem.h> 30135446Strhodes#include <isc/mutex.h> 31135446Strhodes#include <isc/print.h> 32180477Sdougb#include <isc/random.h> 33135446Strhodes#include <isc/string.h> 34135446Strhodes#include <isc/task.h> 35171577Sdougb#include <isc/time.h> 36135446Strhodes#include <isc/util.h> 37135446Strhodes 38135446Strhodes#include <dns/acl.h> 39135446Strhodes#include <dns/dispatch.h> 40135446Strhodes#include <dns/events.h> 41135446Strhodes#include <dns/log.h> 42135446Strhodes#include <dns/message.h> 43135446Strhodes#include <dns/portlist.h> 44135446Strhodes#include <dns/tcpmsg.h> 45135446Strhodes#include <dns/types.h> 46135446Strhodes 47135446Strhodestypedef ISC_LIST(dns_dispentry_t) dns_displist_t; 48135446Strhodes 49135446Strhodestypedef struct dns_qid { 50135446Strhodes unsigned int magic; 51170222Sdougb unsigned int qid_nbuckets; /*%< hash table size */ 52170222Sdougb unsigned int qid_increment; /*%< id increment on collision */ 53135446Strhodes isc_mutex_t lock; 54170222Sdougb dns_displist_t *qid_table; /*%< the table itself */ 55135446Strhodes} dns_qid_t; 56135446Strhodes 57180477Sdougb/* ARC4 Random generator state */ 58180477Sdougbtypedef struct arc4ctx { 59180477Sdougb isc_uint8_t i; 60180477Sdougb isc_uint8_t j; 61180477Sdougb isc_uint8_t s[256]; 62180477Sdougb int count; 63180477Sdougb} arc4ctx_t; 64180477Sdougb 65135446Strhodesstruct dns_dispatchmgr { 66135446Strhodes /* Unlocked. */ 67135446Strhodes unsigned int magic; 68135446Strhodes isc_mem_t *mctx; 69135446Strhodes dns_acl_t *blackhole; 70135446Strhodes dns_portlist_t *portlist; 71135446Strhodes 72135446Strhodes /* Locked by "lock". */ 73135446Strhodes isc_mutex_t lock; 74135446Strhodes unsigned int state; 75135446Strhodes ISC_LIST(dns_dispatch_t) list; 76135446Strhodes 77180477Sdougb /* Locked by arc4_lock. */ 78180477Sdougb isc_mutex_t arc4_lock; 79180477Sdougb arc4ctx_t arc4ctx; /*%< ARC4 context for QID */ 80180477Sdougb 81135446Strhodes /* locked by buffer lock */ 82135446Strhodes dns_qid_t *qid; 83135446Strhodes isc_mutex_t buffer_lock; 84170222Sdougb unsigned int buffers; /*%< allocated buffers */ 85170222Sdougb unsigned int buffersize; /*%< size of each buffer */ 86170222Sdougb unsigned int maxbuffers; /*%< max buffers */ 87135446Strhodes 88135446Strhodes /* Locked internally. */ 89135446Strhodes isc_mutex_t pool_lock; 90170222Sdougb isc_mempool_t *epool; /*%< memory pool for events */ 91170222Sdougb isc_mempool_t *rpool; /*%< memory pool for replies */ 92170222Sdougb isc_mempool_t *dpool; /*%< dispatch allocations */ 93170222Sdougb isc_mempool_t *bpool; /*%< memory pool for buffers */ 94135446Strhodes 95170222Sdougb isc_entropy_t *entropy; /*%< entropy source */ 96135446Strhodes}; 97135446Strhodes 98135446Strhodes#define MGR_SHUTTINGDOWN 0x00000001U 99135446Strhodes#define MGR_IS_SHUTTINGDOWN(l) (((l)->state & MGR_SHUTTINGDOWN) != 0) 100135446Strhodes 101135446Strhodes#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0) 102135446Strhodes 103135446Strhodesstruct dns_dispentry { 104135446Strhodes unsigned int magic; 105135446Strhodes dns_dispatch_t *disp; 106135446Strhodes dns_messageid_t id; 107180477Sdougb in_port_t port; 108135446Strhodes unsigned int bucket; 109135446Strhodes isc_sockaddr_t host; 110135446Strhodes isc_task_t *task; 111135446Strhodes isc_taskaction_t action; 112135446Strhodes void *arg; 113135446Strhodes isc_boolean_t item_out; 114135446Strhodes ISC_LIST(dns_dispatchevent_t) items; 115135446Strhodes ISC_LINK(dns_dispentry_t) link; 116135446Strhodes}; 117135446Strhodes 118135446Strhodes#define INVALID_BUCKET (0xffffdead) 119135446Strhodes 120135446Strhodesstruct dns_dispatch { 121135446Strhodes /* Unlocked. */ 122170222Sdougb unsigned int magic; /*%< magic */ 123170222Sdougb dns_dispatchmgr_t *mgr; /*%< dispatch manager */ 124170222Sdougb isc_task_t *task; /*%< internal task */ 125170222Sdougb isc_socket_t *socket; /*%< isc socket attached to */ 126170222Sdougb isc_sockaddr_t local; /*%< local address */ 127180477Sdougb in_port_t localport; /*%< local UDP port */ 128170222Sdougb unsigned int maxrequests; /*%< max requests */ 129135446Strhodes isc_event_t *ctlevent; 130135446Strhodes 131170222Sdougb /*% Locked by mgr->lock. */ 132135446Strhodes ISC_LINK(dns_dispatch_t) link; 133135446Strhodes 134135446Strhodes /* Locked by "lock". */ 135170222Sdougb isc_mutex_t lock; /*%< locks all below */ 136135446Strhodes isc_sockettype_t socktype; 137135446Strhodes unsigned int attributes; 138170222Sdougb unsigned int refcount; /*%< number of users */ 139170222Sdougb dns_dispatchevent_t *failsafe_ev; /*%< failsafe cancel event */ 140135446Strhodes unsigned int shutting_down : 1, 141135446Strhodes shutdown_out : 1, 142135446Strhodes connected : 1, 143135446Strhodes tcpmsg_valid : 1, 144170222Sdougb recv_pending : 1; /*%< is a recv() pending? */ 145135446Strhodes isc_result_t shutdown_why; 146170222Sdougb unsigned int requests; /*%< how many requests we have */ 147170222Sdougb unsigned int tcpbuffers; /*%< allocated buffers */ 148170222Sdougb dns_tcpmsg_t tcpmsg; /*%< for tcp streams */ 149135446Strhodes dns_qid_t *qid; 150135446Strhodes}; 151135446Strhodes 152135446Strhodes#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ') 153135446Strhodes#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC) 154135446Strhodes 155135446Strhodes#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p') 156135446Strhodes#define VALID_RESPONSE(e) ISC_MAGIC_VALID((e), RESPONSE_MAGIC) 157135446Strhodes 158135446Strhodes#define DISPATCH_MAGIC ISC_MAGIC('D', 'i', 's', 'p') 159135446Strhodes#define VALID_DISPATCH(e) ISC_MAGIC_VALID((e), DISPATCH_MAGIC) 160135446Strhodes 161135446Strhodes#define DNS_DISPATCHMGR_MAGIC ISC_MAGIC('D', 'M', 'g', 'r') 162135446Strhodes#define VALID_DISPATCHMGR(e) ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC) 163135446Strhodes 164135446Strhodes#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \ 165135446Strhodes (disp)->qid : (disp)->mgr->qid 166135446Strhodes/* 167135446Strhodes * Statics. 168135446Strhodes */ 169135446Strhodesstatic dns_dispentry_t *bucket_search(dns_qid_t *, isc_sockaddr_t *, 170180477Sdougb dns_messageid_t, in_port_t, unsigned int); 171135446Strhodesstatic isc_boolean_t destroy_disp_ok(dns_dispatch_t *); 172135446Strhodesstatic void destroy_disp(isc_task_t *task, isc_event_t *event); 173135446Strhodesstatic void udp_recv(isc_task_t *, isc_event_t *); 174135446Strhodesstatic void tcp_recv(isc_task_t *, isc_event_t *); 175135446Strhodesstatic void startrecv(dns_dispatch_t *); 176180477Sdougbstatic isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t, 177180477Sdougb in_port_t); 178135446Strhodesstatic void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len); 179135446Strhodesstatic void *allocate_udp_buffer(dns_dispatch_t *disp); 180135446Strhodesstatic inline void free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev); 181135446Strhodesstatic inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp); 182135446Strhodesstatic void do_cancel(dns_dispatch_t *disp); 183135446Strhodesstatic dns_dispentry_t *linear_first(dns_qid_t *disp); 184135446Strhodesstatic dns_dispentry_t *linear_next(dns_qid_t *disp, 185135446Strhodes dns_dispentry_t *resp); 186135446Strhodesstatic void dispatch_free(dns_dispatch_t **dispp); 187135446Strhodesstatic isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, 188135446Strhodes isc_socketmgr_t *sockmgr, 189135446Strhodes isc_taskmgr_t *taskmgr, 190135446Strhodes isc_sockaddr_t *localaddr, 191135446Strhodes unsigned int maxrequests, 192135446Strhodes unsigned int attributes, 193135446Strhodes dns_dispatch_t **dispp); 194135446Strhodesstatic isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr); 195135446Strhodesstatic void destroy_mgr(dns_dispatchmgr_t **mgrp); 196135446Strhodesstatic isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, 197180477Sdougb unsigned int increment, dns_qid_t **qidp); 198135446Strhodesstatic void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp); 199135446Strhodes 200135446Strhodes#define LVL(x) ISC_LOG_DEBUG(x) 201135446Strhodes 202135446Strhodesstatic void 203135446Strhodesmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) 204135446Strhodes ISC_FORMAT_PRINTF(3, 4); 205135446Strhodes 206135446Strhodesstatic void 207135446Strhodesmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) { 208135446Strhodes char msgbuf[2048]; 209135446Strhodes va_list ap; 210135446Strhodes 211135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 212135446Strhodes return; 213135446Strhodes 214135446Strhodes va_start(ap, fmt); 215135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 216135446Strhodes va_end(ap); 217135446Strhodes 218135446Strhodes isc_log_write(dns_lctx, 219135446Strhodes DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, 220135446Strhodes level, "dispatchmgr %p: %s", mgr, msgbuf); 221135446Strhodes} 222135446Strhodes 223135446Strhodesstatic void 224135446Strhodesdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) 225135446Strhodes ISC_FORMAT_PRINTF(3, 4); 226135446Strhodes 227135446Strhodesstatic void 228135446Strhodesdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) { 229135446Strhodes char msgbuf[2048]; 230135446Strhodes va_list ap; 231135446Strhodes 232135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 233135446Strhodes return; 234135446Strhodes 235135446Strhodes va_start(ap, fmt); 236135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 237135446Strhodes va_end(ap); 238135446Strhodes 239135446Strhodes isc_log_write(dns_lctx, 240135446Strhodes DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, 241135446Strhodes level, "dispatch %p: %s", disp, msgbuf); 242135446Strhodes} 243135446Strhodes 244135446Strhodesstatic void 245135446Strhodesrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, 246135446Strhodes int level, const char *fmt, ...) 247135446Strhodes ISC_FORMAT_PRINTF(4, 5); 248135446Strhodes 249135446Strhodesstatic void 250135446Strhodesrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, 251135446Strhodes int level, const char *fmt, ...) 252135446Strhodes{ 253135446Strhodes char msgbuf[2048]; 254135446Strhodes char peerbuf[256]; 255135446Strhodes va_list ap; 256135446Strhodes 257135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 258135446Strhodes return; 259135446Strhodes 260135446Strhodes va_start(ap, fmt); 261135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 262135446Strhodes va_end(ap); 263135446Strhodes 264135446Strhodes if (VALID_RESPONSE(resp)) { 265135446Strhodes isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf)); 266135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, 267135446Strhodes DNS_LOGMODULE_DISPATCH, level, 268135446Strhodes "dispatch %p response %p %s: %s", disp, resp, 269135446Strhodes peerbuf, msgbuf); 270135446Strhodes } else { 271135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, 272135446Strhodes DNS_LOGMODULE_DISPATCH, level, 273135446Strhodes "dispatch %p req/resp %p: %s", disp, resp, 274135446Strhodes msgbuf); 275135446Strhodes } 276135446Strhodes} 277135446Strhodes 278135446Strhodes/* 279180477Sdougb * ARC4 random number generator obtained from OpenBSD 280135446Strhodes */ 281180477Sdougbstatic void 282180477Sdougbdispatch_arc4init(arc4ctx_t *actx) { 283180477Sdougb int n; 284180477Sdougb for (n = 0; n < 256; n++) 285180477Sdougb actx->s[n] = n; 286180477Sdougb actx->i = 0; 287180477Sdougb actx->j = 0; 288180477Sdougb actx->count = 0; 289180477Sdougb} 290135446Strhodes 291180477Sdougbstatic void 292180477Sdougbdispatch_arc4addrandom(arc4ctx_t *actx, unsigned char *dat, int datlen) { 293180477Sdougb int n; 294180477Sdougb isc_uint8_t si; 295135446Strhodes 296180477Sdougb actx->i--; 297180477Sdougb for (n = 0; n < 256; n++) { 298180477Sdougb actx->i = (actx->i + 1); 299180477Sdougb si = actx->s[actx->i]; 300180477Sdougb actx->j = (actx->j + si + dat[n % datlen]); 301180477Sdougb actx->s[actx->i] = actx->s[actx->j]; 302180477Sdougb actx->s[actx->j] = si; 303180477Sdougb } 304180477Sdougb actx->j = actx->i; 305135446Strhodes} 306135446Strhodes 307180477Sdougbstatic inline isc_uint8_t 308180477Sdougbdispatch_arc4get8(arc4ctx_t *actx) { 309180477Sdougb isc_uint8_t si, sj; 310180477Sdougb 311180477Sdougb actx->i = (actx->i + 1); 312180477Sdougb si = actx->s[actx->i]; 313180477Sdougb actx->j = (actx->j + si); 314180477Sdougb sj = actx->s[actx->j]; 315180477Sdougb actx->s[actx->i] = sj; 316180477Sdougb actx->s[actx->j] = si; 317180477Sdougb 318180477Sdougb return (actx->s[(si + sj) & 0xff]); 319180477Sdougb} 320180477Sdougb 321180477Sdougbstatic inline isc_uint16_t 322180477Sdougbdispatch_arc4get16(arc4ctx_t *actx) { 323180477Sdougb isc_uint16_t val; 324180477Sdougb 325180477Sdougb val = dispatch_arc4get8(actx) << 8; 326180477Sdougb val |= dispatch_arc4get8(actx); 327180477Sdougb 328180477Sdougb return (val); 329180477Sdougb} 330180477Sdougb 331180477Sdougbstatic void 332180477Sdougbdispatch_arc4stir(dns_dispatchmgr_t *mgr) { 333180477Sdougb int i; 334180477Sdougb union { 335180477Sdougb unsigned char rnd[128]; 336180477Sdougb isc_uint32_t rnd32[32]; 337180477Sdougb } rnd; 338180477Sdougb isc_result_t result; 339180477Sdougb 340180477Sdougb if (mgr->entropy != NULL) { 341180477Sdougb /* 342180477Sdougb * We accept any quality of random data to avoid blocking. 343180477Sdougb */ 344180477Sdougb result = isc_entropy_getdata(mgr->entropy, rnd.rnd, 345180477Sdougb sizeof(rnd), NULL, 0); 346180477Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 347180477Sdougb } else { 348180477Sdougb for (i = 0; i < 32; i++) 349180477Sdougb isc_random_get(&rnd.rnd32[i]); 350180477Sdougb } 351180477Sdougb dispatch_arc4addrandom(&mgr->arc4ctx, rnd.rnd, sizeof(rnd.rnd)); 352180477Sdougb 353180477Sdougb /* 354180477Sdougb * Discard early keystream, as per recommendations in: 355180477Sdougb * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps 356180477Sdougb */ 357180477Sdougb for (i = 0; i < 256; i++) 358180477Sdougb (void)dispatch_arc4get8(&mgr->arc4ctx); 359180477Sdougb 360180477Sdougb /* 361180477Sdougb * Derived from OpenBSD's implementation. The rationale is not clear, 362180477Sdougb * but should be conservative enough in safety, and reasonably large 363180477Sdougb * for efficiency. 364180477Sdougb */ 365180477Sdougb mgr->arc4ctx.count = 1600000; 366180477Sdougb} 367180477Sdougb 368180477Sdougbstatic isc_uint16_t 369180477Sdougbdispatch_arc4random(dns_dispatchmgr_t *mgr) { 370180477Sdougb isc_uint16_t result; 371180477Sdougb 372180477Sdougb LOCK(&mgr->arc4_lock); 373180477Sdougb mgr->arc4ctx.count -= sizeof(isc_uint16_t); 374180477Sdougb if (mgr->arc4ctx.count <= 0) 375180477Sdougb dispatch_arc4stir(mgr); 376180477Sdougb result = dispatch_arc4get16(&mgr->arc4ctx); 377180477Sdougb UNLOCK(&mgr->arc4_lock); 378180477Sdougb return (result); 379180477Sdougb} 380180477Sdougb 381180477Sdougbstatic isc_uint16_t 382180477Sdougbdispatch_arc4uniformrandom(dns_dispatchmgr_t *mgr, isc_uint16_t upper_bound) { 383180477Sdougb isc_uint16_t min, r; 384180477Sdougb /* The caller must hold the manager lock. */ 385180477Sdougb 386180477Sdougb if (upper_bound < 2) 387180477Sdougb return (0); 388180477Sdougb 389180477Sdougb /* 390180477Sdougb * Ensure the range of random numbers [min, 0xffff] be a multiple of 391180477Sdougb * upper_bound and contain at least a half of the 16 bit range. 392180477Sdougb */ 393180477Sdougb 394180477Sdougb if (upper_bound > 0x8000) 395180477Sdougb min = 1 + ~upper_bound; /* 0x8000 - upper_bound */ 396180477Sdougb else 397180477Sdougb min = (isc_uint16_t)(0x10000 % (isc_uint32_t)upper_bound); 398180477Sdougb 399180477Sdougb /* 400180477Sdougb * This could theoretically loop forever but each retry has 401180477Sdougb * p > 0.5 (worst case, usually far better) of selecting a 402180477Sdougb * number inside the range we need, so it should rarely need 403180477Sdougb * to re-roll. 404180477Sdougb */ 405180477Sdougb for (;;) { 406180477Sdougb r = dispatch_arc4random(mgr); 407180477Sdougb if (r >= min) 408180477Sdougb break; 409180477Sdougb } 410180477Sdougb 411180477Sdougb return (r % upper_bound); 412180477Sdougb} 413180477Sdougb 414135446Strhodes/* 415135446Strhodes * Return a hash of the destination and message id. 416135446Strhodes */ 417135446Strhodesstatic isc_uint32_t 418180477Sdougbdns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, 419180477Sdougb in_port_t port) 420180477Sdougb{ 421135446Strhodes unsigned int ret; 422135446Strhodes 423135446Strhodes ret = isc_sockaddr_hash(dest, ISC_TRUE); 424180477Sdougb ret ^= (id << 16) | port; 425135446Strhodes ret %= qid->qid_nbuckets; 426135446Strhodes 427135446Strhodes INSIST(ret < qid->qid_nbuckets); 428135446Strhodes 429135446Strhodes return (ret); 430135446Strhodes} 431135446Strhodes 432135446Strhodes/* 433135446Strhodes * Find the first entry in 'qid'. Returns NULL if there are no entries. 434135446Strhodes */ 435135446Strhodesstatic dns_dispentry_t * 436135446Strhodeslinear_first(dns_qid_t *qid) { 437135446Strhodes dns_dispentry_t *ret; 438135446Strhodes unsigned int bucket; 439135446Strhodes 440135446Strhodes bucket = 0; 441135446Strhodes 442135446Strhodes while (bucket < qid->qid_nbuckets) { 443135446Strhodes ret = ISC_LIST_HEAD(qid->qid_table[bucket]); 444135446Strhodes if (ret != NULL) 445135446Strhodes return (ret); 446135446Strhodes bucket++; 447135446Strhodes } 448135446Strhodes 449135446Strhodes return (NULL); 450135446Strhodes} 451135446Strhodes 452135446Strhodes/* 453135446Strhodes * Find the next entry after 'resp' in 'qid'. Return NULL if there are 454135446Strhodes * no more entries. 455135446Strhodes */ 456135446Strhodesstatic dns_dispentry_t * 457135446Strhodeslinear_next(dns_qid_t *qid, dns_dispentry_t *resp) { 458135446Strhodes dns_dispentry_t *ret; 459135446Strhodes unsigned int bucket; 460135446Strhodes 461135446Strhodes ret = ISC_LIST_NEXT(resp, link); 462135446Strhodes if (ret != NULL) 463135446Strhodes return (ret); 464135446Strhodes 465135446Strhodes bucket = resp->bucket; 466135446Strhodes bucket++; 467135446Strhodes while (bucket < qid->qid_nbuckets) { 468135446Strhodes ret = ISC_LIST_HEAD(qid->qid_table[bucket]); 469135446Strhodes if (ret != NULL) 470135446Strhodes return (ret); 471135446Strhodes bucket++; 472135446Strhodes } 473135446Strhodes 474135446Strhodes return (NULL); 475135446Strhodes} 476135446Strhodes 477135446Strhodes/* 478135446Strhodes * The dispatch must be locked. 479135446Strhodes */ 480135446Strhodesstatic isc_boolean_t 481135446Strhodesdestroy_disp_ok(dns_dispatch_t *disp) 482135446Strhodes{ 483135446Strhodes if (disp->refcount != 0) 484135446Strhodes return (ISC_FALSE); 485135446Strhodes 486135446Strhodes if (disp->recv_pending != 0) 487135446Strhodes return (ISC_FALSE); 488135446Strhodes 489135446Strhodes if (disp->shutting_down == 0) 490135446Strhodes return (ISC_FALSE); 491135446Strhodes 492135446Strhodes return (ISC_TRUE); 493135446Strhodes} 494135446Strhodes 495135446Strhodes 496135446Strhodes/* 497135446Strhodes * Called when refcount reaches 0 (and safe to destroy). 498135446Strhodes * 499135446Strhodes * The dispatcher must not be locked. 500135446Strhodes * The manager must be locked. 501135446Strhodes */ 502135446Strhodesstatic void 503135446Strhodesdestroy_disp(isc_task_t *task, isc_event_t *event) { 504135446Strhodes dns_dispatch_t *disp; 505135446Strhodes dns_dispatchmgr_t *mgr; 506135446Strhodes isc_boolean_t killmgr; 507135446Strhodes 508135446Strhodes INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL); 509135446Strhodes 510135446Strhodes UNUSED(task); 511135446Strhodes 512135446Strhodes disp = event->ev_arg; 513135446Strhodes mgr = disp->mgr; 514135446Strhodes 515135446Strhodes LOCK(&mgr->lock); 516135446Strhodes ISC_LIST_UNLINK(mgr->list, disp, link); 517135446Strhodes 518135446Strhodes dispatch_log(disp, LVL(90), 519135446Strhodes "shutting down; detaching from sock %p, task %p", 520135446Strhodes disp->socket, disp->task); 521135446Strhodes 522135446Strhodes isc_socket_detach(&disp->socket); 523135446Strhodes isc_task_detach(&disp->task); 524135446Strhodes isc_event_free(&event); 525135446Strhodes 526135446Strhodes dispatch_free(&disp); 527135446Strhodes 528135446Strhodes killmgr = destroy_mgr_ok(mgr); 529135446Strhodes UNLOCK(&mgr->lock); 530135446Strhodes if (killmgr) 531135446Strhodes destroy_mgr(&mgr); 532135446Strhodes} 533135446Strhodes 534135446Strhodes 535135446Strhodes/* 536135446Strhodes * Find an entry for query ID 'id' and socket address 'dest' in 'qid'. 537135446Strhodes * Return NULL if no such entry exists. 538135446Strhodes */ 539135446Strhodesstatic dns_dispentry_t * 540135446Strhodesbucket_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, 541180477Sdougb in_port_t port, unsigned int bucket) 542135446Strhodes{ 543135446Strhodes dns_dispentry_t *res; 544135446Strhodes 545135446Strhodes REQUIRE(bucket < qid->qid_nbuckets); 546135446Strhodes 547135446Strhodes res = ISC_LIST_HEAD(qid->qid_table[bucket]); 548135446Strhodes 549135446Strhodes while (res != NULL) { 550180477Sdougb if ((res->id == id) && isc_sockaddr_equal(dest, &res->host) && 551180477Sdougb res->port == port) { 552135446Strhodes return (res); 553180477Sdougb } 554135446Strhodes res = ISC_LIST_NEXT(res, link); 555135446Strhodes } 556135446Strhodes 557135446Strhodes return (NULL); 558135446Strhodes} 559135446Strhodes 560135446Strhodesstatic void 561135446Strhodesfree_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { 562135446Strhodes INSIST(buf != NULL && len != 0); 563135446Strhodes 564135446Strhodes 565135446Strhodes switch (disp->socktype) { 566135446Strhodes case isc_sockettype_tcp: 567135446Strhodes INSIST(disp->tcpbuffers > 0); 568135446Strhodes disp->tcpbuffers--; 569135446Strhodes isc_mem_put(disp->mgr->mctx, buf, len); 570135446Strhodes break; 571135446Strhodes case isc_sockettype_udp: 572135446Strhodes LOCK(&disp->mgr->buffer_lock); 573135446Strhodes INSIST(disp->mgr->buffers > 0); 574135446Strhodes INSIST(len == disp->mgr->buffersize); 575135446Strhodes disp->mgr->buffers--; 576135446Strhodes isc_mempool_put(disp->mgr->bpool, buf); 577135446Strhodes UNLOCK(&disp->mgr->buffer_lock); 578135446Strhodes break; 579135446Strhodes default: 580135446Strhodes INSIST(0); 581135446Strhodes break; 582135446Strhodes } 583135446Strhodes} 584135446Strhodes 585135446Strhodesstatic void * 586135446Strhodesallocate_udp_buffer(dns_dispatch_t *disp) { 587135446Strhodes void *temp; 588135446Strhodes 589135446Strhodes LOCK(&disp->mgr->buffer_lock); 590135446Strhodes temp = isc_mempool_get(disp->mgr->bpool); 591135446Strhodes 592135446Strhodes if (temp != NULL) 593135446Strhodes disp->mgr->buffers++; 594135446Strhodes UNLOCK(&disp->mgr->buffer_lock); 595135446Strhodes 596135446Strhodes return (temp); 597135446Strhodes} 598135446Strhodes 599135446Strhodesstatic inline void 600135446Strhodesfree_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { 601135446Strhodes if (disp->failsafe_ev == ev) { 602135446Strhodes INSIST(disp->shutdown_out == 1); 603135446Strhodes disp->shutdown_out = 0; 604135446Strhodes 605135446Strhodes return; 606135446Strhodes } 607135446Strhodes 608135446Strhodes isc_mempool_put(disp->mgr->epool, ev); 609135446Strhodes} 610135446Strhodes 611135446Strhodesstatic inline dns_dispatchevent_t * 612135446Strhodesallocate_event(dns_dispatch_t *disp) { 613135446Strhodes dns_dispatchevent_t *ev; 614135446Strhodes 615135446Strhodes ev = isc_mempool_get(disp->mgr->epool); 616135446Strhodes if (ev == NULL) 617135446Strhodes return (NULL); 618135446Strhodes ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0, 619135446Strhodes NULL, NULL, NULL, NULL, NULL); 620135446Strhodes 621135446Strhodes return (ev); 622135446Strhodes} 623135446Strhodes 624135446Strhodes/* 625135446Strhodes * General flow: 626135446Strhodes * 627135446Strhodes * If I/O result == CANCELED or error, free the buffer. 628135446Strhodes * 629135446Strhodes * If query, free the buffer, restart. 630135446Strhodes * 631135446Strhodes * If response: 632135446Strhodes * Allocate event, fill in details. 633135446Strhodes * If cannot allocate, free buffer, restart. 634135446Strhodes * find target. If not found, free buffer, restart. 635135446Strhodes * if event queue is not empty, queue. else, send. 636135446Strhodes * restart. 637135446Strhodes */ 638135446Strhodesstatic void 639135446Strhodesudp_recv(isc_task_t *task, isc_event_t *ev_in) { 640135446Strhodes isc_socketevent_t *ev = (isc_socketevent_t *)ev_in; 641135446Strhodes dns_dispatch_t *disp = ev_in->ev_arg; 642135446Strhodes dns_messageid_t id; 643135446Strhodes isc_result_t dres; 644135446Strhodes isc_buffer_t source; 645135446Strhodes unsigned int flags; 646135446Strhodes dns_dispentry_t *resp; 647135446Strhodes dns_dispatchevent_t *rev; 648135446Strhodes unsigned int bucket; 649135446Strhodes isc_boolean_t killit; 650135446Strhodes isc_boolean_t queue_response; 651135446Strhodes dns_dispatchmgr_t *mgr; 652135446Strhodes dns_qid_t *qid; 653135446Strhodes isc_netaddr_t netaddr; 654135446Strhodes int match; 655135446Strhodes 656135446Strhodes UNUSED(task); 657135446Strhodes 658135446Strhodes LOCK(&disp->lock); 659135446Strhodes 660135446Strhodes mgr = disp->mgr; 661135446Strhodes qid = mgr->qid; 662135446Strhodes 663135446Strhodes dispatch_log(disp, LVL(90), 664135446Strhodes "got packet: requests %d, buffers %d, recvs %d", 665135446Strhodes disp->requests, disp->mgr->buffers, disp->recv_pending); 666135446Strhodes 667135446Strhodes if (ev->ev_type == ISC_SOCKEVENT_RECVDONE) { 668135446Strhodes /* 669135446Strhodes * Unless the receive event was imported from a listening 670135446Strhodes * interface, in which case the event type is 671135446Strhodes * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending. 672135446Strhodes */ 673135446Strhodes INSIST(disp->recv_pending != 0); 674135446Strhodes disp->recv_pending = 0; 675135446Strhodes } 676135446Strhodes 677135446Strhodes if (disp->shutting_down) { 678135446Strhodes /* 679135446Strhodes * This dispatcher is shutting down. 680135446Strhodes */ 681135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 682135446Strhodes 683135446Strhodes isc_event_free(&ev_in); 684135446Strhodes ev = NULL; 685135446Strhodes 686135446Strhodes killit = destroy_disp_ok(disp); 687135446Strhodes UNLOCK(&disp->lock); 688135446Strhodes if (killit) 689135446Strhodes isc_task_send(disp->task, &disp->ctlevent); 690135446Strhodes 691135446Strhodes return; 692135446Strhodes } 693135446Strhodes 694135446Strhodes if (ev->result != ISC_R_SUCCESS) { 695135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 696135446Strhodes 697135446Strhodes if (ev->result != ISC_R_CANCELED) 698135446Strhodes dispatch_log(disp, ISC_LOG_ERROR, 699135446Strhodes "odd socket result in udp_recv(): %s", 700135446Strhodes isc_result_totext(ev->result)); 701135446Strhodes 702135446Strhodes UNLOCK(&disp->lock); 703135446Strhodes isc_event_free(&ev_in); 704135446Strhodes return; 705135446Strhodes } 706135446Strhodes 707135446Strhodes /* 708135446Strhodes * If this is from a blackholed address, drop it. 709135446Strhodes */ 710135446Strhodes isc_netaddr_fromsockaddr(&netaddr, &ev->address); 711135446Strhodes if (disp->mgr->blackhole != NULL && 712135446Strhodes dns_acl_match(&netaddr, NULL, disp->mgr->blackhole, 713135446Strhodes NULL, &match, NULL) == ISC_R_SUCCESS && 714135446Strhodes match > 0) 715135446Strhodes { 716135446Strhodes if (isc_log_wouldlog(dns_lctx, LVL(10))) { 717135446Strhodes char netaddrstr[ISC_NETADDR_FORMATSIZE]; 718135446Strhodes isc_netaddr_format(&netaddr, netaddrstr, 719135446Strhodes sizeof(netaddrstr)); 720135446Strhodes dispatch_log(disp, LVL(10), 721135446Strhodes "blackholed packet from %s", 722135446Strhodes netaddrstr); 723135446Strhodes } 724135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 725135446Strhodes goto restart; 726135446Strhodes } 727135446Strhodes 728135446Strhodes /* 729135446Strhodes * Peek into the buffer to see what we can see. 730135446Strhodes */ 731135446Strhodes isc_buffer_init(&source, ev->region.base, ev->region.length); 732135446Strhodes isc_buffer_add(&source, ev->n); 733135446Strhodes dres = dns_message_peekheader(&source, &id, &flags); 734135446Strhodes if (dres != ISC_R_SUCCESS) { 735135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 736135446Strhodes dispatch_log(disp, LVL(10), "got garbage packet"); 737135446Strhodes goto restart; 738135446Strhodes } 739135446Strhodes 740135446Strhodes dispatch_log(disp, LVL(92), 741135446Strhodes "got valid DNS message header, /QR %c, id %u", 742135446Strhodes ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id); 743135446Strhodes 744135446Strhodes /* 745135446Strhodes * Look at flags. If query, drop it. If response, 746135446Strhodes * look to see where it goes. 747135446Strhodes */ 748135446Strhodes queue_response = ISC_FALSE; 749135446Strhodes if ((flags & DNS_MESSAGEFLAG_QR) == 0) { 750135446Strhodes /* query */ 751135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 752135446Strhodes goto restart; 753135446Strhodes } 754135446Strhodes 755135446Strhodes /* response */ 756180477Sdougb bucket = dns_hash(qid, &ev->address, id, disp->localport); 757135446Strhodes LOCK(&qid->lock); 758180477Sdougb resp = bucket_search(qid, &ev->address, id, disp->localport, bucket); 759135446Strhodes dispatch_log(disp, LVL(90), 760135446Strhodes "search for response in bucket %d: %s", 761135446Strhodes bucket, (resp == NULL ? "not found" : "found")); 762135446Strhodes 763135446Strhodes if (resp == NULL) { 764135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 765135446Strhodes goto unlock; 766135446Strhodes } 767165071Sdougb 768165071Sdougb /* 769165071Sdougb * Now that we have the original dispatch the query was sent 770165071Sdougb * from check that the address and port the response was 771165071Sdougb * sent to make sense. 772165071Sdougb */ 773165071Sdougb if (disp != resp->disp) { 774165071Sdougb isc_sockaddr_t a1; 775165071Sdougb isc_sockaddr_t a2; 776165071Sdougb 777165071Sdougb /* 778165071Sdougb * Check that the socket types and ports match. 779165071Sdougb */ 780165071Sdougb if (disp->socktype != resp->disp->socktype || 781165071Sdougb isc_sockaddr_getport(&disp->local) != 782165071Sdougb isc_sockaddr_getport(&resp->disp->local)) { 783165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 784165071Sdougb goto unlock; 785165071Sdougb } 786165071Sdougb 787165071Sdougb /* 788165071Sdougb * If both dispatches are bound to an address then fail as 789165071Sdougb * the addresses can't be equal (enforced by the IP stack). 790165071Sdougb * 791165071Sdougb * Note under Linux a packet can be sent out via IPv4 socket 792165071Sdougb * and the response be received via a IPv6 socket. 793165071Sdougb * 794165071Sdougb * Requests sent out via IPv6 should always come back in 795165071Sdougb * via IPv6. 796165071Sdougb */ 797165071Sdougb if (isc_sockaddr_pf(&resp->disp->local) == PF_INET6 && 798165071Sdougb isc_sockaddr_pf(&disp->local) != PF_INET6) { 799165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 800165071Sdougb goto unlock; 801165071Sdougb } 802165071Sdougb isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local)); 803165071Sdougb isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local)); 804165071Sdougb if (!isc_sockaddr_eqaddr(&a1, &resp->disp->local) && 805165071Sdougb !isc_sockaddr_eqaddr(&a2, &disp->local)) { 806165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 807165071Sdougb goto unlock; 808165071Sdougb } 809165071Sdougb } 810165071Sdougb 811135446Strhodes queue_response = resp->item_out; 812135446Strhodes rev = allocate_event(resp->disp); 813135446Strhodes if (rev == NULL) { 814135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 815135446Strhodes goto unlock; 816135446Strhodes } 817135446Strhodes 818135446Strhodes /* 819135446Strhodes * At this point, rev contains the event we want to fill in, and 820135446Strhodes * resp contains the information on the place to send it to. 821135446Strhodes * Send the event off. 822135446Strhodes */ 823135446Strhodes isc_buffer_init(&rev->buffer, ev->region.base, ev->region.length); 824135446Strhodes isc_buffer_add(&rev->buffer, ev->n); 825135446Strhodes rev->result = ISC_R_SUCCESS; 826135446Strhodes rev->id = id; 827135446Strhodes rev->addr = ev->address; 828135446Strhodes rev->pktinfo = ev->pktinfo; 829135446Strhodes rev->attributes = ev->attributes; 830135446Strhodes if (queue_response) { 831135446Strhodes ISC_LIST_APPEND(resp->items, rev, ev_link); 832135446Strhodes } else { 833135446Strhodes ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, 834135446Strhodes DNS_EVENT_DISPATCH, 835135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 836135446Strhodes request_log(disp, resp, LVL(90), 837135446Strhodes "[a] Sent event %p buffer %p len %d to task %p", 838135446Strhodes rev, rev->buffer.base, rev->buffer.length, 839135446Strhodes resp->task); 840135446Strhodes resp->item_out = ISC_TRUE; 841135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&rev)); 842135446Strhodes } 843135446Strhodes unlock: 844135446Strhodes UNLOCK(&qid->lock); 845135446Strhodes 846135446Strhodes /* 847135446Strhodes * Restart recv() to get the next packet. 848135446Strhodes */ 849135446Strhodes restart: 850135446Strhodes startrecv(disp); 851135446Strhodes 852135446Strhodes UNLOCK(&disp->lock); 853135446Strhodes 854135446Strhodes isc_event_free(&ev_in); 855135446Strhodes} 856135446Strhodes 857135446Strhodes/* 858135446Strhodes * General flow: 859135446Strhodes * 860135446Strhodes * If I/O result == CANCELED, EOF, or error, notify everyone as the 861135446Strhodes * various queues drain. 862135446Strhodes * 863135446Strhodes * If query, restart. 864135446Strhodes * 865135446Strhodes * If response: 866135446Strhodes * Allocate event, fill in details. 867135446Strhodes * If cannot allocate, restart. 868135446Strhodes * find target. If not found, restart. 869135446Strhodes * if event queue is not empty, queue. else, send. 870135446Strhodes * restart. 871135446Strhodes */ 872135446Strhodesstatic void 873135446Strhodestcp_recv(isc_task_t *task, isc_event_t *ev_in) { 874135446Strhodes dns_dispatch_t *disp = ev_in->ev_arg; 875135446Strhodes dns_tcpmsg_t *tcpmsg = &disp->tcpmsg; 876135446Strhodes dns_messageid_t id; 877135446Strhodes isc_result_t dres; 878135446Strhodes unsigned int flags; 879135446Strhodes dns_dispentry_t *resp; 880135446Strhodes dns_dispatchevent_t *rev; 881135446Strhodes unsigned int bucket; 882135446Strhodes isc_boolean_t killit; 883135446Strhodes isc_boolean_t queue_response; 884135446Strhodes dns_qid_t *qid; 885135446Strhodes int level; 886135446Strhodes char buf[ISC_SOCKADDR_FORMATSIZE]; 887135446Strhodes 888135446Strhodes UNUSED(task); 889135446Strhodes 890135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 891135446Strhodes 892135446Strhodes qid = disp->qid; 893135446Strhodes 894135446Strhodes dispatch_log(disp, LVL(90), 895135446Strhodes "got TCP packet: requests %d, buffers %d, recvs %d", 896135446Strhodes disp->requests, disp->tcpbuffers, disp->recv_pending); 897135446Strhodes 898135446Strhodes LOCK(&disp->lock); 899135446Strhodes 900135446Strhodes INSIST(disp->recv_pending != 0); 901135446Strhodes disp->recv_pending = 0; 902135446Strhodes 903135446Strhodes if (disp->refcount == 0) { 904135446Strhodes /* 905135446Strhodes * This dispatcher is shutting down. Force cancelation. 906135446Strhodes */ 907135446Strhodes tcpmsg->result = ISC_R_CANCELED; 908135446Strhodes } 909135446Strhodes 910135446Strhodes if (tcpmsg->result != ISC_R_SUCCESS) { 911135446Strhodes switch (tcpmsg->result) { 912135446Strhodes case ISC_R_CANCELED: 913135446Strhodes break; 914135446Strhodes 915135446Strhodes case ISC_R_EOF: 916135446Strhodes dispatch_log(disp, LVL(90), "shutting down on EOF"); 917135446Strhodes do_cancel(disp); 918135446Strhodes break; 919135446Strhodes 920135446Strhodes case ISC_R_CONNECTIONRESET: 921135446Strhodes level = ISC_LOG_INFO; 922135446Strhodes goto logit; 923135446Strhodes 924135446Strhodes default: 925135446Strhodes level = ISC_LOG_ERROR; 926135446Strhodes logit: 927135446Strhodes isc_sockaddr_format(&tcpmsg->address, buf, sizeof(buf)); 928135446Strhodes dispatch_log(disp, level, "shutting down due to TCP " 929135446Strhodes "receive error: %s: %s", buf, 930135446Strhodes isc_result_totext(tcpmsg->result)); 931135446Strhodes do_cancel(disp); 932135446Strhodes break; 933135446Strhodes } 934135446Strhodes 935135446Strhodes /* 936135446Strhodes * The event is statically allocated in the tcpmsg 937135446Strhodes * structure, and destroy_disp() frees the tcpmsg, so we must 938135446Strhodes * free the event *before* calling destroy_disp(). 939135446Strhodes */ 940135446Strhodes isc_event_free(&ev_in); 941135446Strhodes 942135446Strhodes disp->shutting_down = 1; 943135446Strhodes disp->shutdown_why = tcpmsg->result; 944135446Strhodes 945135446Strhodes /* 946135446Strhodes * If the recv() was canceled pass the word on. 947135446Strhodes */ 948135446Strhodes killit = destroy_disp_ok(disp); 949135446Strhodes UNLOCK(&disp->lock); 950135446Strhodes if (killit) 951135446Strhodes isc_task_send(disp->task, &disp->ctlevent); 952135446Strhodes return; 953135446Strhodes } 954135446Strhodes 955135446Strhodes dispatch_log(disp, LVL(90), "result %d, length == %d, addr = %p", 956135446Strhodes tcpmsg->result, 957135446Strhodes tcpmsg->buffer.length, tcpmsg->buffer.base); 958135446Strhodes 959135446Strhodes /* 960135446Strhodes * Peek into the buffer to see what we can see. 961135446Strhodes */ 962135446Strhodes dres = dns_message_peekheader(&tcpmsg->buffer, &id, &flags); 963135446Strhodes if (dres != ISC_R_SUCCESS) { 964135446Strhodes dispatch_log(disp, LVL(10), "got garbage packet"); 965135446Strhodes goto restart; 966135446Strhodes } 967135446Strhodes 968135446Strhodes dispatch_log(disp, LVL(92), 969135446Strhodes "got valid DNS message header, /QR %c, id %u", 970135446Strhodes ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id); 971135446Strhodes 972135446Strhodes /* 973135446Strhodes * Allocate an event to send to the query or response client, and 974135446Strhodes * allocate a new buffer for our use. 975135446Strhodes */ 976135446Strhodes 977135446Strhodes /* 978135446Strhodes * Look at flags. If query, drop it. If response, 979135446Strhodes * look to see where it goes. 980135446Strhodes */ 981135446Strhodes queue_response = ISC_FALSE; 982135446Strhodes if ((flags & DNS_MESSAGEFLAG_QR) == 0) { 983135446Strhodes /* 984135446Strhodes * Query. 985135446Strhodes */ 986135446Strhodes goto restart; 987135446Strhodes } 988135446Strhodes 989135446Strhodes /* 990135446Strhodes * Response. 991135446Strhodes */ 992180477Sdougb bucket = dns_hash(qid, &tcpmsg->address, id, disp->localport); 993135446Strhodes LOCK(&qid->lock); 994180477Sdougb resp = bucket_search(qid, &tcpmsg->address, id, disp->localport, 995180477Sdougb bucket); 996135446Strhodes dispatch_log(disp, LVL(90), 997135446Strhodes "search for response in bucket %d: %s", 998135446Strhodes bucket, (resp == NULL ? "not found" : "found")); 999135446Strhodes 1000135446Strhodes if (resp == NULL) 1001135446Strhodes goto unlock; 1002135446Strhodes queue_response = resp->item_out; 1003135446Strhodes rev = allocate_event(disp); 1004135446Strhodes if (rev == NULL) 1005135446Strhodes goto unlock; 1006135446Strhodes 1007135446Strhodes /* 1008135446Strhodes * At this point, rev contains the event we want to fill in, and 1009135446Strhodes * resp contains the information on the place to send it to. 1010135446Strhodes * Send the event off. 1011135446Strhodes */ 1012135446Strhodes dns_tcpmsg_keepbuffer(tcpmsg, &rev->buffer); 1013135446Strhodes disp->tcpbuffers++; 1014135446Strhodes rev->result = ISC_R_SUCCESS; 1015135446Strhodes rev->id = id; 1016135446Strhodes rev->addr = tcpmsg->address; 1017135446Strhodes if (queue_response) { 1018135446Strhodes ISC_LIST_APPEND(resp->items, rev, ev_link); 1019135446Strhodes } else { 1020135446Strhodes ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, DNS_EVENT_DISPATCH, 1021135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 1022135446Strhodes request_log(disp, resp, LVL(90), 1023135446Strhodes "[b] Sent event %p buffer %p len %d to task %p", 1024135446Strhodes rev, rev->buffer.base, rev->buffer.length, 1025135446Strhodes resp->task); 1026135446Strhodes resp->item_out = ISC_TRUE; 1027135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&rev)); 1028135446Strhodes } 1029135446Strhodes unlock: 1030135446Strhodes UNLOCK(&qid->lock); 1031135446Strhodes 1032135446Strhodes /* 1033135446Strhodes * Restart recv() to get the next packet. 1034135446Strhodes */ 1035135446Strhodes restart: 1036135446Strhodes startrecv(disp); 1037135446Strhodes 1038135446Strhodes UNLOCK(&disp->lock); 1039135446Strhodes 1040135446Strhodes isc_event_free(&ev_in); 1041135446Strhodes} 1042135446Strhodes 1043135446Strhodes/* 1044135446Strhodes * disp must be locked. 1045135446Strhodes */ 1046135446Strhodesstatic void 1047135446Strhodesstartrecv(dns_dispatch_t *disp) { 1048135446Strhodes isc_result_t res; 1049135446Strhodes isc_region_t region; 1050135446Strhodes 1051135446Strhodes if (disp->shutting_down == 1) 1052135446Strhodes return; 1053135446Strhodes 1054135446Strhodes if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) 1055135446Strhodes return; 1056135446Strhodes 1057135446Strhodes if (disp->recv_pending != 0) 1058135446Strhodes return; 1059135446Strhodes 1060135446Strhodes if (disp->mgr->buffers >= disp->mgr->maxbuffers) 1061135446Strhodes return; 1062135446Strhodes 1063135446Strhodes switch (disp->socktype) { 1064135446Strhodes /* 1065135446Strhodes * UDP reads are always maximal. 1066135446Strhodes */ 1067135446Strhodes case isc_sockettype_udp: 1068135446Strhodes region.length = disp->mgr->buffersize; 1069135446Strhodes region.base = allocate_udp_buffer(disp); 1070135446Strhodes if (region.base == NULL) 1071135446Strhodes return; 1072135446Strhodes res = isc_socket_recv(disp->socket, ®ion, 1, 1073135446Strhodes disp->task, udp_recv, disp); 1074135446Strhodes if (res != ISC_R_SUCCESS) { 1075135446Strhodes free_buffer(disp, region.base, region.length); 1076135446Strhodes disp->shutdown_why = res; 1077135446Strhodes disp->shutting_down = 1; 1078135446Strhodes do_cancel(disp); 1079135446Strhodes return; 1080135446Strhodes } 1081135446Strhodes INSIST(disp->recv_pending == 0); 1082135446Strhodes disp->recv_pending = 1; 1083135446Strhodes break; 1084135446Strhodes 1085135446Strhodes case isc_sockettype_tcp: 1086135446Strhodes res = dns_tcpmsg_readmessage(&disp->tcpmsg, disp->task, 1087135446Strhodes tcp_recv, disp); 1088135446Strhodes if (res != ISC_R_SUCCESS) { 1089135446Strhodes disp->shutdown_why = res; 1090135446Strhodes disp->shutting_down = 1; 1091135446Strhodes do_cancel(disp); 1092135446Strhodes return; 1093135446Strhodes } 1094135446Strhodes INSIST(disp->recv_pending == 0); 1095135446Strhodes disp->recv_pending = 1; 1096135446Strhodes break; 1097170222Sdougb default: 1098170222Sdougb INSIST(0); 1099170222Sdougb break; 1100135446Strhodes } 1101135446Strhodes} 1102135446Strhodes 1103135446Strhodes/* 1104135446Strhodes * Mgr must be locked when calling this function. 1105135446Strhodes */ 1106135446Strhodesstatic isc_boolean_t 1107135446Strhodesdestroy_mgr_ok(dns_dispatchmgr_t *mgr) { 1108135446Strhodes mgr_log(mgr, LVL(90), 1109135446Strhodes "destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, " 1110135446Strhodes "epool=%d, rpool=%d, dpool=%d", 1111135446Strhodes MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list), 1112135446Strhodes isc_mempool_getallocated(mgr->epool), 1113135446Strhodes isc_mempool_getallocated(mgr->rpool), 1114135446Strhodes isc_mempool_getallocated(mgr->dpool)); 1115135446Strhodes if (!MGR_IS_SHUTTINGDOWN(mgr)) 1116135446Strhodes return (ISC_FALSE); 1117135446Strhodes if (!ISC_LIST_EMPTY(mgr->list)) 1118135446Strhodes return (ISC_FALSE); 1119135446Strhodes if (isc_mempool_getallocated(mgr->epool) != 0) 1120135446Strhodes return (ISC_FALSE); 1121135446Strhodes if (isc_mempool_getallocated(mgr->rpool) != 0) 1122135446Strhodes return (ISC_FALSE); 1123135446Strhodes if (isc_mempool_getallocated(mgr->dpool) != 0) 1124135446Strhodes return (ISC_FALSE); 1125135446Strhodes 1126135446Strhodes return (ISC_TRUE); 1127135446Strhodes} 1128135446Strhodes 1129135446Strhodes/* 1130135446Strhodes * Mgr must be unlocked when calling this function. 1131135446Strhodes */ 1132135446Strhodesstatic void 1133135446Strhodesdestroy_mgr(dns_dispatchmgr_t **mgrp) { 1134135446Strhodes isc_mem_t *mctx; 1135135446Strhodes dns_dispatchmgr_t *mgr; 1136135446Strhodes 1137135446Strhodes mgr = *mgrp; 1138135446Strhodes *mgrp = NULL; 1139135446Strhodes 1140135446Strhodes mctx = mgr->mctx; 1141135446Strhodes 1142135446Strhodes mgr->magic = 0; 1143135446Strhodes mgr->mctx = NULL; 1144135446Strhodes DESTROYLOCK(&mgr->lock); 1145135446Strhodes mgr->state = 0; 1146135446Strhodes 1147180477Sdougb DESTROYLOCK(&mgr->arc4_lock); 1148180477Sdougb 1149135446Strhodes isc_mempool_destroy(&mgr->epool); 1150135446Strhodes isc_mempool_destroy(&mgr->rpool); 1151135446Strhodes isc_mempool_destroy(&mgr->dpool); 1152135446Strhodes isc_mempool_destroy(&mgr->bpool); 1153135446Strhodes 1154135446Strhodes DESTROYLOCK(&mgr->pool_lock); 1155135446Strhodes 1156135446Strhodes if (mgr->entropy != NULL) 1157135446Strhodes isc_entropy_detach(&mgr->entropy); 1158135446Strhodes if (mgr->qid != NULL) 1159135446Strhodes qid_destroy(mctx, &mgr->qid); 1160135446Strhodes 1161135446Strhodes DESTROYLOCK(&mgr->buffer_lock); 1162135446Strhodes 1163135446Strhodes if (mgr->blackhole != NULL) 1164135446Strhodes dns_acl_detach(&mgr->blackhole); 1165135446Strhodes 1166135446Strhodes if (mgr->portlist != NULL) 1167135446Strhodes dns_portlist_detach(&mgr->portlist); 1168135446Strhodes 1169135446Strhodes isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); 1170135446Strhodes isc_mem_detach(&mctx); 1171135446Strhodes} 1172135446Strhodes 1173135446Strhodesstatic isc_result_t 1174135446Strhodescreate_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, 1175135446Strhodes isc_socket_t **sockp) 1176135446Strhodes{ 1177135446Strhodes isc_socket_t *sock; 1178135446Strhodes isc_result_t result; 1179135446Strhodes 1180135446Strhodes sock = NULL; 1181135446Strhodes result = isc_socket_create(mgr, isc_sockaddr_pf(local), 1182135446Strhodes isc_sockettype_udp, &sock); 1183135446Strhodes if (result != ISC_R_SUCCESS) 1184135446Strhodes return (result); 1185135446Strhodes 1186135446Strhodes#ifndef ISC_ALLOW_MAPPED 1187135446Strhodes isc_socket_ipv6only(sock, ISC_TRUE); 1188135446Strhodes#endif 1189135446Strhodes result = isc_socket_bind(sock, local); 1190135446Strhodes if (result != ISC_R_SUCCESS) { 1191135446Strhodes isc_socket_detach(&sock); 1192135446Strhodes return (result); 1193135446Strhodes } 1194135446Strhodes 1195135446Strhodes *sockp = sock; 1196135446Strhodes return (ISC_R_SUCCESS); 1197135446Strhodes} 1198135446Strhodes 1199135446Strhodes/* 1200135446Strhodes * Publics. 1201135446Strhodes */ 1202135446Strhodes 1203135446Strhodesisc_result_t 1204135446Strhodesdns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, 1205135446Strhodes dns_dispatchmgr_t **mgrp) 1206135446Strhodes{ 1207135446Strhodes dns_dispatchmgr_t *mgr; 1208135446Strhodes isc_result_t result; 1209135446Strhodes 1210135446Strhodes REQUIRE(mctx != NULL); 1211135446Strhodes REQUIRE(mgrp != NULL && *mgrp == NULL); 1212135446Strhodes 1213135446Strhodes mgr = isc_mem_get(mctx, sizeof(dns_dispatchmgr_t)); 1214135446Strhodes if (mgr == NULL) 1215135446Strhodes return (ISC_R_NOMEMORY); 1216135446Strhodes 1217135446Strhodes mgr->mctx = NULL; 1218135446Strhodes isc_mem_attach(mctx, &mgr->mctx); 1219135446Strhodes 1220135446Strhodes mgr->blackhole = NULL; 1221135446Strhodes mgr->portlist = NULL; 1222135446Strhodes 1223135446Strhodes result = isc_mutex_init(&mgr->lock); 1224135446Strhodes if (result != ISC_R_SUCCESS) 1225135446Strhodes goto deallocate; 1226135446Strhodes 1227180477Sdougb result = isc_mutex_init(&mgr->arc4_lock); 1228135446Strhodes if (result != ISC_R_SUCCESS) 1229135446Strhodes goto kill_lock; 1230135446Strhodes 1231180477Sdougb result = isc_mutex_init(&mgr->buffer_lock); 1232180477Sdougb if (result != ISC_R_SUCCESS) 1233180477Sdougb goto kill_arc4_lock; 1234180477Sdougb 1235135446Strhodes result = isc_mutex_init(&mgr->pool_lock); 1236135446Strhodes if (result != ISC_R_SUCCESS) 1237135446Strhodes goto kill_buffer_lock; 1238135446Strhodes 1239135446Strhodes mgr->epool = NULL; 1240135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t), 1241135446Strhodes &mgr->epool) != ISC_R_SUCCESS) { 1242135446Strhodes result = ISC_R_NOMEMORY; 1243135446Strhodes goto kill_pool_lock; 1244135446Strhodes } 1245135446Strhodes 1246135446Strhodes mgr->rpool = NULL; 1247135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispentry_t), 1248135446Strhodes &mgr->rpool) != ISC_R_SUCCESS) { 1249135446Strhodes result = ISC_R_NOMEMORY; 1250135446Strhodes goto kill_epool; 1251135446Strhodes } 1252135446Strhodes 1253135446Strhodes mgr->dpool = NULL; 1254135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatch_t), 1255135446Strhodes &mgr->dpool) != ISC_R_SUCCESS) { 1256135446Strhodes result = ISC_R_NOMEMORY; 1257135446Strhodes goto kill_rpool; 1258135446Strhodes } 1259135446Strhodes 1260135446Strhodes isc_mempool_setname(mgr->epool, "dispmgr_epool"); 1261135446Strhodes isc_mempool_setfreemax(mgr->epool, 1024); 1262135446Strhodes isc_mempool_associatelock(mgr->epool, &mgr->pool_lock); 1263135446Strhodes 1264135446Strhodes isc_mempool_setname(mgr->rpool, "dispmgr_rpool"); 1265135446Strhodes isc_mempool_setfreemax(mgr->rpool, 1024); 1266135446Strhodes isc_mempool_associatelock(mgr->rpool, &mgr->pool_lock); 1267135446Strhodes 1268135446Strhodes isc_mempool_setname(mgr->dpool, "dispmgr_dpool"); 1269135446Strhodes isc_mempool_setfreemax(mgr->dpool, 1024); 1270135446Strhodes isc_mempool_associatelock(mgr->dpool, &mgr->pool_lock); 1271135446Strhodes 1272135446Strhodes mgr->buffers = 0; 1273135446Strhodes mgr->buffersize = 0; 1274135446Strhodes mgr->maxbuffers = 0; 1275135446Strhodes mgr->bpool = NULL; 1276135446Strhodes mgr->entropy = NULL; 1277135446Strhodes mgr->qid = NULL; 1278135446Strhodes mgr->state = 0; 1279135446Strhodes ISC_LIST_INIT(mgr->list); 1280135446Strhodes mgr->magic = DNS_DISPATCHMGR_MAGIC; 1281135446Strhodes 1282135446Strhodes if (entropy != NULL) 1283135446Strhodes isc_entropy_attach(entropy, &mgr->entropy); 1284135446Strhodes 1285180477Sdougb dispatch_arc4init(&mgr->arc4ctx); 1286180477Sdougb 1287135446Strhodes *mgrp = mgr; 1288135446Strhodes return (ISC_R_SUCCESS); 1289135446Strhodes 1290135446Strhodes kill_rpool: 1291135446Strhodes isc_mempool_destroy(&mgr->rpool); 1292135446Strhodes kill_epool: 1293135446Strhodes isc_mempool_destroy(&mgr->epool); 1294135446Strhodes kill_pool_lock: 1295135446Strhodes DESTROYLOCK(&mgr->pool_lock); 1296135446Strhodes kill_buffer_lock: 1297135446Strhodes DESTROYLOCK(&mgr->buffer_lock); 1298180477Sdougb kill_arc4_lock: 1299180477Sdougb DESTROYLOCK(&mgr->arc4_lock); 1300135446Strhodes kill_lock: 1301135446Strhodes DESTROYLOCK(&mgr->lock); 1302135446Strhodes deallocate: 1303135446Strhodes isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); 1304135446Strhodes isc_mem_detach(&mctx); 1305135446Strhodes 1306135446Strhodes return (result); 1307135446Strhodes} 1308135446Strhodes 1309135446Strhodesvoid 1310135446Strhodesdns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole) { 1311135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1312135446Strhodes if (mgr->blackhole != NULL) 1313135446Strhodes dns_acl_detach(&mgr->blackhole); 1314135446Strhodes dns_acl_attach(blackhole, &mgr->blackhole); 1315135446Strhodes} 1316135446Strhodes 1317135446Strhodesdns_acl_t * 1318135446Strhodesdns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr) { 1319135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1320135446Strhodes return (mgr->blackhole); 1321135446Strhodes} 1322135446Strhodes 1323135446Strhodesvoid 1324135446Strhodesdns_dispatchmgr_setblackportlist(dns_dispatchmgr_t *mgr, 1325135446Strhodes dns_portlist_t *portlist) 1326135446Strhodes{ 1327135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1328135446Strhodes if (mgr->portlist != NULL) 1329135446Strhodes dns_portlist_detach(&mgr->portlist); 1330135446Strhodes if (portlist != NULL) 1331135446Strhodes dns_portlist_attach(portlist, &mgr->portlist); 1332135446Strhodes} 1333135446Strhodes 1334135446Strhodesdns_portlist_t * 1335135446Strhodesdns_dispatchmgr_getblackportlist(dns_dispatchmgr_t *mgr) { 1336135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1337135446Strhodes return (mgr->portlist); 1338135446Strhodes} 1339135446Strhodes 1340135446Strhodesstatic isc_result_t 1341135446Strhodesdns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, 1342135446Strhodes unsigned int buffersize, unsigned int maxbuffers, 1343135446Strhodes unsigned int buckets, unsigned int increment) 1344135446Strhodes{ 1345135446Strhodes isc_result_t result; 1346135446Strhodes 1347135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1348135446Strhodes REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); 1349135446Strhodes REQUIRE(maxbuffers > 0); 1350135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 1351135446Strhodes REQUIRE(increment > buckets); 1352135446Strhodes 1353135446Strhodes /* 1354135446Strhodes * Keep some number of items around. This should be a config 1355135446Strhodes * option. For now, keep 8, but later keep at least two even 1356135446Strhodes * if the caller wants less. This allows us to ensure certain 1357135446Strhodes * things, like an event can be "freed" and the next allocation 1358135446Strhodes * will always succeed. 1359135446Strhodes * 1360135446Strhodes * Note that if limits are placed on anything here, we use one 1361135446Strhodes * event internally, so the actual limit should be "wanted + 1." 1362135446Strhodes * 1363135446Strhodes * XXXMLG 1364135446Strhodes */ 1365135446Strhodes 1366135446Strhodes if (maxbuffers < 8) 1367135446Strhodes maxbuffers = 8; 1368135446Strhodes 1369135446Strhodes LOCK(&mgr->buffer_lock); 1370135446Strhodes if (mgr->bpool != NULL) { 1371135446Strhodes isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); 1372135446Strhodes mgr->maxbuffers = maxbuffers; 1373135446Strhodes UNLOCK(&mgr->buffer_lock); 1374135446Strhodes return (ISC_R_SUCCESS); 1375135446Strhodes } 1376135446Strhodes 1377135446Strhodes if (isc_mempool_create(mgr->mctx, buffersize, 1378135446Strhodes &mgr->bpool) != ISC_R_SUCCESS) { 1379170222Sdougb UNLOCK(&mgr->buffer_lock); 1380135446Strhodes return (ISC_R_NOMEMORY); 1381135446Strhodes } 1382135446Strhodes 1383135446Strhodes isc_mempool_setname(mgr->bpool, "dispmgr_bpool"); 1384135446Strhodes isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); 1385135446Strhodes isc_mempool_associatelock(mgr->bpool, &mgr->pool_lock); 1386135446Strhodes 1387180477Sdougb result = qid_allocate(mgr, buckets, increment, &mgr->qid); 1388135446Strhodes if (result != ISC_R_SUCCESS) 1389135446Strhodes goto cleanup; 1390135446Strhodes 1391135446Strhodes mgr->buffersize = buffersize; 1392135446Strhodes mgr->maxbuffers = maxbuffers; 1393135446Strhodes UNLOCK(&mgr->buffer_lock); 1394135446Strhodes return (ISC_R_SUCCESS); 1395135446Strhodes 1396135446Strhodes cleanup: 1397135446Strhodes isc_mempool_destroy(&mgr->bpool); 1398135446Strhodes UNLOCK(&mgr->buffer_lock); 1399135446Strhodes return (ISC_R_NOMEMORY); 1400135446Strhodes} 1401135446Strhodes 1402135446Strhodesvoid 1403135446Strhodesdns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { 1404135446Strhodes dns_dispatchmgr_t *mgr; 1405135446Strhodes isc_boolean_t killit; 1406135446Strhodes 1407135446Strhodes REQUIRE(mgrp != NULL); 1408135446Strhodes REQUIRE(VALID_DISPATCHMGR(*mgrp)); 1409135446Strhodes 1410135446Strhodes mgr = *mgrp; 1411135446Strhodes *mgrp = NULL; 1412135446Strhodes 1413135446Strhodes LOCK(&mgr->lock); 1414135446Strhodes mgr->state |= MGR_SHUTTINGDOWN; 1415135446Strhodes 1416135446Strhodes killit = destroy_mgr_ok(mgr); 1417135446Strhodes UNLOCK(&mgr->lock); 1418135446Strhodes 1419135446Strhodes mgr_log(mgr, LVL(90), "destroy: killit=%d", killit); 1420135446Strhodes 1421135446Strhodes if (killit) 1422135446Strhodes destroy_mgr(&mgr); 1423135446Strhodes} 1424135446Strhodes 1425135446Strhodesstatic isc_boolean_t 1426180477Sdougbblacklisted(dns_dispatchmgr_t *mgr, isc_socket_t *sock, 1427180477Sdougb isc_sockaddr_t *sockaddrp) 1428180477Sdougb{ 1429135446Strhodes isc_sockaddr_t sockaddr; 1430135446Strhodes isc_result_t result; 1431135446Strhodes 1432180477Sdougb REQUIRE(sock != NULL || sockaddrp != NULL); 1433180477Sdougb 1434135446Strhodes if (mgr->portlist == NULL) 1435135446Strhodes return (ISC_FALSE); 1436135446Strhodes 1437180477Sdougb if (sock != NULL) { 1438180477Sdougb sockaddrp = &sockaddr; 1439180477Sdougb result = isc_socket_getsockname(sock, sockaddrp); 1440180477Sdougb if (result != ISC_R_SUCCESS) 1441180477Sdougb return (ISC_FALSE); 1442180477Sdougb } 1443135446Strhodes 1444135446Strhodes if (mgr->portlist != NULL && 1445180477Sdougb dns_portlist_match(mgr->portlist, isc_sockaddr_pf(sockaddrp), 1446180477Sdougb isc_sockaddr_getport(sockaddrp))) 1447135446Strhodes return (ISC_TRUE); 1448135446Strhodes return (ISC_FALSE); 1449135446Strhodes} 1450135446Strhodes 1451135446Strhodes#define ATTRMATCH(_a1, _a2, _mask) (((_a1) & (_mask)) == ((_a2) & (_mask))) 1452135446Strhodes 1453135446Strhodesstatic isc_boolean_t 1454135446Strhodeslocal_addr_match(dns_dispatch_t *disp, isc_sockaddr_t *addr) { 1455135446Strhodes isc_sockaddr_t sockaddr; 1456135446Strhodes isc_result_t result; 1457135446Strhodes 1458135446Strhodes if (addr == NULL) 1459135446Strhodes return (ISC_TRUE); 1460135446Strhodes 1461135446Strhodes /* 1462135446Strhodes * Don't match wildcard ports against newly blacklisted ports. 1463135446Strhodes */ 1464135446Strhodes if (disp->mgr->portlist != NULL && 1465135446Strhodes isc_sockaddr_getport(addr) == 0 && 1466135446Strhodes isc_sockaddr_getport(&disp->local) == 0 && 1467180477Sdougb blacklisted(disp->mgr, disp->socket, NULL)) 1468135446Strhodes return (ISC_FALSE); 1469135446Strhodes 1470135446Strhodes /* 1471135446Strhodes * Check if we match the binding <address,port>. 1472135446Strhodes * Wildcard ports match/fail here. 1473135446Strhodes */ 1474135446Strhodes if (isc_sockaddr_equal(&disp->local, addr)) 1475135446Strhodes return (ISC_TRUE); 1476135446Strhodes if (isc_sockaddr_getport(addr) == 0) 1477135446Strhodes return (ISC_FALSE); 1478135446Strhodes 1479135446Strhodes /* 1480135446Strhodes * Check if we match a bound wildcard port <address,port>. 1481135446Strhodes */ 1482135446Strhodes if (!isc_sockaddr_eqaddr(&disp->local, addr)) 1483135446Strhodes return (ISC_FALSE); 1484135446Strhodes result = isc_socket_getsockname(disp->socket, &sockaddr); 1485135446Strhodes if (result != ISC_R_SUCCESS) 1486135446Strhodes return (ISC_FALSE); 1487135446Strhodes 1488135446Strhodes return (isc_sockaddr_equal(&sockaddr, addr)); 1489135446Strhodes} 1490135446Strhodes 1491135446Strhodes/* 1492135446Strhodes * Requires mgr be locked. 1493135446Strhodes * 1494135446Strhodes * No dispatcher can be locked by this thread when calling this function. 1495135446Strhodes * 1496135446Strhodes * 1497135446Strhodes * NOTE: 1498135446Strhodes * If a matching dispatcher is found, it is locked after this function 1499135446Strhodes * returns, and must be unlocked by the caller. 1500135446Strhodes */ 1501135446Strhodesstatic isc_result_t 1502135446Strhodesdispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local, 1503135446Strhodes unsigned int attributes, unsigned int mask, 1504135446Strhodes dns_dispatch_t **dispp) 1505135446Strhodes{ 1506135446Strhodes dns_dispatch_t *disp; 1507135446Strhodes isc_result_t result; 1508135446Strhodes 1509135446Strhodes /* 1510135446Strhodes * Make certain that we will not match a private dispatch. 1511135446Strhodes */ 1512135446Strhodes attributes &= ~DNS_DISPATCHATTR_PRIVATE; 1513135446Strhodes mask |= DNS_DISPATCHATTR_PRIVATE; 1514135446Strhodes 1515135446Strhodes disp = ISC_LIST_HEAD(mgr->list); 1516135446Strhodes while (disp != NULL) { 1517135446Strhodes LOCK(&disp->lock); 1518135446Strhodes if ((disp->shutting_down == 0) 1519135446Strhodes && ATTRMATCH(disp->attributes, attributes, mask) 1520135446Strhodes && local_addr_match(disp, local)) 1521135446Strhodes break; 1522135446Strhodes UNLOCK(&disp->lock); 1523135446Strhodes disp = ISC_LIST_NEXT(disp, link); 1524135446Strhodes } 1525135446Strhodes 1526135446Strhodes if (disp == NULL) { 1527135446Strhodes result = ISC_R_NOTFOUND; 1528135446Strhodes goto out; 1529135446Strhodes } 1530135446Strhodes 1531135446Strhodes *dispp = disp; 1532135446Strhodes result = ISC_R_SUCCESS; 1533135446Strhodes out: 1534135446Strhodes 1535135446Strhodes return (result); 1536135446Strhodes} 1537135446Strhodes 1538135446Strhodesstatic isc_result_t 1539135446Strhodesqid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, 1540180477Sdougb unsigned int increment, dns_qid_t **qidp) 1541135446Strhodes{ 1542135446Strhodes dns_qid_t *qid; 1543135446Strhodes unsigned int i; 1544170222Sdougb isc_result_t result; 1545135446Strhodes 1546135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1547135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 1548135446Strhodes REQUIRE(increment > buckets); 1549135446Strhodes REQUIRE(qidp != NULL && *qidp == NULL); 1550135446Strhodes 1551135446Strhodes qid = isc_mem_get(mgr->mctx, sizeof(*qid)); 1552135446Strhodes if (qid == NULL) 1553135446Strhodes return (ISC_R_NOMEMORY); 1554135446Strhodes 1555135446Strhodes qid->qid_table = isc_mem_get(mgr->mctx, 1556135446Strhodes buckets * sizeof(dns_displist_t)); 1557135446Strhodes if (qid->qid_table == NULL) { 1558135446Strhodes isc_mem_put(mgr->mctx, qid, sizeof(*qid)); 1559135446Strhodes return (ISC_R_NOMEMORY); 1560135446Strhodes } 1561135446Strhodes 1562170222Sdougb result = isc_mutex_init(&qid->lock); 1563170222Sdougb if (result != ISC_R_SUCCESS) { 1564135446Strhodes isc_mem_put(mgr->mctx, qid->qid_table, 1565135446Strhodes buckets * sizeof(dns_displist_t)); 1566135446Strhodes isc_mem_put(mgr->mctx, qid, sizeof(*qid)); 1567170222Sdougb return (result); 1568135446Strhodes } 1569135446Strhodes 1570135446Strhodes for (i = 0; i < buckets; i++) 1571135446Strhodes ISC_LIST_INIT(qid->qid_table[i]); 1572135446Strhodes 1573135446Strhodes qid->qid_nbuckets = buckets; 1574135446Strhodes qid->qid_increment = increment; 1575135446Strhodes qid->magic = QID_MAGIC; 1576135446Strhodes *qidp = qid; 1577135446Strhodes return (ISC_R_SUCCESS); 1578135446Strhodes} 1579135446Strhodes 1580135446Strhodesstatic void 1581135446Strhodesqid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) { 1582135446Strhodes dns_qid_t *qid; 1583135446Strhodes 1584135446Strhodes REQUIRE(qidp != NULL); 1585135446Strhodes qid = *qidp; 1586135446Strhodes 1587135446Strhodes REQUIRE(VALID_QID(qid)); 1588135446Strhodes 1589135446Strhodes *qidp = NULL; 1590135446Strhodes qid->magic = 0; 1591135446Strhodes isc_mem_put(mctx, qid->qid_table, 1592135446Strhodes qid->qid_nbuckets * sizeof(dns_displist_t)); 1593135446Strhodes DESTROYLOCK(&qid->lock); 1594135446Strhodes isc_mem_put(mctx, qid, sizeof(*qid)); 1595135446Strhodes} 1596135446Strhodes 1597135446Strhodes/* 1598135446Strhodes * Allocate and set important limits. 1599135446Strhodes */ 1600135446Strhodesstatic isc_result_t 1601135446Strhodesdispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests, 1602135446Strhodes dns_dispatch_t **dispp) 1603135446Strhodes{ 1604135446Strhodes dns_dispatch_t *disp; 1605170222Sdougb isc_result_t result; 1606135446Strhodes 1607135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1608135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 1609135446Strhodes 1610135446Strhodes /* 1611135446Strhodes * Set up the dispatcher, mostly. Don't bother setting some of 1612135446Strhodes * the options that are controlled by tcp vs. udp, etc. 1613135446Strhodes */ 1614135446Strhodes 1615135446Strhodes disp = isc_mempool_get(mgr->dpool); 1616135446Strhodes if (disp == NULL) 1617135446Strhodes return (ISC_R_NOMEMORY); 1618135446Strhodes 1619135446Strhodes disp->magic = 0; 1620135446Strhodes disp->mgr = mgr; 1621135446Strhodes disp->maxrequests = maxrequests; 1622135446Strhodes disp->attributes = 0; 1623135446Strhodes ISC_LINK_INIT(disp, link); 1624135446Strhodes disp->refcount = 1; 1625135446Strhodes disp->recv_pending = 0; 1626135446Strhodes memset(&disp->local, 0, sizeof(disp->local)); 1627180477Sdougb disp->localport = 0; 1628135446Strhodes disp->shutting_down = 0; 1629135446Strhodes disp->shutdown_out = 0; 1630135446Strhodes disp->connected = 0; 1631135446Strhodes disp->tcpmsg_valid = 0; 1632135446Strhodes disp->shutdown_why = ISC_R_UNEXPECTED; 1633135446Strhodes disp->requests = 0; 1634135446Strhodes disp->tcpbuffers = 0; 1635135446Strhodes disp->qid = NULL; 1636135446Strhodes 1637170222Sdougb result = isc_mutex_init(&disp->lock); 1638170222Sdougb if (result != ISC_R_SUCCESS) 1639135446Strhodes goto deallocate; 1640135446Strhodes 1641135446Strhodes disp->failsafe_ev = allocate_event(disp); 1642135446Strhodes if (disp->failsafe_ev == NULL) { 1643170222Sdougb result = ISC_R_NOMEMORY; 1644135446Strhodes goto kill_lock; 1645135446Strhodes } 1646135446Strhodes 1647135446Strhodes disp->magic = DISPATCH_MAGIC; 1648135446Strhodes 1649135446Strhodes *dispp = disp; 1650135446Strhodes return (ISC_R_SUCCESS); 1651135446Strhodes 1652135446Strhodes /* 1653135446Strhodes * error returns 1654135446Strhodes */ 1655135446Strhodes kill_lock: 1656135446Strhodes DESTROYLOCK(&disp->lock); 1657135446Strhodes deallocate: 1658135446Strhodes isc_mempool_put(mgr->dpool, disp); 1659135446Strhodes 1660170222Sdougb return (result); 1661135446Strhodes} 1662135446Strhodes 1663135446Strhodes 1664135446Strhodes/* 1665135446Strhodes * MUST be unlocked, and not used by anthing. 1666135446Strhodes */ 1667135446Strhodesstatic void 1668135446Strhodesdispatch_free(dns_dispatch_t **dispp) 1669135446Strhodes{ 1670135446Strhodes dns_dispatch_t *disp; 1671135446Strhodes dns_dispatchmgr_t *mgr; 1672135446Strhodes 1673135446Strhodes REQUIRE(VALID_DISPATCH(*dispp)); 1674135446Strhodes disp = *dispp; 1675135446Strhodes *dispp = NULL; 1676135446Strhodes 1677135446Strhodes mgr = disp->mgr; 1678135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1679135446Strhodes 1680135446Strhodes if (disp->tcpmsg_valid) { 1681135446Strhodes dns_tcpmsg_invalidate(&disp->tcpmsg); 1682135446Strhodes disp->tcpmsg_valid = 0; 1683135446Strhodes } 1684135446Strhodes 1685135446Strhodes INSIST(disp->tcpbuffers == 0); 1686135446Strhodes INSIST(disp->requests == 0); 1687135446Strhodes INSIST(disp->recv_pending == 0); 1688135446Strhodes 1689135446Strhodes isc_mempool_put(mgr->epool, disp->failsafe_ev); 1690135446Strhodes disp->failsafe_ev = NULL; 1691135446Strhodes 1692135446Strhodes if (disp->qid != NULL) 1693135446Strhodes qid_destroy(mgr->mctx, &disp->qid); 1694135446Strhodes disp->mgr = NULL; 1695135446Strhodes DESTROYLOCK(&disp->lock); 1696135446Strhodes disp->magic = 0; 1697135446Strhodes isc_mempool_put(mgr->dpool, disp); 1698135446Strhodes} 1699135446Strhodes 1700135446Strhodesisc_result_t 1701135446Strhodesdns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, 1702135446Strhodes isc_taskmgr_t *taskmgr, unsigned int buffersize, 1703135446Strhodes unsigned int maxbuffers, unsigned int maxrequests, 1704135446Strhodes unsigned int buckets, unsigned int increment, 1705135446Strhodes unsigned int attributes, dns_dispatch_t **dispp) 1706135446Strhodes{ 1707135446Strhodes isc_result_t result; 1708135446Strhodes dns_dispatch_t *disp; 1709135446Strhodes 1710135446Strhodes UNUSED(maxbuffers); 1711135446Strhodes UNUSED(buffersize); 1712135446Strhodes 1713135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1714135446Strhodes REQUIRE(isc_socket_gettype(sock) == isc_sockettype_tcp); 1715135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0); 1716135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0); 1717135446Strhodes 1718135446Strhodes attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ 1719135446Strhodes 1720135446Strhodes LOCK(&mgr->lock); 1721135446Strhodes 1722135446Strhodes /* 1723135446Strhodes * dispatch_allocate() checks mgr for us. 1724135446Strhodes * qid_allocate() checks buckets and increment for us. 1725135446Strhodes */ 1726135446Strhodes disp = NULL; 1727135446Strhodes result = dispatch_allocate(mgr, maxrequests, &disp); 1728135446Strhodes if (result != ISC_R_SUCCESS) { 1729135446Strhodes UNLOCK(&mgr->lock); 1730135446Strhodes return (result); 1731135446Strhodes } 1732135446Strhodes 1733180477Sdougb result = qid_allocate(mgr, buckets, increment, &disp->qid); 1734135446Strhodes if (result != ISC_R_SUCCESS) 1735135446Strhodes goto deallocate_dispatch; 1736135446Strhodes 1737135446Strhodes disp->socktype = isc_sockettype_tcp; 1738135446Strhodes disp->socket = NULL; 1739135446Strhodes isc_socket_attach(sock, &disp->socket); 1740135446Strhodes 1741135446Strhodes disp->task = NULL; 1742135446Strhodes result = isc_task_create(taskmgr, 0, &disp->task); 1743135446Strhodes if (result != ISC_R_SUCCESS) 1744135446Strhodes goto kill_socket; 1745135446Strhodes 1746135446Strhodes disp->ctlevent = isc_event_allocate(mgr->mctx, disp, 1747135446Strhodes DNS_EVENT_DISPATCHCONTROL, 1748135446Strhodes destroy_disp, disp, 1749135446Strhodes sizeof(isc_event_t)); 1750174187Sdougb if (disp->ctlevent == NULL) { 1751174187Sdougb result = ISC_R_NOMEMORY; 1752135446Strhodes goto kill_task; 1753174187Sdougb } 1754135446Strhodes 1755135446Strhodes isc_task_setname(disp->task, "tcpdispatch", disp); 1756135446Strhodes 1757135446Strhodes dns_tcpmsg_init(mgr->mctx, disp->socket, &disp->tcpmsg); 1758135446Strhodes disp->tcpmsg_valid = 1; 1759135446Strhodes 1760135446Strhodes disp->attributes = attributes; 1761135446Strhodes 1762135446Strhodes /* 1763135446Strhodes * Append it to the dispatcher list. 1764135446Strhodes */ 1765135446Strhodes ISC_LIST_APPEND(mgr->list, disp, link); 1766135446Strhodes UNLOCK(&mgr->lock); 1767135446Strhodes 1768135446Strhodes mgr_log(mgr, LVL(90), "created TCP dispatcher %p", disp); 1769135446Strhodes dispatch_log(disp, LVL(90), "created task %p", disp->task); 1770135446Strhodes 1771135446Strhodes *dispp = disp; 1772135446Strhodes 1773135446Strhodes return (ISC_R_SUCCESS); 1774135446Strhodes 1775135446Strhodes /* 1776135446Strhodes * Error returns. 1777135446Strhodes */ 1778135446Strhodes kill_task: 1779135446Strhodes isc_task_detach(&disp->task); 1780135446Strhodes kill_socket: 1781135446Strhodes isc_socket_detach(&disp->socket); 1782135446Strhodes deallocate_dispatch: 1783135446Strhodes dispatch_free(&disp); 1784135446Strhodes 1785135446Strhodes UNLOCK(&mgr->lock); 1786135446Strhodes 1787135446Strhodes return (result); 1788135446Strhodes} 1789135446Strhodes 1790135446Strhodesisc_result_t 1791135446Strhodesdns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 1792135446Strhodes isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, 1793135446Strhodes unsigned int buffersize, 1794135446Strhodes unsigned int maxbuffers, unsigned int maxrequests, 1795135446Strhodes unsigned int buckets, unsigned int increment, 1796135446Strhodes unsigned int attributes, unsigned int mask, 1797135446Strhodes dns_dispatch_t **dispp) 1798135446Strhodes{ 1799135446Strhodes isc_result_t result; 1800180477Sdougb dns_dispatch_t *disp = NULL; 1801135446Strhodes 1802135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 1803135446Strhodes REQUIRE(sockmgr != NULL); 1804135446Strhodes REQUIRE(localaddr != NULL); 1805135446Strhodes REQUIRE(taskmgr != NULL); 1806135446Strhodes REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); 1807135446Strhodes REQUIRE(maxbuffers > 0); 1808135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 1809135446Strhodes REQUIRE(increment > buckets); 1810135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 1811135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_TCP) == 0); 1812135446Strhodes 1813135446Strhodes result = dns_dispatchmgr_setudp(mgr, buffersize, maxbuffers, 1814135446Strhodes buckets, increment); 1815135446Strhodes if (result != ISC_R_SUCCESS) 1816135446Strhodes return (result); 1817135446Strhodes 1818135446Strhodes LOCK(&mgr->lock); 1819135446Strhodes 1820180477Sdougb if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) { 1821180477Sdougb REQUIRE(isc_sockaddr_getport(localaddr) == 0); 1822180477Sdougb goto createudp; 1823180477Sdougb } 1824180477Sdougb 1825135446Strhodes /* 1826135446Strhodes * First, see if we have a dispatcher that matches. 1827135446Strhodes */ 1828135446Strhodes disp = NULL; 1829135446Strhodes result = dispatch_find(mgr, localaddr, attributes, mask, &disp); 1830135446Strhodes if (result == ISC_R_SUCCESS) { 1831135446Strhodes disp->refcount++; 1832135446Strhodes 1833135446Strhodes if (disp->maxrequests < maxrequests) 1834135446Strhodes disp->maxrequests = maxrequests; 1835135446Strhodes 1836135446Strhodes if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 && 1837135446Strhodes (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) 1838135446Strhodes { 1839135446Strhodes disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; 1840135446Strhodes if (disp->recv_pending != 0) 1841135446Strhodes isc_socket_cancel(disp->socket, disp->task, 1842135446Strhodes ISC_SOCKCANCEL_RECV); 1843135446Strhodes } 1844135446Strhodes 1845135446Strhodes UNLOCK(&disp->lock); 1846135446Strhodes UNLOCK(&mgr->lock); 1847135446Strhodes 1848135446Strhodes *dispp = disp; 1849135446Strhodes 1850135446Strhodes return (ISC_R_SUCCESS); 1851135446Strhodes } 1852135446Strhodes 1853180477Sdougb createudp: 1854135446Strhodes /* 1855135446Strhodes * Nope, create one. 1856135446Strhodes */ 1857135446Strhodes result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr, 1858135446Strhodes maxrequests, attributes, &disp); 1859135446Strhodes if (result != ISC_R_SUCCESS) { 1860135446Strhodes UNLOCK(&mgr->lock); 1861135446Strhodes return (result); 1862135446Strhodes } 1863135446Strhodes 1864135446Strhodes UNLOCK(&mgr->lock); 1865135446Strhodes *dispp = disp; 1866135446Strhodes return (ISC_R_SUCCESS); 1867135446Strhodes} 1868135446Strhodes 1869135446Strhodes/* 1870135446Strhodes * mgr should be locked. 1871135446Strhodes */ 1872165071Sdougb 1873165071Sdougb#ifndef DNS_DISPATCH_HELD 1874165071Sdougb#define DNS_DISPATCH_HELD 20U 1875165071Sdougb#endif 1876165071Sdougb 1877135446Strhodesstatic isc_result_t 1878135446Strhodesdispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 1879135446Strhodes isc_taskmgr_t *taskmgr, 1880135446Strhodes isc_sockaddr_t *localaddr, 1881135446Strhodes unsigned int maxrequests, 1882135446Strhodes unsigned int attributes, 1883135446Strhodes dns_dispatch_t **dispp) 1884135446Strhodes{ 1885135446Strhodes isc_result_t result; 1886135446Strhodes dns_dispatch_t *disp; 1887165071Sdougb isc_socket_t *sock = NULL; 1888165071Sdougb isc_socket_t *held[DNS_DISPATCH_HELD]; 1889180477Sdougb unsigned int i = 0, j = 0, k = 0; 1890180477Sdougb isc_sockaddr_t localaddr_bound; 1891180477Sdougb in_port_t localport = 0; 1892135446Strhodes 1893135446Strhodes /* 1894135446Strhodes * dispatch_allocate() checks mgr for us. 1895135446Strhodes */ 1896135446Strhodes disp = NULL; 1897135446Strhodes result = dispatch_allocate(mgr, maxrequests, &disp); 1898135446Strhodes if (result != ISC_R_SUCCESS) 1899135446Strhodes return (result); 1900135446Strhodes 1901135446Strhodes /* 1902165071Sdougb * Try to allocate a socket that is not on the blacklist. 1903165071Sdougb * Hold up to DNS_DISPATCH_HELD sockets to prevent the OS 1904165071Sdougb * from returning the same port to us too quickly. 1905135446Strhodes */ 1906165071Sdougb memset(held, 0, sizeof(held)); 1907180477Sdougb localaddr_bound = *localaddr; 1908135446Strhodes getsocket: 1909180477Sdougb if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) { 1910180477Sdougb in_port_t prt; 1911180477Sdougb 1912180477Sdougb /* XXX: should the range be configurable? */ 1913180477Sdougb prt = 1024 + dispatch_arc4uniformrandom(mgr, 65535 - 1023); 1914180477Sdougb isc_sockaddr_setport(&localaddr_bound, prt); 1915180477Sdougb if (blacklisted(mgr, NULL, &localaddr_bound)) { 1916180477Sdougb if (++k == 1024) 1917180477Sdougb attributes &= ~DNS_DISPATCHATTR_RANDOMPORT; 1918180477Sdougb goto getsocket; 1919180477Sdougb } 1920180477Sdougb result = create_socket(sockmgr, &localaddr_bound, &sock); 1921180477Sdougb if (result == ISC_R_ADDRINUSE) { 1922180477Sdougb if (++k == 1024) 1923180477Sdougb attributes &= ~DNS_DISPATCHATTR_RANDOMPORT; 1924180477Sdougb goto getsocket; 1925180477Sdougb } 1926180477Sdougb localport = prt; 1927180477Sdougb } else 1928180477Sdougb result = create_socket(sockmgr, localaddr, &sock); 1929135446Strhodes if (result != ISC_R_SUCCESS) 1930135446Strhodes goto deallocate_dispatch; 1931180477Sdougb if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) == 0 && 1932180477Sdougb isc_sockaddr_getport(localaddr) == 0 && 1933180477Sdougb blacklisted(mgr, sock, NULL)) 1934180477Sdougb { 1935165071Sdougb if (held[i] != NULL) 1936165071Sdougb isc_socket_detach(&held[i]); 1937165071Sdougb held[i++] = sock; 1938165071Sdougb sock = NULL; 1939165071Sdougb if (i == DNS_DISPATCH_HELD) 1940165071Sdougb i = 0; 1941165071Sdougb if (j++ == 0xffffU) { 1942165071Sdougb mgr_log(mgr, ISC_LOG_ERROR, "avoid-v%s-udp-ports: " 1943165071Sdougb "unable to allocate a non-blacklisted port", 1944165071Sdougb isc_sockaddr_pf(localaddr) == AF_INET ? 1945165071Sdougb "4" : "6"); 1946165071Sdougb result = ISC_R_FAILURE; 1947165071Sdougb goto deallocate_dispatch; 1948165071Sdougb } 1949135446Strhodes goto getsocket; 1950135446Strhodes } 1951135446Strhodes 1952135446Strhodes disp->socktype = isc_sockettype_udp; 1953135446Strhodes disp->socket = sock; 1954135446Strhodes disp->local = *localaddr; 1955180477Sdougb disp->localport = localport; 1956135446Strhodes 1957135446Strhodes disp->task = NULL; 1958135446Strhodes result = isc_task_create(taskmgr, 0, &disp->task); 1959135446Strhodes if (result != ISC_R_SUCCESS) 1960135446Strhodes goto kill_socket; 1961135446Strhodes 1962135446Strhodes disp->ctlevent = isc_event_allocate(mgr->mctx, disp, 1963135446Strhodes DNS_EVENT_DISPATCHCONTROL, 1964135446Strhodes destroy_disp, disp, 1965135446Strhodes sizeof(isc_event_t)); 1966174187Sdougb if (disp->ctlevent == NULL) { 1967174187Sdougb result = ISC_R_NOMEMORY; 1968135446Strhodes goto kill_task; 1969174187Sdougb } 1970135446Strhodes 1971135446Strhodes isc_task_setname(disp->task, "udpdispatch", disp); 1972135446Strhodes 1973135446Strhodes attributes &= ~DNS_DISPATCHATTR_TCP; 1974135446Strhodes attributes |= DNS_DISPATCHATTR_UDP; 1975135446Strhodes disp->attributes = attributes; 1976135446Strhodes 1977135446Strhodes /* 1978135446Strhodes * Append it to the dispatcher list. 1979135446Strhodes */ 1980135446Strhodes ISC_LIST_APPEND(mgr->list, disp, link); 1981135446Strhodes 1982135446Strhodes mgr_log(mgr, LVL(90), "created UDP dispatcher %p", disp); 1983135446Strhodes dispatch_log(disp, LVL(90), "created task %p", disp->task); 1984135446Strhodes dispatch_log(disp, LVL(90), "created socket %p", disp->socket); 1985135446Strhodes 1986135446Strhodes *dispp = disp; 1987135446Strhodes 1988165071Sdougb goto cleanheld; 1989135446Strhodes 1990135446Strhodes /* 1991135446Strhodes * Error returns. 1992135446Strhodes */ 1993135446Strhodes kill_task: 1994135446Strhodes isc_task_detach(&disp->task); 1995135446Strhodes kill_socket: 1996135446Strhodes isc_socket_detach(&disp->socket); 1997135446Strhodes deallocate_dispatch: 1998135446Strhodes dispatch_free(&disp); 1999165071Sdougb cleanheld: 2000165071Sdougb for (i = 0; i < DNS_DISPATCH_HELD; i++) 2001165071Sdougb if (held[i] != NULL) 2002165071Sdougb isc_socket_detach(&held[i]); 2003135446Strhodes return (result); 2004135446Strhodes} 2005135446Strhodes 2006135446Strhodesvoid 2007135446Strhodesdns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp) { 2008135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2009135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 2010135446Strhodes 2011135446Strhodes LOCK(&disp->lock); 2012135446Strhodes disp->refcount++; 2013135446Strhodes UNLOCK(&disp->lock); 2014135446Strhodes 2015135446Strhodes *dispp = disp; 2016135446Strhodes} 2017135446Strhodes 2018135446Strhodes/* 2019135446Strhodes * It is important to lock the manager while we are deleting the dispatch, 2020135446Strhodes * since dns_dispatch_getudp will call dispatch_find, which returns to 2021135446Strhodes * the caller a dispatch but does not attach to it until later. _getudp 2022135446Strhodes * locks the manager, however, so locking it here will keep us from attaching 2023135446Strhodes * to a dispatcher that is in the process of going away. 2024135446Strhodes */ 2025135446Strhodesvoid 2026135446Strhodesdns_dispatch_detach(dns_dispatch_t **dispp) { 2027135446Strhodes dns_dispatch_t *disp; 2028135446Strhodes isc_boolean_t killit; 2029135446Strhodes 2030135446Strhodes REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp)); 2031135446Strhodes 2032135446Strhodes disp = *dispp; 2033135446Strhodes *dispp = NULL; 2034135446Strhodes 2035135446Strhodes LOCK(&disp->lock); 2036135446Strhodes 2037135446Strhodes INSIST(disp->refcount > 0); 2038135446Strhodes disp->refcount--; 2039135446Strhodes killit = ISC_FALSE; 2040135446Strhodes if (disp->refcount == 0) { 2041135446Strhodes if (disp->recv_pending > 0) 2042135446Strhodes isc_socket_cancel(disp->socket, disp->task, 2043135446Strhodes ISC_SOCKCANCEL_RECV); 2044135446Strhodes disp->shutting_down = 1; 2045135446Strhodes } 2046135446Strhodes 2047135446Strhodes dispatch_log(disp, LVL(90), "detach: refcount %d", disp->refcount); 2048135446Strhodes 2049135446Strhodes killit = destroy_disp_ok(disp); 2050135446Strhodes UNLOCK(&disp->lock); 2051135446Strhodes if (killit) 2052135446Strhodes isc_task_send(disp->task, &disp->ctlevent); 2053135446Strhodes} 2054135446Strhodes 2055135446Strhodesisc_result_t 2056135446Strhodesdns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, 2057135446Strhodes isc_task_t *task, isc_taskaction_t action, void *arg, 2058135446Strhodes dns_messageid_t *idp, dns_dispentry_t **resp) 2059135446Strhodes{ 2060135446Strhodes dns_dispentry_t *res; 2061135446Strhodes unsigned int bucket; 2062135446Strhodes dns_messageid_t id; 2063135446Strhodes int i; 2064135446Strhodes isc_boolean_t ok; 2065135446Strhodes dns_qid_t *qid; 2066135446Strhodes 2067135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2068135446Strhodes REQUIRE(task != NULL); 2069135446Strhodes REQUIRE(dest != NULL); 2070135446Strhodes REQUIRE(resp != NULL && *resp == NULL); 2071135446Strhodes REQUIRE(idp != NULL); 2072135446Strhodes 2073135446Strhodes LOCK(&disp->lock); 2074135446Strhodes 2075135446Strhodes if (disp->shutting_down == 1) { 2076135446Strhodes UNLOCK(&disp->lock); 2077135446Strhodes return (ISC_R_SHUTTINGDOWN); 2078135446Strhodes } 2079135446Strhodes 2080135446Strhodes if (disp->requests >= disp->maxrequests) { 2081135446Strhodes UNLOCK(&disp->lock); 2082135446Strhodes return (ISC_R_QUOTA); 2083135446Strhodes } 2084135446Strhodes 2085135446Strhodes /* 2086135446Strhodes * Try somewhat hard to find an unique ID. 2087135446Strhodes */ 2088180477Sdougb id = (dns_messageid_t)dispatch_arc4random(disp->mgr); 2089135446Strhodes qid = DNS_QID(disp); 2090135446Strhodes LOCK(&qid->lock); 2091180477Sdougb bucket = dns_hash(qid, dest, id, disp->localport); 2092135446Strhodes ok = ISC_FALSE; 2093135446Strhodes for (i = 0; i < 64; i++) { 2094180477Sdougb if (bucket_search(qid, dest, id, disp->localport, bucket) == 2095180477Sdougb NULL) { 2096135446Strhodes ok = ISC_TRUE; 2097135446Strhodes break; 2098135446Strhodes } 2099135446Strhodes id += qid->qid_increment; 2100135446Strhodes id &= 0x0000ffff; 2101180477Sdougb bucket = dns_hash(qid, dest, id, disp->localport); 2102135446Strhodes } 2103135446Strhodes 2104135446Strhodes if (!ok) { 2105135446Strhodes UNLOCK(&qid->lock); 2106135446Strhodes UNLOCK(&disp->lock); 2107135446Strhodes return (ISC_R_NOMORE); 2108135446Strhodes } 2109135446Strhodes 2110135446Strhodes res = isc_mempool_get(disp->mgr->rpool); 2111135446Strhodes if (res == NULL) { 2112135446Strhodes UNLOCK(&qid->lock); 2113135446Strhodes UNLOCK(&disp->lock); 2114135446Strhodes return (ISC_R_NOMEMORY); 2115135446Strhodes } 2116135446Strhodes 2117135446Strhodes disp->refcount++; 2118135446Strhodes disp->requests++; 2119135446Strhodes res->task = NULL; 2120135446Strhodes isc_task_attach(task, &res->task); 2121135446Strhodes res->disp = disp; 2122135446Strhodes res->id = id; 2123180477Sdougb res->port = disp->localport; 2124135446Strhodes res->bucket = bucket; 2125135446Strhodes res->host = *dest; 2126135446Strhodes res->action = action; 2127135446Strhodes res->arg = arg; 2128135446Strhodes res->item_out = ISC_FALSE; 2129135446Strhodes ISC_LIST_INIT(res->items); 2130135446Strhodes ISC_LINK_INIT(res, link); 2131135446Strhodes res->magic = RESPONSE_MAGIC; 2132135446Strhodes ISC_LIST_APPEND(qid->qid_table[bucket], res, link); 2133135446Strhodes UNLOCK(&qid->lock); 2134135446Strhodes 2135135446Strhodes request_log(disp, res, LVL(90), 2136135446Strhodes "attached to task %p", res->task); 2137135446Strhodes 2138135446Strhodes if (((disp->attributes & DNS_DISPATCHATTR_UDP) != 0) || 2139135446Strhodes ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) != 0)) 2140135446Strhodes startrecv(disp); 2141135446Strhodes 2142135446Strhodes UNLOCK(&disp->lock); 2143135446Strhodes 2144135446Strhodes *idp = id; 2145135446Strhodes *resp = res; 2146135446Strhodes 2147135446Strhodes return (ISC_R_SUCCESS); 2148135446Strhodes} 2149135446Strhodes 2150135446Strhodesvoid 2151135446Strhodesdns_dispatch_starttcp(dns_dispatch_t *disp) { 2152135446Strhodes 2153135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2154135446Strhodes 2155135446Strhodes dispatch_log(disp, LVL(90), "starttcp %p", disp->task); 2156135446Strhodes 2157135446Strhodes LOCK(&disp->lock); 2158135446Strhodes disp->attributes |= DNS_DISPATCHATTR_CONNECTED; 2159135446Strhodes startrecv(disp); 2160135446Strhodes UNLOCK(&disp->lock); 2161135446Strhodes} 2162135446Strhodes 2163135446Strhodesvoid 2164135446Strhodesdns_dispatch_removeresponse(dns_dispentry_t **resp, 2165135446Strhodes dns_dispatchevent_t **sockevent) 2166135446Strhodes{ 2167135446Strhodes dns_dispatchmgr_t *mgr; 2168135446Strhodes dns_dispatch_t *disp; 2169135446Strhodes dns_dispentry_t *res; 2170135446Strhodes dns_dispatchevent_t *ev; 2171135446Strhodes unsigned int bucket; 2172135446Strhodes isc_boolean_t killit; 2173135446Strhodes unsigned int n; 2174135446Strhodes isc_eventlist_t events; 2175135446Strhodes dns_qid_t *qid; 2176135446Strhodes 2177135446Strhodes REQUIRE(resp != NULL); 2178135446Strhodes REQUIRE(VALID_RESPONSE(*resp)); 2179135446Strhodes 2180135446Strhodes res = *resp; 2181135446Strhodes *resp = NULL; 2182135446Strhodes 2183135446Strhodes disp = res->disp; 2184135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2185135446Strhodes mgr = disp->mgr; 2186135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2187135446Strhodes 2188135446Strhodes qid = DNS_QID(disp); 2189135446Strhodes 2190135446Strhodes if (sockevent != NULL) { 2191135446Strhodes REQUIRE(*sockevent != NULL); 2192135446Strhodes ev = *sockevent; 2193135446Strhodes *sockevent = NULL; 2194135446Strhodes } else { 2195135446Strhodes ev = NULL; 2196135446Strhodes } 2197135446Strhodes 2198135446Strhodes LOCK(&disp->lock); 2199135446Strhodes 2200135446Strhodes INSIST(disp->requests > 0); 2201135446Strhodes disp->requests--; 2202135446Strhodes INSIST(disp->refcount > 0); 2203135446Strhodes disp->refcount--; 2204135446Strhodes killit = ISC_FALSE; 2205135446Strhodes if (disp->refcount == 0) { 2206135446Strhodes if (disp->recv_pending > 0) 2207135446Strhodes isc_socket_cancel(disp->socket, disp->task, 2208135446Strhodes ISC_SOCKCANCEL_RECV); 2209135446Strhodes disp->shutting_down = 1; 2210135446Strhodes } 2211135446Strhodes 2212135446Strhodes bucket = res->bucket; 2213135446Strhodes 2214135446Strhodes LOCK(&qid->lock); 2215135446Strhodes ISC_LIST_UNLINK(qid->qid_table[bucket], res, link); 2216135446Strhodes UNLOCK(&qid->lock); 2217135446Strhodes 2218135446Strhodes if (ev == NULL && res->item_out) { 2219135446Strhodes /* 2220135446Strhodes * We've posted our event, but the caller hasn't gotten it 2221135446Strhodes * yet. Take it back. 2222135446Strhodes */ 2223135446Strhodes ISC_LIST_INIT(events); 2224135446Strhodes n = isc_task_unsend(res->task, res, DNS_EVENT_DISPATCH, 2225135446Strhodes NULL, &events); 2226135446Strhodes /* 2227135446Strhodes * We had better have gotten it back. 2228135446Strhodes */ 2229135446Strhodes INSIST(n == 1); 2230135446Strhodes ev = (dns_dispatchevent_t *)ISC_LIST_HEAD(events); 2231135446Strhodes } 2232135446Strhodes 2233135446Strhodes if (ev != NULL) { 2234135446Strhodes REQUIRE(res->item_out == ISC_TRUE); 2235135446Strhodes res->item_out = ISC_FALSE; 2236135446Strhodes if (ev->buffer.base != NULL) 2237135446Strhodes free_buffer(disp, ev->buffer.base, ev->buffer.length); 2238135446Strhodes free_event(disp, ev); 2239135446Strhodes } 2240135446Strhodes 2241135446Strhodes request_log(disp, res, LVL(90), "detaching from task %p", res->task); 2242135446Strhodes isc_task_detach(&res->task); 2243135446Strhodes 2244135446Strhodes /* 2245135446Strhodes * Free any buffered requests as well 2246135446Strhodes */ 2247135446Strhodes ev = ISC_LIST_HEAD(res->items); 2248135446Strhodes while (ev != NULL) { 2249135446Strhodes ISC_LIST_UNLINK(res->items, ev, ev_link); 2250135446Strhodes if (ev->buffer.base != NULL) 2251135446Strhodes free_buffer(disp, ev->buffer.base, ev->buffer.length); 2252135446Strhodes free_event(disp, ev); 2253135446Strhodes ev = ISC_LIST_HEAD(res->items); 2254135446Strhodes } 2255135446Strhodes res->magic = 0; 2256135446Strhodes isc_mempool_put(disp->mgr->rpool, res); 2257135446Strhodes if (disp->shutting_down == 1) 2258135446Strhodes do_cancel(disp); 2259135446Strhodes else 2260135446Strhodes startrecv(disp); 2261135446Strhodes 2262135446Strhodes killit = destroy_disp_ok(disp); 2263135446Strhodes UNLOCK(&disp->lock); 2264135446Strhodes if (killit) 2265135446Strhodes isc_task_send(disp->task, &disp->ctlevent); 2266135446Strhodes} 2267135446Strhodes 2268135446Strhodesstatic void 2269135446Strhodesdo_cancel(dns_dispatch_t *disp) { 2270135446Strhodes dns_dispatchevent_t *ev; 2271135446Strhodes dns_dispentry_t *resp; 2272135446Strhodes dns_qid_t *qid; 2273135446Strhodes 2274135446Strhodes if (disp->shutdown_out == 1) 2275135446Strhodes return; 2276135446Strhodes 2277135446Strhodes qid = DNS_QID(disp); 2278135446Strhodes 2279135446Strhodes /* 2280135446Strhodes * Search for the first response handler without packets outstanding. 2281135446Strhodes */ 2282135446Strhodes LOCK(&qid->lock); 2283135446Strhodes for (resp = linear_first(qid); 2284135446Strhodes resp != NULL && resp->item_out != ISC_FALSE; 2285135446Strhodes /* Empty. */) 2286135446Strhodes resp = linear_next(qid, resp); 2287135446Strhodes /* 2288135446Strhodes * No one to send the cancel event to, so nothing to do. 2289135446Strhodes */ 2290135446Strhodes if (resp == NULL) 2291135446Strhodes goto unlock; 2292135446Strhodes 2293135446Strhodes /* 2294135446Strhodes * Send the shutdown failsafe event to this resp. 2295135446Strhodes */ 2296135446Strhodes ev = disp->failsafe_ev; 2297135446Strhodes ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH, 2298135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 2299135446Strhodes ev->result = disp->shutdown_why; 2300135446Strhodes ev->buffer.base = NULL; 2301135446Strhodes ev->buffer.length = 0; 2302135446Strhodes disp->shutdown_out = 1; 2303135446Strhodes request_log(disp, resp, LVL(10), 2304135446Strhodes "cancel: failsafe event %p -> task %p", 2305135446Strhodes ev, resp->task); 2306135446Strhodes resp->item_out = ISC_TRUE; 2307135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&ev)); 2308135446Strhodes unlock: 2309135446Strhodes UNLOCK(&qid->lock); 2310135446Strhodes} 2311135446Strhodes 2312135446Strhodesisc_socket_t * 2313135446Strhodesdns_dispatch_getsocket(dns_dispatch_t *disp) { 2314135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2315135446Strhodes 2316135446Strhodes return (disp->socket); 2317135446Strhodes} 2318135446Strhodes 2319135446Strhodesisc_result_t 2320135446Strhodesdns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp) { 2321135446Strhodes 2322135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2323135446Strhodes REQUIRE(addrp != NULL); 2324135446Strhodes 2325135446Strhodes if (disp->socktype == isc_sockettype_udp) { 2326135446Strhodes *addrp = disp->local; 2327135446Strhodes return (ISC_R_SUCCESS); 2328135446Strhodes } 2329135446Strhodes return (ISC_R_NOTIMPLEMENTED); 2330135446Strhodes} 2331135446Strhodes 2332135446Strhodesvoid 2333135446Strhodesdns_dispatch_cancel(dns_dispatch_t *disp) { 2334135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2335135446Strhodes 2336135446Strhodes LOCK(&disp->lock); 2337135446Strhodes 2338135446Strhodes if (disp->shutting_down == 1) { 2339135446Strhodes UNLOCK(&disp->lock); 2340135446Strhodes return; 2341135446Strhodes } 2342135446Strhodes 2343135446Strhodes disp->shutdown_why = ISC_R_CANCELED; 2344135446Strhodes disp->shutting_down = 1; 2345135446Strhodes do_cancel(disp); 2346135446Strhodes 2347135446Strhodes UNLOCK(&disp->lock); 2348135446Strhodes 2349135446Strhodes return; 2350135446Strhodes} 2351135446Strhodes 2352135446Strhodesvoid 2353135446Strhodesdns_dispatch_changeattributes(dns_dispatch_t *disp, 2354135446Strhodes unsigned int attributes, unsigned int mask) 2355135446Strhodes{ 2356135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2357135446Strhodes 2358135446Strhodes /* XXXMLG 2359135446Strhodes * Should check for valid attributes here! 2360135446Strhodes */ 2361135446Strhodes 2362135446Strhodes LOCK(&disp->lock); 2363135446Strhodes 2364135446Strhodes if ((mask & DNS_DISPATCHATTR_NOLISTEN) != 0) { 2365135446Strhodes if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0 && 2366135446Strhodes (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0) { 2367135446Strhodes disp->attributes &= ~DNS_DISPATCHATTR_NOLISTEN; 2368135446Strhodes startrecv(disp); 2369135446Strhodes } else if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) 2370135446Strhodes == 0 && 2371135446Strhodes (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) { 2372135446Strhodes disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; 2373135446Strhodes if (disp->recv_pending != 0) 2374135446Strhodes isc_socket_cancel(disp->socket, disp->task, 2375135446Strhodes ISC_SOCKCANCEL_RECV); 2376135446Strhodes } 2377135446Strhodes } 2378135446Strhodes 2379135446Strhodes disp->attributes &= ~mask; 2380135446Strhodes disp->attributes |= (attributes & mask); 2381135446Strhodes UNLOCK(&disp->lock); 2382135446Strhodes} 2383135446Strhodes 2384135446Strhodesvoid 2385135446Strhodesdns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) { 2386135446Strhodes void *buf; 2387135446Strhodes isc_socketevent_t *sevent, *newsevent; 2388135446Strhodes 2389135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 2390135446Strhodes REQUIRE((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0); 2391135446Strhodes REQUIRE(event != NULL); 2392135446Strhodes 2393135446Strhodes sevent = (isc_socketevent_t *)event; 2394135446Strhodes 2395135446Strhodes INSIST(sevent->n <= disp->mgr->buffersize); 2396135446Strhodes newsevent = (isc_socketevent_t *) 2397135446Strhodes isc_event_allocate(disp->mgr->mctx, NULL, 2398135446Strhodes DNS_EVENT_IMPORTRECVDONE, udp_recv, 2399135446Strhodes disp, sizeof(isc_socketevent_t)); 2400135446Strhodes if (newsevent == NULL) 2401135446Strhodes return; 2402135446Strhodes 2403135446Strhodes buf = allocate_udp_buffer(disp); 2404135446Strhodes if (buf == NULL) { 2405135446Strhodes isc_event_free(ISC_EVENT_PTR(&newsevent)); 2406135446Strhodes return; 2407135446Strhodes } 2408135446Strhodes memcpy(buf, sevent->region.base, sevent->n); 2409135446Strhodes newsevent->region.base = buf; 2410135446Strhodes newsevent->region.length = disp->mgr->buffersize; 2411135446Strhodes newsevent->n = sevent->n; 2412135446Strhodes newsevent->result = sevent->result; 2413135446Strhodes newsevent->address = sevent->address; 2414135446Strhodes newsevent->timestamp = sevent->timestamp; 2415135446Strhodes newsevent->pktinfo = sevent->pktinfo; 2416135446Strhodes newsevent->attributes = sevent->attributes; 2417135446Strhodes 2418135446Strhodes isc_task_send(disp->task, ISC_EVENT_PTR(&newsevent)); 2419135446Strhodes} 2420135446Strhodes 2421135446Strhodes#if 0 2422135446Strhodesvoid 2423135446Strhodesdns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) { 2424135446Strhodes dns_dispatch_t *disp; 2425135446Strhodes char foo[1024]; 2426135446Strhodes 2427135446Strhodes disp = ISC_LIST_HEAD(mgr->list); 2428135446Strhodes while (disp != NULL) { 2429135446Strhodes isc_sockaddr_format(&disp->local, foo, sizeof(foo)); 2430135446Strhodes printf("\tdispatch %p, addr %s\n", disp, foo); 2431135446Strhodes disp = ISC_LIST_NEXT(disp, link); 2432135446Strhodes } 2433135446Strhodes} 2434135446Strhodes#endif 2435