1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2009, 2011-2014 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 18254897Serwin/* $Id: dispatch.c,v 1.175 2011/11/29 01:03:47 marka Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <stdlib.h> 25171577Sdougb#include <sys/types.h> 26171577Sdougb#include <unistd.h> 27186462Sdougb#include <stdlib.h> 28135446Strhodes 29135446Strhodes#include <isc/entropy.h> 30135446Strhodes#include <isc/mem.h> 31135446Strhodes#include <isc/mutex.h> 32186462Sdougb#include <isc/portset.h> 33135446Strhodes#include <isc/print.h> 34180477Sdougb#include <isc/random.h> 35254897Serwin#include <isc/socket.h> 36193149Sdougb#include <isc/stats.h> 37135446Strhodes#include <isc/string.h> 38135446Strhodes#include <isc/task.h> 39171577Sdougb#include <isc/time.h> 40135446Strhodes#include <isc/util.h> 41135446Strhodes 42135446Strhodes#include <dns/acl.h> 43135446Strhodes#include <dns/dispatch.h> 44135446Strhodes#include <dns/events.h> 45135446Strhodes#include <dns/log.h> 46135446Strhodes#include <dns/message.h> 47135446Strhodes#include <dns/portlist.h> 48193149Sdougb#include <dns/stats.h> 49135446Strhodes#include <dns/tcpmsg.h> 50135446Strhodes#include <dns/types.h> 51135446Strhodes 52135446Strhodestypedef ISC_LIST(dns_dispentry_t) dns_displist_t; 53135446Strhodes 54193149Sdougbtypedef struct dispsocket dispsocket_t; 55186462Sdougbtypedef ISC_LIST(dispsocket_t) dispsocketlist_t; 56135446Strhodes 57193149Sdougbtypedef struct dispportentry dispportentry_t; 58193149Sdougbtypedef ISC_LIST(dispportentry_t) dispportlist_t; 59193149Sdougb 60180477Sdougb/* ARC4 Random generator state */ 61180477Sdougbtypedef struct arc4ctx { 62180477Sdougb isc_uint8_t i; 63180477Sdougb isc_uint8_t j; 64180477Sdougb isc_uint8_t s[256]; 65180477Sdougb int count; 66186462Sdougb isc_entropy_t *entropy; /*%< entropy source for ARC4 */ 67186462Sdougb isc_mutex_t *lock; 68180477Sdougb} arc4ctx_t; 69180477Sdougb 70186462Sdougbtypedef struct dns_qid { 71186462Sdougb unsigned int magic; 72186462Sdougb unsigned int qid_nbuckets; /*%< hash table size */ 73186462Sdougb unsigned int qid_increment; /*%< id increment on collision */ 74186462Sdougb isc_mutex_t lock; 75186462Sdougb dns_displist_t *qid_table; /*%< the table itself */ 76186462Sdougb dispsocketlist_t *sock_table; /*%< socket table */ 77186462Sdougb} dns_qid_t; 78186462Sdougb 79135446Strhodesstruct dns_dispatchmgr { 80135446Strhodes /* Unlocked. */ 81135446Strhodes unsigned int magic; 82135446Strhodes isc_mem_t *mctx; 83135446Strhodes dns_acl_t *blackhole; 84135446Strhodes dns_portlist_t *portlist; 85193149Sdougb isc_stats_t *stats; 86186462Sdougb isc_entropy_t *entropy; /*%< entropy source */ 87135446Strhodes 88135446Strhodes /* Locked by "lock". */ 89135446Strhodes isc_mutex_t lock; 90135446Strhodes unsigned int state; 91135446Strhodes ISC_LIST(dns_dispatch_t) list; 92135446Strhodes 93180477Sdougb /* Locked by arc4_lock. */ 94180477Sdougb isc_mutex_t arc4_lock; 95180477Sdougb arc4ctx_t arc4ctx; /*%< ARC4 context for QID */ 96180477Sdougb 97135446Strhodes /* locked by buffer lock */ 98135446Strhodes dns_qid_t *qid; 99135446Strhodes isc_mutex_t buffer_lock; 100170222Sdougb unsigned int buffers; /*%< allocated buffers */ 101170222Sdougb unsigned int buffersize; /*%< size of each buffer */ 102170222Sdougb unsigned int maxbuffers; /*%< max buffers */ 103135446Strhodes 104135446Strhodes /* Locked internally. */ 105254897Serwin isc_mutex_t depool_lock; 106254897Serwin isc_mempool_t *depool; /*%< pool for dispatch events */ 107254897Serwin isc_mutex_t rpool_lock; 108254897Serwin isc_mempool_t *rpool; /*%< pool for replies */ 109254897Serwin isc_mutex_t dpool_lock; 110170222Sdougb isc_mempool_t *dpool; /*%< dispatch allocations */ 111254897Serwin isc_mutex_t bpool_lock; 112254897Serwin isc_mempool_t *bpool; /*%< pool for buffers */ 113254897Serwin isc_mutex_t spool_lock; 114254897Serwin isc_mempool_t *spool; /*%< pool for dispsocks */ 115135446Strhodes 116186462Sdougb /*% 117186462Sdougb * Locked by qid->lock if qid exists; otherwise, can be used without 118186462Sdougb * being locked. 119186462Sdougb * Memory footprint considerations: this is a simple implementation of 120186462Sdougb * available ports, i.e., an ordered array of the actual port numbers. 121186462Sdougb * This will require about 256KB of memory in the worst case (128KB for 122186462Sdougb * each of IPv4 and IPv6). We could reduce it by representing it as a 123186462Sdougb * more sophisticated way such as a list (or array) of ranges that are 124186462Sdougb * searched to identify a specific port. Our decision here is the saved 125186462Sdougb * memory isn't worth the implementation complexity, considering the 126186462Sdougb * fact that the whole BIND9 process (which is mainly named) already 127186462Sdougb * requires a pretty large memory footprint. We may, however, have to 128186462Sdougb * revisit the decision when we want to use it as a separate module for 129186462Sdougb * an environment where memory requirement is severer. 130186462Sdougb */ 131186462Sdougb in_port_t *v4ports; /*%< available ports for IPv4 */ 132186462Sdougb unsigned int nv4ports; /*%< # of available ports for IPv4 */ 133186462Sdougb in_port_t *v6ports; /*%< available ports for IPv4 */ 134186462Sdougb unsigned int nv6ports; /*%< # of available ports for IPv4 */ 135135446Strhodes}; 136135446Strhodes 137135446Strhodes#define MGR_SHUTTINGDOWN 0x00000001U 138135446Strhodes#define MGR_IS_SHUTTINGDOWN(l) (((l)->state & MGR_SHUTTINGDOWN) != 0) 139135446Strhodes 140135446Strhodes#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0) 141135446Strhodes 142135446Strhodesstruct dns_dispentry { 143135446Strhodes unsigned int magic; 144135446Strhodes dns_dispatch_t *disp; 145135446Strhodes dns_messageid_t id; 146180477Sdougb in_port_t port; 147135446Strhodes unsigned int bucket; 148135446Strhodes isc_sockaddr_t host; 149135446Strhodes isc_task_t *task; 150135446Strhodes isc_taskaction_t action; 151135446Strhodes void *arg; 152135446Strhodes isc_boolean_t item_out; 153186462Sdougb dispsocket_t *dispsocket; 154135446Strhodes ISC_LIST(dns_dispatchevent_t) items; 155135446Strhodes ISC_LINK(dns_dispentry_t) link; 156135446Strhodes}; 157135446Strhodes 158186462Sdougb/*% 159186462Sdougb * Maximum number of dispatch sockets that can be pooled for reuse. The 160186462Sdougb * appropriate value may vary, but experiments have shown a busy caching server 161186462Sdougb * may need more than 1000 sockets concurrently opened. The maximum allowable 162186462Sdougb * number of dispatch sockets (per manager) will be set to the double of this 163186462Sdougb * value. 164186462Sdougb */ 165186462Sdougb#ifndef DNS_DISPATCH_POOLSOCKS 166186462Sdougb#define DNS_DISPATCH_POOLSOCKS 2048 167186462Sdougb#endif 168186462Sdougb 169186462Sdougb/*% 170186462Sdougb * Quota to control the number of dispatch sockets. If a dispatch has more 171186462Sdougb * than the quota of sockets, new queries will purge oldest ones, so that 172186462Sdougb * a massive number of outstanding queries won't prevent subsequent queries 173186462Sdougb * (especially if the older ones take longer time and result in timeout). 174186462Sdougb */ 175186462Sdougb#ifndef DNS_DISPATCH_SOCKSQUOTA 176186462Sdougb#define DNS_DISPATCH_SOCKSQUOTA 3072 177186462Sdougb#endif 178186462Sdougb 179186462Sdougbstruct dispsocket { 180186462Sdougb unsigned int magic; 181186462Sdougb isc_socket_t *socket; 182186462Sdougb dns_dispatch_t *disp; 183186462Sdougb isc_sockaddr_t host; 184193149Sdougb in_port_t localport; /* XXX: should be removed later */ 185193149Sdougb dispportentry_t *portentry; 186186462Sdougb dns_dispentry_t *resp; 187186462Sdougb isc_task_t *task; 188186462Sdougb ISC_LINK(dispsocket_t) link; 189186462Sdougb unsigned int bucket; 190186462Sdougb ISC_LINK(dispsocket_t) blink; 191186462Sdougb}; 192186462Sdougb 193193149Sdougb/*% 194193149Sdougb * A port table entry. We remember every port we first open in a table with a 195193149Sdougb * reference counter so that we can 'reuse' the same port (with different 196193149Sdougb * destination addresses) using the SO_REUSEADDR socket option. 197193149Sdougb */ 198193149Sdougbstruct dispportentry { 199193149Sdougb in_port_t port; 200193149Sdougb unsigned int refs; 201193149Sdougb ISC_LINK(struct dispportentry) link; 202193149Sdougb}; 203193149Sdougb 204193149Sdougb#ifndef DNS_DISPATCH_PORTTABLESIZE 205193149Sdougb#define DNS_DISPATCH_PORTTABLESIZE 1024 206193149Sdougb#endif 207193149Sdougb 208135446Strhodes#define INVALID_BUCKET (0xffffdead) 209135446Strhodes 210186462Sdougb/*% 211186462Sdougb * Number of tasks for each dispatch that use separate sockets for different 212186462Sdougb * transactions. This must be a power of 2 as it will divide 32 bit numbers 213186462Sdougb * to get an uniformly random tasks selection. See get_dispsocket(). 214186462Sdougb */ 215186462Sdougb#define MAX_INTERNAL_TASKS 64 216186462Sdougb 217135446Strhodesstruct dns_dispatch { 218135446Strhodes /* Unlocked. */ 219170222Sdougb unsigned int magic; /*%< magic */ 220170222Sdougb dns_dispatchmgr_t *mgr; /*%< dispatch manager */ 221186462Sdougb int ntasks; 222186462Sdougb /*% 223186462Sdougb * internal task buckets. We use multiple tasks to distribute various 224186462Sdougb * socket events well when using separate dispatch sockets. We use the 225186462Sdougb * 1st task (task[0]) for internal control events. 226186462Sdougb */ 227186462Sdougb isc_task_t *task[MAX_INTERNAL_TASKS]; 228170222Sdougb isc_socket_t *socket; /*%< isc socket attached to */ 229170222Sdougb isc_sockaddr_t local; /*%< local address */ 230180477Sdougb in_port_t localport; /*%< local UDP port */ 231170222Sdougb unsigned int maxrequests; /*%< max requests */ 232135446Strhodes isc_event_t *ctlevent; 233135446Strhodes 234254897Serwin isc_mutex_t sepool_lock; 235254897Serwin isc_mempool_t *sepool; /*%< pool for socket events */ 236254897Serwin 237170222Sdougb /*% Locked by mgr->lock. */ 238135446Strhodes ISC_LINK(dns_dispatch_t) link; 239135446Strhodes 240135446Strhodes /* Locked by "lock". */ 241170222Sdougb isc_mutex_t lock; /*%< locks all below */ 242135446Strhodes isc_sockettype_t socktype; 243135446Strhodes unsigned int attributes; 244170222Sdougb unsigned int refcount; /*%< number of users */ 245170222Sdougb dns_dispatchevent_t *failsafe_ev; /*%< failsafe cancel event */ 246135446Strhodes unsigned int shutting_down : 1, 247135446Strhodes shutdown_out : 1, 248135446Strhodes connected : 1, 249135446Strhodes tcpmsg_valid : 1, 250170222Sdougb recv_pending : 1; /*%< is a recv() pending? */ 251135446Strhodes isc_result_t shutdown_why; 252186462Sdougb ISC_LIST(dispsocket_t) activesockets; 253186462Sdougb ISC_LIST(dispsocket_t) inactivesockets; 254186462Sdougb unsigned int nsockets; 255170222Sdougb unsigned int requests; /*%< how many requests we have */ 256170222Sdougb unsigned int tcpbuffers; /*%< allocated buffers */ 257170222Sdougb dns_tcpmsg_t tcpmsg; /*%< for tcp streams */ 258135446Strhodes dns_qid_t *qid; 259186462Sdougb arc4ctx_t arc4ctx; /*%< for QID/UDP port num */ 260193149Sdougb dispportlist_t *port_table; /*%< hold ports 'owned' by us */ 261193149Sdougb isc_mempool_t *portpool; /*%< port table entries */ 262135446Strhodes}; 263135446Strhodes 264135446Strhodes#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ') 265135446Strhodes#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC) 266135446Strhodes 267135446Strhodes#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p') 268135446Strhodes#define VALID_RESPONSE(e) ISC_MAGIC_VALID((e), RESPONSE_MAGIC) 269135446Strhodes 270186462Sdougb#define DISPSOCK_MAGIC ISC_MAGIC('D', 's', 'o', 'c') 271186462Sdougb#define VALID_DISPSOCK(e) ISC_MAGIC_VALID((e), DISPSOCK_MAGIC) 272186462Sdougb 273135446Strhodes#define DISPATCH_MAGIC ISC_MAGIC('D', 'i', 's', 'p') 274135446Strhodes#define VALID_DISPATCH(e) ISC_MAGIC_VALID((e), DISPATCH_MAGIC) 275135446Strhodes 276135446Strhodes#define DNS_DISPATCHMGR_MAGIC ISC_MAGIC('D', 'M', 'g', 'r') 277135446Strhodes#define VALID_DISPATCHMGR(e) ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC) 278135446Strhodes 279135446Strhodes#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \ 280135446Strhodes (disp)->qid : (disp)->mgr->qid 281186462Sdougb#define DISP_ARC4CTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \ 282186462Sdougb (&(disp)->arc4ctx) : (&(disp)->mgr->arc4ctx) 283186462Sdougb 284186462Sdougb/*% 285186462Sdougb * Locking a query port buffer is a bit tricky. We access the buffer without 286186462Sdougb * locking until qid is created. Technically, there is a possibility of race 287186462Sdougb * between the creation of qid and access to the port buffer; in practice, 288186462Sdougb * however, this should be safe because qid isn't created until the first 289186462Sdougb * dispatch is created and there should be no contending situation until then. 290186462Sdougb */ 291186462Sdougb#define PORTBUFLOCK(mgr) if ((mgr)->qid != NULL) LOCK(&((mgr)->qid->lock)) 292186462Sdougb#define PORTBUFUNLOCK(mgr) if ((mgr)->qid != NULL) UNLOCK((&(mgr)->qid->lock)) 293186462Sdougb 294135446Strhodes/* 295135446Strhodes * Statics. 296135446Strhodes */ 297186462Sdougbstatic dns_dispentry_t *entry_search(dns_qid_t *, isc_sockaddr_t *, 298186462Sdougb dns_messageid_t, in_port_t, unsigned int); 299135446Strhodesstatic isc_boolean_t destroy_disp_ok(dns_dispatch_t *); 300135446Strhodesstatic void destroy_disp(isc_task_t *task, isc_event_t *event); 301186462Sdougbstatic void destroy_dispsocket(dns_dispatch_t *, dispsocket_t **); 302186462Sdougbstatic void deactivate_dispsocket(dns_dispatch_t *, dispsocket_t *); 303186462Sdougbstatic void udp_exrecv(isc_task_t *, isc_event_t *); 304186462Sdougbstatic void udp_shrecv(isc_task_t *, isc_event_t *); 305186462Sdougbstatic void udp_recv(isc_event_t *, dns_dispatch_t *, dispsocket_t *); 306135446Strhodesstatic void tcp_recv(isc_task_t *, isc_event_t *); 307186462Sdougbstatic isc_result_t startrecv(dns_dispatch_t *, dispsocket_t *); 308180477Sdougbstatic isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t, 309180477Sdougb in_port_t); 310135446Strhodesstatic void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len); 311135446Strhodesstatic void *allocate_udp_buffer(dns_dispatch_t *disp); 312254897Serwinstatic inline void free_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev); 313254897Serwinstatic inline dns_dispatchevent_t *allocate_devent(dns_dispatch_t *disp); 314135446Strhodesstatic void do_cancel(dns_dispatch_t *disp); 315135446Strhodesstatic dns_dispentry_t *linear_first(dns_qid_t *disp); 316135446Strhodesstatic dns_dispentry_t *linear_next(dns_qid_t *disp, 317135446Strhodes dns_dispentry_t *resp); 318135446Strhodesstatic void dispatch_free(dns_dispatch_t **dispp); 319186462Sdougbstatic isc_result_t get_udpsocket(dns_dispatchmgr_t *mgr, 320186462Sdougb dns_dispatch_t *disp, 321186462Sdougb isc_socketmgr_t *sockmgr, 322186462Sdougb isc_sockaddr_t *localaddr, 323254897Serwin isc_socket_t **sockp, 324254897Serwin isc_socket_t *dup_socket); 325135446Strhodesstatic isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr, 326135446Strhodes isc_socketmgr_t *sockmgr, 327135446Strhodes isc_taskmgr_t *taskmgr, 328135446Strhodes isc_sockaddr_t *localaddr, 329135446Strhodes unsigned int maxrequests, 330135446Strhodes unsigned int attributes, 331254897Serwin dns_dispatch_t **dispp, 332254897Serwin isc_socket_t *dup_socket); 333135446Strhodesstatic isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr); 334135446Strhodesstatic void destroy_mgr(dns_dispatchmgr_t **mgrp); 335135446Strhodesstatic isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, 336186462Sdougb unsigned int increment, dns_qid_t **qidp, 337186462Sdougb isc_boolean_t needaddrtable); 338135446Strhodesstatic void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp); 339186462Sdougbstatic isc_result_t open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, 340254897Serwin unsigned int options, isc_socket_t **sockp, 341254897Serwin isc_socket_t *dup_socket); 342186462Sdougbstatic isc_boolean_t portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock, 343186462Sdougb isc_sockaddr_t *sockaddrp); 344135446Strhodes 345135446Strhodes#define LVL(x) ISC_LOG_DEBUG(x) 346135446Strhodes 347135446Strhodesstatic void 348135446Strhodesmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) 349135446Strhodes ISC_FORMAT_PRINTF(3, 4); 350135446Strhodes 351135446Strhodesstatic void 352135446Strhodesmgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) { 353135446Strhodes char msgbuf[2048]; 354135446Strhodes va_list ap; 355135446Strhodes 356135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 357135446Strhodes return; 358135446Strhodes 359135446Strhodes va_start(ap, fmt); 360135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 361135446Strhodes va_end(ap); 362135446Strhodes 363135446Strhodes isc_log_write(dns_lctx, 364135446Strhodes DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, 365135446Strhodes level, "dispatchmgr %p: %s", mgr, msgbuf); 366135446Strhodes} 367135446Strhodes 368193149Sdougbstatic inline void 369193149Sdougbinc_stats(dns_dispatchmgr_t *mgr, isc_statscounter_t counter) { 370193149Sdougb if (mgr->stats != NULL) 371193149Sdougb isc_stats_increment(mgr->stats, counter); 372193149Sdougb} 373193149Sdougb 374135446Strhodesstatic void 375135446Strhodesdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) 376135446Strhodes ISC_FORMAT_PRINTF(3, 4); 377135446Strhodes 378135446Strhodesstatic void 379135446Strhodesdispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) { 380135446Strhodes char msgbuf[2048]; 381135446Strhodes va_list ap; 382135446Strhodes 383135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 384135446Strhodes return; 385135446Strhodes 386135446Strhodes va_start(ap, fmt); 387135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 388135446Strhodes va_end(ap); 389135446Strhodes 390135446Strhodes isc_log_write(dns_lctx, 391135446Strhodes DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH, 392135446Strhodes level, "dispatch %p: %s", disp, msgbuf); 393135446Strhodes} 394135446Strhodes 395135446Strhodesstatic void 396135446Strhodesrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, 397135446Strhodes int level, const char *fmt, ...) 398135446Strhodes ISC_FORMAT_PRINTF(4, 5); 399135446Strhodes 400135446Strhodesstatic void 401135446Strhodesrequest_log(dns_dispatch_t *disp, dns_dispentry_t *resp, 402135446Strhodes int level, const char *fmt, ...) 403135446Strhodes{ 404135446Strhodes char msgbuf[2048]; 405135446Strhodes char peerbuf[256]; 406135446Strhodes va_list ap; 407135446Strhodes 408135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 409135446Strhodes return; 410135446Strhodes 411135446Strhodes va_start(ap, fmt); 412135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 413135446Strhodes va_end(ap); 414135446Strhodes 415135446Strhodes if (VALID_RESPONSE(resp)) { 416135446Strhodes isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf)); 417135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, 418135446Strhodes DNS_LOGMODULE_DISPATCH, level, 419135446Strhodes "dispatch %p response %p %s: %s", disp, resp, 420135446Strhodes peerbuf, msgbuf); 421135446Strhodes } else { 422135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH, 423135446Strhodes DNS_LOGMODULE_DISPATCH, level, 424135446Strhodes "dispatch %p req/resp %p: %s", disp, resp, 425135446Strhodes msgbuf); 426135446Strhodes } 427135446Strhodes} 428135446Strhodes 429186462Sdougb/*% 430182645Sdougb * ARC4 random number generator derived from OpenBSD. 431224092Sdougb * Only dispatch_random() and dispatch_uniformrandom() are expected 432182645Sdougb * to be called from general dispatch routines; the rest of them are subroutines 433182645Sdougb * for these two. 434182645Sdougb * 435182645Sdougb * The original copyright follows: 436182645Sdougb * Copyright (c) 1996, David Mazieres <dm@uun.org> 437182645Sdougb * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 438182645Sdougb * 439182645Sdougb * Permission to use, copy, modify, and distribute this software for any 440182645Sdougb * purpose with or without fee is hereby granted, provided that the above 441182645Sdougb * copyright notice and this permission notice appear in all copies. 442182645Sdougb * 443182645Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 444182645Sdougb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 445182645Sdougb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 446182645Sdougb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 447182645Sdougb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 448182645Sdougb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 449182645Sdougb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 450135446Strhodes */ 451224092Sdougb#ifdef BIND9 452180477Sdougbstatic void 453224092Sdougbdispatch_initrandom(arc4ctx_t *actx, isc_entropy_t *entropy, 454224092Sdougb isc_mutex_t *lock) 455224092Sdougb{ 456180477Sdougb int n; 457180477Sdougb for (n = 0; n < 256; n++) 458180477Sdougb actx->s[n] = n; 459180477Sdougb actx->i = 0; 460180477Sdougb actx->j = 0; 461180477Sdougb actx->count = 0; 462186462Sdougb actx->entropy = entropy; /* don't have to attach */ 463186462Sdougb actx->lock = lock; 464180477Sdougb} 465135446Strhodes 466180477Sdougbstatic void 467180477Sdougbdispatch_arc4addrandom(arc4ctx_t *actx, unsigned char *dat, int datlen) { 468180477Sdougb int n; 469180477Sdougb isc_uint8_t si; 470135446Strhodes 471180477Sdougb actx->i--; 472180477Sdougb for (n = 0; n < 256; n++) { 473180477Sdougb actx->i = (actx->i + 1); 474180477Sdougb si = actx->s[actx->i]; 475180477Sdougb actx->j = (actx->j + si + dat[n % datlen]); 476180477Sdougb actx->s[actx->i] = actx->s[actx->j]; 477180477Sdougb actx->s[actx->j] = si; 478180477Sdougb } 479180477Sdougb actx->j = actx->i; 480135446Strhodes} 481135446Strhodes 482180477Sdougbstatic inline isc_uint8_t 483180477Sdougbdispatch_arc4get8(arc4ctx_t *actx) { 484180477Sdougb isc_uint8_t si, sj; 485180477Sdougb 486180477Sdougb actx->i = (actx->i + 1); 487180477Sdougb si = actx->s[actx->i]; 488180477Sdougb actx->j = (actx->j + si); 489180477Sdougb sj = actx->s[actx->j]; 490180477Sdougb actx->s[actx->i] = sj; 491180477Sdougb actx->s[actx->j] = si; 492180477Sdougb 493180477Sdougb return (actx->s[(si + sj) & 0xff]); 494180477Sdougb} 495180477Sdougb 496180477Sdougbstatic inline isc_uint16_t 497180477Sdougbdispatch_arc4get16(arc4ctx_t *actx) { 498180477Sdougb isc_uint16_t val; 499180477Sdougb 500180477Sdougb val = dispatch_arc4get8(actx) << 8; 501180477Sdougb val |= dispatch_arc4get8(actx); 502180477Sdougb 503180477Sdougb return (val); 504180477Sdougb} 505180477Sdougb 506180477Sdougbstatic void 507186462Sdougbdispatch_arc4stir(arc4ctx_t *actx) { 508180477Sdougb int i; 509180477Sdougb union { 510180477Sdougb unsigned char rnd[128]; 511180477Sdougb isc_uint32_t rnd32[32]; 512180477Sdougb } rnd; 513180477Sdougb isc_result_t result; 514180477Sdougb 515186462Sdougb if (actx->entropy != NULL) { 516180477Sdougb /* 517180477Sdougb * We accept any quality of random data to avoid blocking. 518180477Sdougb */ 519186462Sdougb result = isc_entropy_getdata(actx->entropy, rnd.rnd, 520180477Sdougb sizeof(rnd), NULL, 0); 521180477Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 522180477Sdougb } else { 523180477Sdougb for (i = 0; i < 32; i++) 524180477Sdougb isc_random_get(&rnd.rnd32[i]); 525180477Sdougb } 526186462Sdougb dispatch_arc4addrandom(actx, rnd.rnd, sizeof(rnd.rnd)); 527180477Sdougb 528180477Sdougb /* 529180477Sdougb * Discard early keystream, as per recommendations in: 530180477Sdougb * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps 531180477Sdougb */ 532180477Sdougb for (i = 0; i < 256; i++) 533186462Sdougb (void)dispatch_arc4get8(actx); 534180477Sdougb 535180477Sdougb /* 536180477Sdougb * Derived from OpenBSD's implementation. The rationale is not clear, 537180477Sdougb * but should be conservative enough in safety, and reasonably large 538180477Sdougb * for efficiency. 539180477Sdougb */ 540186462Sdougb actx->count = 1600000; 541180477Sdougb} 542180477Sdougb 543180477Sdougbstatic isc_uint16_t 544224092Sdougbdispatch_random(arc4ctx_t *actx) { 545180477Sdougb isc_uint16_t result; 546180477Sdougb 547186462Sdougb if (actx->lock != NULL) 548186462Sdougb LOCK(actx->lock); 549186462Sdougb 550186462Sdougb actx->count -= sizeof(isc_uint16_t); 551186462Sdougb if (actx->count <= 0) 552186462Sdougb dispatch_arc4stir(actx); 553186462Sdougb result = dispatch_arc4get16(actx); 554186462Sdougb 555186462Sdougb if (actx->lock != NULL) 556186462Sdougb UNLOCK(actx->lock); 557186462Sdougb 558180477Sdougb return (result); 559180477Sdougb} 560224092Sdougb#else 561224092Sdougb/* 562224092Sdougb * For general purpose library, we don't have to be too strict about the 563224092Sdougb * quality of random values. Performance doesn't matter much, either. 564224092Sdougb * So we simply use the isc_random module to keep the library as small as 565224092Sdougb * possible. 566224092Sdougb */ 567180477Sdougb 568224092Sdougbstatic void 569224092Sdougbdispatch_initrandom(arc4ctx_t *actx, isc_entropy_t *entropy, 570224092Sdougb isc_mutex_t *lock) 571224092Sdougb{ 572224092Sdougb UNUSED(actx); 573224092Sdougb UNUSED(entropy); 574224092Sdougb UNUSED(lock); 575224092Sdougb 576224092Sdougb return; 577224092Sdougb} 578224092Sdougb 579180477Sdougbstatic isc_uint16_t 580224092Sdougbdispatch_random(arc4ctx_t *actx) { 581224092Sdougb isc_uint32_t r; 582224092Sdougb 583224092Sdougb UNUSED(actx); 584224092Sdougb 585224092Sdougb isc_random_get(&r); 586224092Sdougb return (r & 0xffff); 587224092Sdougb} 588224092Sdougb#endif /* BIND9 */ 589224092Sdougb 590224092Sdougbstatic isc_uint16_t 591224092Sdougbdispatch_uniformrandom(arc4ctx_t *actx, isc_uint16_t upper_bound) { 592180477Sdougb isc_uint16_t min, r; 593180477Sdougb 594180477Sdougb if (upper_bound < 2) 595180477Sdougb return (0); 596180477Sdougb 597180477Sdougb /* 598180477Sdougb * Ensure the range of random numbers [min, 0xffff] be a multiple of 599180477Sdougb * upper_bound and contain at least a half of the 16 bit range. 600180477Sdougb */ 601180477Sdougb 602180477Sdougb if (upper_bound > 0x8000) 603180477Sdougb min = 1 + ~upper_bound; /* 0x8000 - upper_bound */ 604180477Sdougb else 605180477Sdougb min = (isc_uint16_t)(0x10000 % (isc_uint32_t)upper_bound); 606180477Sdougb 607180477Sdougb /* 608180477Sdougb * This could theoretically loop forever but each retry has 609180477Sdougb * p > 0.5 (worst case, usually far better) of selecting a 610180477Sdougb * number inside the range we need, so it should rarely need 611180477Sdougb * to re-roll. 612180477Sdougb */ 613180477Sdougb for (;;) { 614224092Sdougb r = dispatch_random(actx); 615180477Sdougb if (r >= min) 616180477Sdougb break; 617180477Sdougb } 618180477Sdougb 619180477Sdougb return (r % upper_bound); 620180477Sdougb} 621180477Sdougb 622135446Strhodes/* 623135446Strhodes * Return a hash of the destination and message id. 624135446Strhodes */ 625135446Strhodesstatic isc_uint32_t 626180477Sdougbdns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, 627180477Sdougb in_port_t port) 628180477Sdougb{ 629135446Strhodes unsigned int ret; 630135446Strhodes 631135446Strhodes ret = isc_sockaddr_hash(dest, ISC_TRUE); 632180477Sdougb ret ^= (id << 16) | port; 633135446Strhodes ret %= qid->qid_nbuckets; 634135446Strhodes 635135446Strhodes INSIST(ret < qid->qid_nbuckets); 636135446Strhodes 637135446Strhodes return (ret); 638135446Strhodes} 639135446Strhodes 640135446Strhodes/* 641135446Strhodes * Find the first entry in 'qid'. Returns NULL if there are no entries. 642135446Strhodes */ 643135446Strhodesstatic dns_dispentry_t * 644135446Strhodeslinear_first(dns_qid_t *qid) { 645135446Strhodes dns_dispentry_t *ret; 646135446Strhodes unsigned int bucket; 647135446Strhodes 648135446Strhodes bucket = 0; 649135446Strhodes 650135446Strhodes while (bucket < qid->qid_nbuckets) { 651135446Strhodes ret = ISC_LIST_HEAD(qid->qid_table[bucket]); 652135446Strhodes if (ret != NULL) 653135446Strhodes return (ret); 654135446Strhodes bucket++; 655135446Strhodes } 656135446Strhodes 657135446Strhodes return (NULL); 658135446Strhodes} 659135446Strhodes 660135446Strhodes/* 661135446Strhodes * Find the next entry after 'resp' in 'qid'. Return NULL if there are 662135446Strhodes * no more entries. 663135446Strhodes */ 664135446Strhodesstatic dns_dispentry_t * 665135446Strhodeslinear_next(dns_qid_t *qid, dns_dispentry_t *resp) { 666135446Strhodes dns_dispentry_t *ret; 667135446Strhodes unsigned int bucket; 668135446Strhodes 669135446Strhodes ret = ISC_LIST_NEXT(resp, link); 670135446Strhodes if (ret != NULL) 671135446Strhodes return (ret); 672135446Strhodes 673135446Strhodes bucket = resp->bucket; 674135446Strhodes bucket++; 675135446Strhodes while (bucket < qid->qid_nbuckets) { 676135446Strhodes ret = ISC_LIST_HEAD(qid->qid_table[bucket]); 677135446Strhodes if (ret != NULL) 678135446Strhodes return (ret); 679135446Strhodes bucket++; 680135446Strhodes } 681135446Strhodes 682135446Strhodes return (NULL); 683135446Strhodes} 684135446Strhodes 685135446Strhodes/* 686135446Strhodes * The dispatch must be locked. 687135446Strhodes */ 688135446Strhodesstatic isc_boolean_t 689135446Strhodesdestroy_disp_ok(dns_dispatch_t *disp) 690135446Strhodes{ 691135446Strhodes if (disp->refcount != 0) 692135446Strhodes return (ISC_FALSE); 693135446Strhodes 694135446Strhodes if (disp->recv_pending != 0) 695135446Strhodes return (ISC_FALSE); 696135446Strhodes 697186462Sdougb if (!ISC_LIST_EMPTY(disp->activesockets)) 698186462Sdougb return (ISC_FALSE); 699186462Sdougb 700135446Strhodes if (disp->shutting_down == 0) 701135446Strhodes return (ISC_FALSE); 702135446Strhodes 703135446Strhodes return (ISC_TRUE); 704135446Strhodes} 705135446Strhodes 706135446Strhodes/* 707135446Strhodes * Called when refcount reaches 0 (and safe to destroy). 708135446Strhodes * 709262706Serwin * The dispatcher must be locked. 710262706Serwin * The manager must not be locked. 711135446Strhodes */ 712135446Strhodesstatic void 713135446Strhodesdestroy_disp(isc_task_t *task, isc_event_t *event) { 714135446Strhodes dns_dispatch_t *disp; 715135446Strhodes dns_dispatchmgr_t *mgr; 716135446Strhodes isc_boolean_t killmgr; 717186462Sdougb dispsocket_t *dispsocket; 718186462Sdougb int i; 719135446Strhodes 720135446Strhodes INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL); 721135446Strhodes 722135446Strhodes UNUSED(task); 723135446Strhodes 724135446Strhodes disp = event->ev_arg; 725135446Strhodes mgr = disp->mgr; 726135446Strhodes 727135446Strhodes LOCK(&mgr->lock); 728135446Strhodes ISC_LIST_UNLINK(mgr->list, disp, link); 729135446Strhodes 730135446Strhodes dispatch_log(disp, LVL(90), 731135446Strhodes "shutting down; detaching from sock %p, task %p", 732186462Sdougb disp->socket, disp->task[0]); /* XXXX */ 733135446Strhodes 734254897Serwin if (disp->sepool != NULL) { 735254897Serwin isc_mempool_destroy(&disp->sepool); 736254897Serwin (void)isc_mutex_destroy(&disp->sepool_lock); 737254897Serwin } 738254897Serwin 739186462Sdougb if (disp->socket != NULL) 740186462Sdougb isc_socket_detach(&disp->socket); 741186462Sdougb while ((dispsocket = ISC_LIST_HEAD(disp->inactivesockets)) != NULL) { 742186462Sdougb ISC_LIST_UNLINK(disp->inactivesockets, dispsocket, link); 743186462Sdougb destroy_dispsocket(disp, &dispsocket); 744186462Sdougb } 745186462Sdougb for (i = 0; i < disp->ntasks; i++) 746186462Sdougb isc_task_detach(&disp->task[i]); 747135446Strhodes isc_event_free(&event); 748135446Strhodes 749135446Strhodes dispatch_free(&disp); 750135446Strhodes 751135446Strhodes killmgr = destroy_mgr_ok(mgr); 752135446Strhodes UNLOCK(&mgr->lock); 753135446Strhodes if (killmgr) 754135446Strhodes destroy_mgr(&mgr); 755135446Strhodes} 756135446Strhodes 757186462Sdougb/*% 758193149Sdougb * Manipulate port table per dispatch: find an entry for a given port number, 759193149Sdougb * create a new entry, and decrement a given entry with possible clean-up. 760193149Sdougb */ 761193149Sdougbstatic dispportentry_t * 762193149Sdougbport_search(dns_dispatch_t *disp, in_port_t port) { 763193149Sdougb dispportentry_t *portentry; 764193149Sdougb 765193149Sdougb REQUIRE(disp->port_table != NULL); 766193149Sdougb 767193149Sdougb portentry = ISC_LIST_HEAD(disp->port_table[port % 768193149Sdougb DNS_DISPATCH_PORTTABLESIZE]); 769193149Sdougb while (portentry != NULL) { 770193149Sdougb if (portentry->port == port) 771193149Sdougb return (portentry); 772193149Sdougb portentry = ISC_LIST_NEXT(portentry, link); 773193149Sdougb } 774193149Sdougb 775193149Sdougb return (NULL); 776193149Sdougb} 777193149Sdougb 778193149Sdougbstatic dispportentry_t * 779193149Sdougbnew_portentry(dns_dispatch_t *disp, in_port_t port) { 780193149Sdougb dispportentry_t *portentry; 781262706Serwin dns_qid_t *qid; 782193149Sdougb 783193149Sdougb REQUIRE(disp->port_table != NULL); 784193149Sdougb 785193149Sdougb portentry = isc_mempool_get(disp->portpool); 786193149Sdougb if (portentry == NULL) 787193149Sdougb return (portentry); 788193149Sdougb 789193149Sdougb portentry->port = port; 790262706Serwin portentry->refs = 1; 791193149Sdougb ISC_LINK_INIT(portentry, link); 792262706Serwin qid = DNS_QID(disp); 793262706Serwin LOCK(&qid->lock); 794193149Sdougb ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE], 795193149Sdougb portentry, link); 796262706Serwin UNLOCK(&qid->lock); 797193149Sdougb 798193149Sdougb return (portentry); 799193149Sdougb} 800193149Sdougb 801204619Sdougb/*% 802204619Sdougb * The caller must not hold the qid->lock. 803204619Sdougb */ 804193149Sdougbstatic void 805193149Sdougbderef_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) { 806193149Sdougb dispportentry_t *portentry = *portentryp; 807204619Sdougb dns_qid_t *qid; 808193149Sdougb 809193149Sdougb REQUIRE(disp->port_table != NULL); 810193149Sdougb REQUIRE(portentry != NULL && portentry->refs > 0); 811193149Sdougb 812204619Sdougb qid = DNS_QID(disp); 813204619Sdougb LOCK(&qid->lock); 814193149Sdougb portentry->refs--; 815254897Serwin 816262706Serwin if (portentry->refs == 0) { 817193149Sdougb ISC_LIST_UNLINK(disp->port_table[portentry->port % 818193149Sdougb DNS_DISPATCH_PORTTABLESIZE], 819193149Sdougb portentry, link); 820193149Sdougb isc_mempool_put(disp->portpool, portentry); 821193149Sdougb } 822262706Serwin UNLOCK(&qid->lock); 823193149Sdougb 824193149Sdougb *portentryp = NULL; 825193149Sdougb} 826193149Sdougb 827193149Sdougb/*% 828186462Sdougb * Find a dispsocket for socket address 'dest', and port number 'port'. 829186462Sdougb * Return NULL if no such entry exists. 830186462Sdougb */ 831186462Sdougbstatic dispsocket_t * 832186462Sdougbsocket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port, 833186462Sdougb unsigned int bucket) 834186462Sdougb{ 835186462Sdougb dispsocket_t *dispsock; 836135446Strhodes 837262706Serwin REQUIRE(VALID_QID(qid)); 838186462Sdougb REQUIRE(bucket < qid->qid_nbuckets); 839186462Sdougb 840186462Sdougb dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]); 841186462Sdougb 842186462Sdougb while (dispsock != NULL) { 843204619Sdougb if (dispsock->portentry != NULL && 844204619Sdougb dispsock->portentry->port == port && 845204619Sdougb isc_sockaddr_equal(dest, &dispsock->host)) 846186462Sdougb return (dispsock); 847186462Sdougb dispsock = ISC_LIST_NEXT(dispsock, blink); 848186462Sdougb } 849186462Sdougb 850186462Sdougb return (NULL); 851186462Sdougb} 852186462Sdougb 853186462Sdougb/*% 854186462Sdougb * Make a new socket for a single dispatch with a random port number. 855254897Serwin * The caller must hold the disp->lock 856186462Sdougb */ 857186462Sdougbstatic isc_result_t 858186462Sdougbget_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest, 859254897Serwin isc_socketmgr_t *sockmgr, dispsocket_t **dispsockp, 860254897Serwin in_port_t *portp) 861186462Sdougb{ 862186462Sdougb int i; 863186462Sdougb isc_uint32_t r; 864186462Sdougb dns_dispatchmgr_t *mgr = disp->mgr; 865186462Sdougb isc_socket_t *sock = NULL; 866186462Sdougb isc_result_t result = ISC_R_FAILURE; 867186462Sdougb in_port_t port; 868186462Sdougb isc_sockaddr_t localaddr; 869186462Sdougb unsigned int bucket = 0; 870186462Sdougb dispsocket_t *dispsock; 871186462Sdougb unsigned int nports; 872186462Sdougb in_port_t *ports; 873193149Sdougb unsigned int bindoptions; 874193149Sdougb dispportentry_t *portentry = NULL; 875254897Serwin dns_qid_t *qid; 876186462Sdougb 877186462Sdougb if (isc_sockaddr_pf(&disp->local) == AF_INET) { 878186462Sdougb nports = disp->mgr->nv4ports; 879186462Sdougb ports = disp->mgr->v4ports; 880186462Sdougb } else { 881186462Sdougb nports = disp->mgr->nv6ports; 882186462Sdougb ports = disp->mgr->v6ports; 883186462Sdougb } 884186462Sdougb if (nports == 0) 885186462Sdougb return (ISC_R_ADDRNOTAVAIL); 886186462Sdougb 887186462Sdougb dispsock = ISC_LIST_HEAD(disp->inactivesockets); 888186462Sdougb if (dispsock != NULL) { 889186462Sdougb ISC_LIST_UNLINK(disp->inactivesockets, dispsock, link); 890186462Sdougb sock = dispsock->socket; 891186462Sdougb dispsock->socket = NULL; 892186462Sdougb } else { 893186462Sdougb dispsock = isc_mempool_get(mgr->spool); 894186462Sdougb if (dispsock == NULL) 895186462Sdougb return (ISC_R_NOMEMORY); 896186462Sdougb 897186462Sdougb disp->nsockets++; 898186462Sdougb dispsock->socket = NULL; 899186462Sdougb dispsock->disp = disp; 900186462Sdougb dispsock->resp = NULL; 901193149Sdougb dispsock->portentry = NULL; 902186462Sdougb isc_random_get(&r); 903186462Sdougb dispsock->task = NULL; 904186462Sdougb isc_task_attach(disp->task[r % disp->ntasks], &dispsock->task); 905186462Sdougb ISC_LINK_INIT(dispsock, link); 906186462Sdougb ISC_LINK_INIT(dispsock, blink); 907186462Sdougb dispsock->magic = DISPSOCK_MAGIC; 908186462Sdougb } 909186462Sdougb 910186462Sdougb /* 911186462Sdougb * Pick up a random UDP port and open a new socket with it. Avoid 912186462Sdougb * choosing ports that share the same destination because it will be 913186462Sdougb * very likely to fail in bind(2) or connect(2). 914186462Sdougb */ 915186462Sdougb localaddr = disp->local; 916254897Serwin qid = DNS_QID(disp); 917254897Serwin 918186462Sdougb for (i = 0; i < 64; i++) { 919224092Sdougb port = ports[dispatch_uniformrandom(DISP_ARC4CTX(disp), 920186462Sdougb nports)]; 921186462Sdougb isc_sockaddr_setport(&localaddr, port); 922186462Sdougb 923254897Serwin LOCK(&qid->lock); 924186462Sdougb bucket = dns_hash(qid, dest, 0, port); 925254897Serwin if (socket_search(qid, dest, port, bucket) != NULL) { 926254897Serwin UNLOCK(&qid->lock); 927186462Sdougb continue; 928254897Serwin } 929254897Serwin UNLOCK(&qid->lock); 930193149Sdougb bindoptions = 0; 931193149Sdougb portentry = port_search(disp, port); 932254897Serwin 933193149Sdougb if (portentry != NULL) 934193149Sdougb bindoptions |= ISC_SOCKET_REUSEADDRESS; 935254897Serwin result = open_socket(sockmgr, &localaddr, bindoptions, &sock, 936254897Serwin NULL); 937193149Sdougb if (result == ISC_R_SUCCESS) { 938193149Sdougb if (portentry == NULL) { 939193149Sdougb portentry = new_portentry(disp, port); 940193149Sdougb if (portentry == NULL) { 941193149Sdougb result = ISC_R_NOMEMORY; 942193149Sdougb break; 943193149Sdougb } 944262706Serwin } else { 945262706Serwin LOCK(&qid->lock); 946262706Serwin portentry->refs++; 947262706Serwin UNLOCK(&qid->lock); 948193149Sdougb } 949186462Sdougb break; 950225361Sdougb } else if (result == ISC_R_NOPERM) { 951225361Sdougb char buf[ISC_SOCKADDR_FORMATSIZE]; 952225361Sdougb isc_sockaddr_format(&localaddr, buf, sizeof(buf)); 953225361Sdougb dispatch_log(disp, ISC_LOG_WARNING, 954225361Sdougb "open_socket(%s) -> %s: continuing", 955225361Sdougb buf, isc_result_totext(result)); 956193149Sdougb } else if (result != ISC_R_ADDRINUSE) 957193149Sdougb break; 958186462Sdougb } 959186462Sdougb 960186462Sdougb if (result == ISC_R_SUCCESS) { 961186462Sdougb dispsock->socket = sock; 962186462Sdougb dispsock->host = *dest; 963193149Sdougb dispsock->portentry = portentry; 964186462Sdougb dispsock->bucket = bucket; 965254897Serwin LOCK(&qid->lock); 966186462Sdougb ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink); 967254897Serwin UNLOCK(&qid->lock); 968186462Sdougb *dispsockp = dispsock; 969186462Sdougb *portp = port; 970186462Sdougb } else { 971186462Sdougb /* 972186462Sdougb * We could keep it in the inactive list, but since this should 973186462Sdougb * be an exceptional case and might be resource shortage, we'd 974186462Sdougb * rather destroy it. 975186462Sdougb */ 976186462Sdougb if (sock != NULL) 977186462Sdougb isc_socket_detach(&sock); 978186462Sdougb destroy_dispsocket(disp, &dispsock); 979186462Sdougb } 980186462Sdougb 981186462Sdougb return (result); 982186462Sdougb} 983186462Sdougb 984186462Sdougb/*% 985186462Sdougb * Destroy a dedicated dispatch socket. 986186462Sdougb */ 987186462Sdougbstatic void 988186462Sdougbdestroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) { 989186462Sdougb dispsocket_t *dispsock; 990186462Sdougb dns_qid_t *qid; 991186462Sdougb 992186462Sdougb /* 993186462Sdougb * The dispatch must be locked. 994186462Sdougb */ 995186462Sdougb 996186462Sdougb REQUIRE(dispsockp != NULL && *dispsockp != NULL); 997186462Sdougb dispsock = *dispsockp; 998186462Sdougb REQUIRE(!ISC_LINK_LINKED(dispsock, link)); 999186462Sdougb 1000186462Sdougb disp->nsockets--; 1001186462Sdougb dispsock->magic = 0; 1002193149Sdougb if (dispsock->portentry != NULL) 1003193149Sdougb deref_portentry(disp, &dispsock->portentry); 1004186462Sdougb if (dispsock->socket != NULL) 1005186462Sdougb isc_socket_detach(&dispsock->socket); 1006186462Sdougb if (ISC_LINK_LINKED(dispsock, blink)) { 1007186462Sdougb qid = DNS_QID(disp); 1008186462Sdougb LOCK(&qid->lock); 1009186462Sdougb ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock, 1010186462Sdougb blink); 1011186462Sdougb UNLOCK(&qid->lock); 1012186462Sdougb } 1013186462Sdougb if (dispsock->task != NULL) 1014186462Sdougb isc_task_detach(&dispsock->task); 1015186462Sdougb isc_mempool_put(disp->mgr->spool, dispsock); 1016186462Sdougb 1017186462Sdougb *dispsockp = NULL; 1018186462Sdougb} 1019186462Sdougb 1020186462Sdougb/*% 1021186462Sdougb * Deactivate a dedicated dispatch socket. Move it to the inactive list for 1022186462Sdougb * future reuse unless the total number of sockets are exceeding the maximum. 1023186462Sdougb */ 1024186462Sdougbstatic void 1025186462Sdougbdeactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) { 1026186462Sdougb isc_result_t result; 1027186462Sdougb dns_qid_t *qid; 1028186462Sdougb 1029186462Sdougb /* 1030186462Sdougb * The dispatch must be locked. 1031186462Sdougb */ 1032186462Sdougb ISC_LIST_UNLINK(disp->activesockets, dispsock, link); 1033186462Sdougb if (dispsock->resp != NULL) { 1034186462Sdougb INSIST(dispsock->resp->dispsocket == dispsock); 1035186462Sdougb dispsock->resp->dispsocket = NULL; 1036186462Sdougb } 1037186462Sdougb 1038193149Sdougb INSIST(dispsock->portentry != NULL); 1039193149Sdougb deref_portentry(disp, &dispsock->portentry); 1040193149Sdougb 1041224092Sdougb#ifdef BIND9 1042186462Sdougb if (disp->nsockets > DNS_DISPATCH_POOLSOCKS) 1043186462Sdougb destroy_dispsocket(disp, &dispsock); 1044186462Sdougb else { 1045186462Sdougb result = isc_socket_close(dispsock->socket); 1046186462Sdougb 1047186462Sdougb qid = DNS_QID(disp); 1048186462Sdougb LOCK(&qid->lock); 1049186462Sdougb ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock, 1050186462Sdougb blink); 1051186462Sdougb UNLOCK(&qid->lock); 1052186462Sdougb 1053186462Sdougb if (result == ISC_R_SUCCESS) 1054186462Sdougb ISC_LIST_APPEND(disp->inactivesockets, dispsock, link); 1055186462Sdougb else { 1056186462Sdougb /* 1057186462Sdougb * If the underlying system does not allow this 1058186462Sdougb * optimization, destroy this temporary structure (and 1059186462Sdougb * create a new one for a new transaction). 1060186462Sdougb */ 1061186462Sdougb INSIST(result == ISC_R_NOTIMPLEMENTED); 1062186462Sdougb destroy_dispsocket(disp, &dispsock); 1063186462Sdougb } 1064186462Sdougb } 1065224092Sdougb#else 1066224092Sdougb /* This kind of optimization isn't necessary for normal use */ 1067224092Sdougb UNUSED(qid); 1068224092Sdougb UNUSED(result); 1069224092Sdougb 1070224092Sdougb destroy_dispsocket(disp, &dispsock); 1071224092Sdougb#endif 1072186462Sdougb} 1073186462Sdougb 1074135446Strhodes/* 1075186462Sdougb * Find an entry for query ID 'id', socket address 'dest', and port number 1076186462Sdougb * 'port'. 1077135446Strhodes * Return NULL if no such entry exists. 1078135446Strhodes */ 1079135446Strhodesstatic dns_dispentry_t * 1080186462Sdougbentry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id, 1081186462Sdougb in_port_t port, unsigned int bucket) 1082135446Strhodes{ 1083135446Strhodes dns_dispentry_t *res; 1084135446Strhodes 1085262706Serwin REQUIRE(VALID_QID(qid)); 1086135446Strhodes REQUIRE(bucket < qid->qid_nbuckets); 1087135446Strhodes 1088135446Strhodes res = ISC_LIST_HEAD(qid->qid_table[bucket]); 1089135446Strhodes 1090135446Strhodes while (res != NULL) { 1091186462Sdougb if (res->id == id && isc_sockaddr_equal(dest, &res->host) && 1092180477Sdougb res->port == port) { 1093135446Strhodes return (res); 1094180477Sdougb } 1095135446Strhodes res = ISC_LIST_NEXT(res, link); 1096135446Strhodes } 1097135446Strhodes 1098135446Strhodes return (NULL); 1099135446Strhodes} 1100135446Strhodes 1101135446Strhodesstatic void 1102135446Strhodesfree_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) { 1103254897Serwin isc_mempool_t *bpool; 1104135446Strhodes INSIST(buf != NULL && len != 0); 1105135446Strhodes 1106135446Strhodes 1107135446Strhodes switch (disp->socktype) { 1108135446Strhodes case isc_sockettype_tcp: 1109135446Strhodes INSIST(disp->tcpbuffers > 0); 1110135446Strhodes disp->tcpbuffers--; 1111135446Strhodes isc_mem_put(disp->mgr->mctx, buf, len); 1112135446Strhodes break; 1113135446Strhodes case isc_sockettype_udp: 1114135446Strhodes LOCK(&disp->mgr->buffer_lock); 1115135446Strhodes INSIST(disp->mgr->buffers > 0); 1116135446Strhodes INSIST(len == disp->mgr->buffersize); 1117135446Strhodes disp->mgr->buffers--; 1118254897Serwin bpool = disp->mgr->bpool; 1119135446Strhodes UNLOCK(&disp->mgr->buffer_lock); 1120254897Serwin isc_mempool_put(bpool, buf); 1121135446Strhodes break; 1122135446Strhodes default: 1123135446Strhodes INSIST(0); 1124135446Strhodes break; 1125135446Strhodes } 1126135446Strhodes} 1127135446Strhodes 1128135446Strhodesstatic void * 1129135446Strhodesallocate_udp_buffer(dns_dispatch_t *disp) { 1130254897Serwin isc_mempool_t *bpool; 1131135446Strhodes void *temp; 1132135446Strhodes 1133135446Strhodes LOCK(&disp->mgr->buffer_lock); 1134254897Serwin bpool = disp->mgr->bpool; 1135254897Serwin disp->mgr->buffers++; 1136135446Strhodes UNLOCK(&disp->mgr->buffer_lock); 1137135446Strhodes 1138254897Serwin temp = isc_mempool_get(bpool); 1139254897Serwin 1140254897Serwin if (temp == NULL) { 1141254897Serwin LOCK(&disp->mgr->buffer_lock); 1142254897Serwin disp->mgr->buffers--; 1143254897Serwin UNLOCK(&disp->mgr->buffer_lock); 1144254897Serwin } 1145254897Serwin 1146135446Strhodes return (temp); 1147135446Strhodes} 1148135446Strhodes 1149135446Strhodesstatic inline void 1150254897Serwinfree_sevent(isc_event_t *ev) { 1151254897Serwin isc_mempool_t *pool = ev->ev_destroy_arg; 1152254897Serwin isc_socketevent_t *sev = (isc_socketevent_t *) ev; 1153254897Serwin isc_mempool_put(pool, sev); 1154254897Serwin} 1155254897Serwin 1156254897Serwinstatic inline isc_socketevent_t * 1157254897Serwinallocate_sevent(dns_dispatch_t *disp, isc_socket_t *socket, 1158254897Serwin isc_eventtype_t type, isc_taskaction_t action, const void *arg) 1159254897Serwin{ 1160254897Serwin isc_socketevent_t *ev; 1161254897Serwin void *deconst_arg; 1162254897Serwin 1163254897Serwin ev = isc_mempool_get(disp->sepool); 1164254897Serwin if (ev == NULL) 1165254897Serwin return (NULL); 1166254897Serwin DE_CONST(arg, deconst_arg); 1167254897Serwin ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, type, 1168254897Serwin action, deconst_arg, socket, 1169254897Serwin free_sevent, disp->sepool); 1170254897Serwin ev->result = ISC_R_UNSET; 1171254897Serwin ISC_LINK_INIT(ev, ev_link); 1172254897Serwin ISC_LIST_INIT(ev->bufferlist); 1173254897Serwin ev->region.base = NULL; 1174254897Serwin ev->n = 0; 1175254897Serwin ev->offset = 0; 1176254897Serwin ev->attributes = 0; 1177254897Serwin 1178254897Serwin return (ev); 1179254897Serwin} 1180254897Serwin 1181254897Serwin 1182254897Serwinstatic inline void 1183254897Serwinfree_devent(dns_dispatch_t *disp, dns_dispatchevent_t *ev) { 1184135446Strhodes if (disp->failsafe_ev == ev) { 1185135446Strhodes INSIST(disp->shutdown_out == 1); 1186135446Strhodes disp->shutdown_out = 0; 1187135446Strhodes 1188135446Strhodes return; 1189135446Strhodes } 1190135446Strhodes 1191254897Serwin isc_mempool_put(disp->mgr->depool, ev); 1192135446Strhodes} 1193135446Strhodes 1194135446Strhodesstatic inline dns_dispatchevent_t * 1195254897Serwinallocate_devent(dns_dispatch_t *disp) { 1196135446Strhodes dns_dispatchevent_t *ev; 1197135446Strhodes 1198254897Serwin ev = isc_mempool_get(disp->mgr->depool); 1199135446Strhodes if (ev == NULL) 1200135446Strhodes return (NULL); 1201135446Strhodes ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0, 1202135446Strhodes NULL, NULL, NULL, NULL, NULL); 1203135446Strhodes 1204135446Strhodes return (ev); 1205135446Strhodes} 1206135446Strhodes 1207186462Sdougbstatic void 1208186462Sdougbudp_exrecv(isc_task_t *task, isc_event_t *ev) { 1209186462Sdougb dispsocket_t *dispsock = ev->ev_arg; 1210186462Sdougb 1211186462Sdougb UNUSED(task); 1212186462Sdougb 1213186462Sdougb REQUIRE(VALID_DISPSOCK(dispsock)); 1214186462Sdougb udp_recv(ev, dispsock->disp, dispsock); 1215186462Sdougb} 1216186462Sdougb 1217186462Sdougbstatic void 1218186462Sdougbudp_shrecv(isc_task_t *task, isc_event_t *ev) { 1219186462Sdougb dns_dispatch_t *disp = ev->ev_arg; 1220186462Sdougb 1221186462Sdougb UNUSED(task); 1222186462Sdougb 1223186462Sdougb REQUIRE(VALID_DISPATCH(disp)); 1224186462Sdougb udp_recv(ev, disp, NULL); 1225186462Sdougb} 1226186462Sdougb 1227135446Strhodes/* 1228135446Strhodes * General flow: 1229135446Strhodes * 1230135446Strhodes * If I/O result == CANCELED or error, free the buffer. 1231135446Strhodes * 1232135446Strhodes * If query, free the buffer, restart. 1233135446Strhodes * 1234135446Strhodes * If response: 1235135446Strhodes * Allocate event, fill in details. 1236135446Strhodes * If cannot allocate, free buffer, restart. 1237135446Strhodes * find target. If not found, free buffer, restart. 1238135446Strhodes * if event queue is not empty, queue. else, send. 1239135446Strhodes * restart. 1240135446Strhodes */ 1241135446Strhodesstatic void 1242186462Sdougbudp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) { 1243135446Strhodes isc_socketevent_t *ev = (isc_socketevent_t *)ev_in; 1244135446Strhodes dns_messageid_t id; 1245135446Strhodes isc_result_t dres; 1246135446Strhodes isc_buffer_t source; 1247135446Strhodes unsigned int flags; 1248186462Sdougb dns_dispentry_t *resp = NULL; 1249135446Strhodes dns_dispatchevent_t *rev; 1250135446Strhodes unsigned int bucket; 1251135446Strhodes isc_boolean_t killit; 1252135446Strhodes isc_boolean_t queue_response; 1253135446Strhodes dns_dispatchmgr_t *mgr; 1254135446Strhodes dns_qid_t *qid; 1255135446Strhodes isc_netaddr_t netaddr; 1256135446Strhodes int match; 1257186462Sdougb int result; 1258186462Sdougb isc_boolean_t qidlocked = ISC_FALSE; 1259135446Strhodes 1260135446Strhodes LOCK(&disp->lock); 1261135446Strhodes 1262135446Strhodes mgr = disp->mgr; 1263135446Strhodes qid = mgr->qid; 1264135446Strhodes 1265135446Strhodes dispatch_log(disp, LVL(90), 1266135446Strhodes "got packet: requests %d, buffers %d, recvs %d", 1267135446Strhodes disp->requests, disp->mgr->buffers, disp->recv_pending); 1268135446Strhodes 1269186462Sdougb if (dispsock == NULL && ev->ev_type == ISC_SOCKEVENT_RECVDONE) { 1270135446Strhodes /* 1271135446Strhodes * Unless the receive event was imported from a listening 1272135446Strhodes * interface, in which case the event type is 1273135446Strhodes * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending. 1274135446Strhodes */ 1275135446Strhodes INSIST(disp->recv_pending != 0); 1276135446Strhodes disp->recv_pending = 0; 1277135446Strhodes } 1278135446Strhodes 1279186462Sdougb if (dispsock != NULL && 1280186462Sdougb (ev->result == ISC_R_CANCELED || dispsock->resp == NULL)) { 1281186462Sdougb /* 1282186462Sdougb * dispsock->resp can be NULL if this transaction was canceled 1283186462Sdougb * just after receiving a response. Since this socket is 1284186462Sdougb * exclusively used and there should be at most one receive 1285186462Sdougb * event the canceled event should have been no effect. So 1286186462Sdougb * we can (and should) deactivate the socket right now. 1287186462Sdougb */ 1288186462Sdougb deactivate_dispsocket(disp, dispsock); 1289186462Sdougb dispsock = NULL; 1290186462Sdougb } 1291186462Sdougb 1292135446Strhodes if (disp->shutting_down) { 1293135446Strhodes /* 1294135446Strhodes * This dispatcher is shutting down. 1295135446Strhodes */ 1296135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1297135446Strhodes 1298135446Strhodes isc_event_free(&ev_in); 1299135446Strhodes ev = NULL; 1300135446Strhodes 1301135446Strhodes killit = destroy_disp_ok(disp); 1302135446Strhodes UNLOCK(&disp->lock); 1303135446Strhodes if (killit) 1304186462Sdougb isc_task_send(disp->task[0], &disp->ctlevent); 1305135446Strhodes 1306135446Strhodes return; 1307135446Strhodes } 1308135446Strhodes 1309186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) { 1310186462Sdougb if (dispsock != NULL) { 1311186462Sdougb resp = dispsock->resp; 1312186462Sdougb id = resp->id; 1313186462Sdougb if (ev->result != ISC_R_SUCCESS) { 1314186462Sdougb /* 1315186462Sdougb * This is most likely a network error on a 1316186462Sdougb * connected socket. It makes no sense to 1317186462Sdougb * check the address or parse the packet, but it 1318186462Sdougb * will help to return the error to the caller. 1319186462Sdougb */ 1320186462Sdougb goto sendresponse; 1321186462Sdougb } 1322186462Sdougb } else { 1323186462Sdougb free_buffer(disp, ev->region.base, ev->region.length); 1324186462Sdougb 1325186462Sdougb UNLOCK(&disp->lock); 1326186462Sdougb isc_event_free(&ev_in); 1327186462Sdougb return; 1328186462Sdougb } 1329186462Sdougb } else if (ev->result != ISC_R_SUCCESS) { 1330135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1331135446Strhodes 1332135446Strhodes if (ev->result != ISC_R_CANCELED) 1333135446Strhodes dispatch_log(disp, ISC_LOG_ERROR, 1334135446Strhodes "odd socket result in udp_recv(): %s", 1335135446Strhodes isc_result_totext(ev->result)); 1336135446Strhodes 1337135446Strhodes UNLOCK(&disp->lock); 1338135446Strhodes isc_event_free(&ev_in); 1339135446Strhodes return; 1340135446Strhodes } 1341135446Strhodes 1342135446Strhodes /* 1343135446Strhodes * If this is from a blackholed address, drop it. 1344135446Strhodes */ 1345135446Strhodes isc_netaddr_fromsockaddr(&netaddr, &ev->address); 1346135446Strhodes if (disp->mgr->blackhole != NULL && 1347135446Strhodes dns_acl_match(&netaddr, NULL, disp->mgr->blackhole, 1348186462Sdougb NULL, &match, NULL) == ISC_R_SUCCESS && 1349135446Strhodes match > 0) 1350135446Strhodes { 1351135446Strhodes if (isc_log_wouldlog(dns_lctx, LVL(10))) { 1352135446Strhodes char netaddrstr[ISC_NETADDR_FORMATSIZE]; 1353135446Strhodes isc_netaddr_format(&netaddr, netaddrstr, 1354135446Strhodes sizeof(netaddrstr)); 1355135446Strhodes dispatch_log(disp, LVL(10), 1356135446Strhodes "blackholed packet from %s", 1357135446Strhodes netaddrstr); 1358135446Strhodes } 1359135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1360135446Strhodes goto restart; 1361135446Strhodes } 1362135446Strhodes 1363135446Strhodes /* 1364135446Strhodes * Peek into the buffer to see what we can see. 1365135446Strhodes */ 1366135446Strhodes isc_buffer_init(&source, ev->region.base, ev->region.length); 1367135446Strhodes isc_buffer_add(&source, ev->n); 1368135446Strhodes dres = dns_message_peekheader(&source, &id, &flags); 1369135446Strhodes if (dres != ISC_R_SUCCESS) { 1370135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1371135446Strhodes dispatch_log(disp, LVL(10), "got garbage packet"); 1372135446Strhodes goto restart; 1373135446Strhodes } 1374135446Strhodes 1375135446Strhodes dispatch_log(disp, LVL(92), 1376135446Strhodes "got valid DNS message header, /QR %c, id %u", 1377135446Strhodes ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id); 1378135446Strhodes 1379135446Strhodes /* 1380135446Strhodes * Look at flags. If query, drop it. If response, 1381135446Strhodes * look to see where it goes. 1382135446Strhodes */ 1383135446Strhodes if ((flags & DNS_MESSAGEFLAG_QR) == 0) { 1384135446Strhodes /* query */ 1385135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1386135446Strhodes goto restart; 1387135446Strhodes } 1388135446Strhodes 1389186462Sdougb /* 1390186462Sdougb * Search for the corresponding response. If we are using an exclusive 1391186462Sdougb * socket, we've already identified it and we can skip the search; but 1392186462Sdougb * the ID and the address must match the expected ones. 1393186462Sdougb */ 1394186462Sdougb if (resp == NULL) { 1395186462Sdougb bucket = dns_hash(qid, &ev->address, id, disp->localport); 1396186462Sdougb LOCK(&qid->lock); 1397186462Sdougb qidlocked = ISC_TRUE; 1398186462Sdougb resp = entry_search(qid, &ev->address, id, disp->localport, 1399186462Sdougb bucket); 1400186462Sdougb dispatch_log(disp, LVL(90), 1401186462Sdougb "search for response in bucket %d: %s", 1402186462Sdougb bucket, (resp == NULL ? "not found" : "found")); 1403135446Strhodes 1404186462Sdougb if (resp == NULL) { 1405193149Sdougb inc_stats(mgr, dns_resstatscounter_mismatch); 1406186462Sdougb free_buffer(disp, ev->region.base, ev->region.length); 1407186462Sdougb goto unlock; 1408186462Sdougb } 1409186462Sdougb } else if (resp->id != id || !isc_sockaddr_equal(&ev->address, 1410186462Sdougb &resp->host)) { 1411186462Sdougb dispatch_log(disp, LVL(90), 1412186462Sdougb "response to an exclusive socket doesn't match"); 1413193149Sdougb inc_stats(mgr, dns_resstatscounter_mismatch); 1414135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1415135446Strhodes goto unlock; 1416186462Sdougb } 1417165071Sdougb 1418165071Sdougb /* 1419165071Sdougb * Now that we have the original dispatch the query was sent 1420165071Sdougb * from check that the address and port the response was 1421165071Sdougb * sent to make sense. 1422165071Sdougb */ 1423165071Sdougb if (disp != resp->disp) { 1424165071Sdougb isc_sockaddr_t a1; 1425165071Sdougb isc_sockaddr_t a2; 1426186462Sdougb 1427165071Sdougb /* 1428165071Sdougb * Check that the socket types and ports match. 1429165071Sdougb */ 1430165071Sdougb if (disp->socktype != resp->disp->socktype || 1431165071Sdougb isc_sockaddr_getport(&disp->local) != 1432165071Sdougb isc_sockaddr_getport(&resp->disp->local)) { 1433165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 1434165071Sdougb goto unlock; 1435165071Sdougb } 1436165071Sdougb 1437165071Sdougb /* 1438262706Serwin * If each dispatch is bound to a different address 1439262706Serwin * then fail. 1440165071Sdougb * 1441165071Sdougb * Note under Linux a packet can be sent out via IPv4 socket 1442165071Sdougb * and the response be received via a IPv6 socket. 1443186462Sdougb * 1444165071Sdougb * Requests sent out via IPv6 should always come back in 1445165071Sdougb * via IPv6. 1446165071Sdougb */ 1447165071Sdougb if (isc_sockaddr_pf(&resp->disp->local) == PF_INET6 && 1448165071Sdougb isc_sockaddr_pf(&disp->local) != PF_INET6) { 1449165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 1450165071Sdougb goto unlock; 1451165071Sdougb } 1452165071Sdougb isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local)); 1453165071Sdougb isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local)); 1454262706Serwin if (!isc_sockaddr_eqaddr(&disp->local, &resp->disp->local) && 1455262706Serwin !isc_sockaddr_eqaddr(&a1, &resp->disp->local) && 1456165071Sdougb !isc_sockaddr_eqaddr(&a2, &disp->local)) { 1457165071Sdougb free_buffer(disp, ev->region.base, ev->region.length); 1458165071Sdougb goto unlock; 1459165071Sdougb } 1460165071Sdougb } 1461165071Sdougb 1462186462Sdougb sendresponse: 1463135446Strhodes queue_response = resp->item_out; 1464254897Serwin rev = allocate_devent(resp->disp); 1465135446Strhodes if (rev == NULL) { 1466135446Strhodes free_buffer(disp, ev->region.base, ev->region.length); 1467135446Strhodes goto unlock; 1468135446Strhodes } 1469135446Strhodes 1470135446Strhodes /* 1471135446Strhodes * At this point, rev contains the event we want to fill in, and 1472135446Strhodes * resp contains the information on the place to send it to. 1473135446Strhodes * Send the event off. 1474135446Strhodes */ 1475135446Strhodes isc_buffer_init(&rev->buffer, ev->region.base, ev->region.length); 1476135446Strhodes isc_buffer_add(&rev->buffer, ev->n); 1477186462Sdougb rev->result = ev->result; 1478135446Strhodes rev->id = id; 1479135446Strhodes rev->addr = ev->address; 1480135446Strhodes rev->pktinfo = ev->pktinfo; 1481135446Strhodes rev->attributes = ev->attributes; 1482135446Strhodes if (queue_response) { 1483135446Strhodes ISC_LIST_APPEND(resp->items, rev, ev_link); 1484135446Strhodes } else { 1485135446Strhodes ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, 1486135446Strhodes DNS_EVENT_DISPATCH, 1487135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 1488135446Strhodes request_log(disp, resp, LVL(90), 1489135446Strhodes "[a] Sent event %p buffer %p len %d to task %p", 1490135446Strhodes rev, rev->buffer.base, rev->buffer.length, 1491135446Strhodes resp->task); 1492135446Strhodes resp->item_out = ISC_TRUE; 1493135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&rev)); 1494135446Strhodes } 1495135446Strhodes unlock: 1496186462Sdougb if (qidlocked) 1497186462Sdougb UNLOCK(&qid->lock); 1498135446Strhodes 1499135446Strhodes /* 1500135446Strhodes * Restart recv() to get the next packet. 1501135446Strhodes */ 1502135446Strhodes restart: 1503186462Sdougb result = startrecv(disp, dispsock); 1504186462Sdougb if (result != ISC_R_SUCCESS && dispsock != NULL) { 1505186462Sdougb /* 1506186462Sdougb * XXX: wired. There seems to be no recovery process other than 1507186462Sdougb * deactivate this socket anyway (since we cannot start 1508186462Sdougb * receiving, we won't be able to receive a cancel event 1509186462Sdougb * from the user). 1510186462Sdougb */ 1511186462Sdougb deactivate_dispsocket(disp, dispsock); 1512186462Sdougb } 1513135446Strhodes UNLOCK(&disp->lock); 1514135446Strhodes 1515135446Strhodes isc_event_free(&ev_in); 1516135446Strhodes} 1517135446Strhodes 1518135446Strhodes/* 1519135446Strhodes * General flow: 1520135446Strhodes * 1521135446Strhodes * If I/O result == CANCELED, EOF, or error, notify everyone as the 1522135446Strhodes * various queues drain. 1523135446Strhodes * 1524135446Strhodes * If query, restart. 1525135446Strhodes * 1526135446Strhodes * If response: 1527135446Strhodes * Allocate event, fill in details. 1528135446Strhodes * If cannot allocate, restart. 1529135446Strhodes * find target. If not found, restart. 1530135446Strhodes * if event queue is not empty, queue. else, send. 1531135446Strhodes * restart. 1532135446Strhodes */ 1533135446Strhodesstatic void 1534135446Strhodestcp_recv(isc_task_t *task, isc_event_t *ev_in) { 1535135446Strhodes dns_dispatch_t *disp = ev_in->ev_arg; 1536135446Strhodes dns_tcpmsg_t *tcpmsg = &disp->tcpmsg; 1537135446Strhodes dns_messageid_t id; 1538135446Strhodes isc_result_t dres; 1539135446Strhodes unsigned int flags; 1540135446Strhodes dns_dispentry_t *resp; 1541135446Strhodes dns_dispatchevent_t *rev; 1542135446Strhodes unsigned int bucket; 1543135446Strhodes isc_boolean_t killit; 1544135446Strhodes isc_boolean_t queue_response; 1545135446Strhodes dns_qid_t *qid; 1546135446Strhodes int level; 1547135446Strhodes char buf[ISC_SOCKADDR_FORMATSIZE]; 1548135446Strhodes 1549135446Strhodes UNUSED(task); 1550135446Strhodes 1551135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 1552135446Strhodes 1553135446Strhodes qid = disp->qid; 1554135446Strhodes 1555135446Strhodes dispatch_log(disp, LVL(90), 1556135446Strhodes "got TCP packet: requests %d, buffers %d, recvs %d", 1557135446Strhodes disp->requests, disp->tcpbuffers, disp->recv_pending); 1558135446Strhodes 1559135446Strhodes LOCK(&disp->lock); 1560135446Strhodes 1561135446Strhodes INSIST(disp->recv_pending != 0); 1562135446Strhodes disp->recv_pending = 0; 1563135446Strhodes 1564135446Strhodes if (disp->refcount == 0) { 1565135446Strhodes /* 1566135446Strhodes * This dispatcher is shutting down. Force cancelation. 1567135446Strhodes */ 1568135446Strhodes tcpmsg->result = ISC_R_CANCELED; 1569135446Strhodes } 1570135446Strhodes 1571135446Strhodes if (tcpmsg->result != ISC_R_SUCCESS) { 1572135446Strhodes switch (tcpmsg->result) { 1573135446Strhodes case ISC_R_CANCELED: 1574135446Strhodes break; 1575186462Sdougb 1576135446Strhodes case ISC_R_EOF: 1577135446Strhodes dispatch_log(disp, LVL(90), "shutting down on EOF"); 1578135446Strhodes do_cancel(disp); 1579135446Strhodes break; 1580135446Strhodes 1581135446Strhodes case ISC_R_CONNECTIONRESET: 1582135446Strhodes level = ISC_LOG_INFO; 1583135446Strhodes goto logit; 1584135446Strhodes 1585135446Strhodes default: 1586135446Strhodes level = ISC_LOG_ERROR; 1587135446Strhodes logit: 1588135446Strhodes isc_sockaddr_format(&tcpmsg->address, buf, sizeof(buf)); 1589135446Strhodes dispatch_log(disp, level, "shutting down due to TCP " 1590135446Strhodes "receive error: %s: %s", buf, 1591135446Strhodes isc_result_totext(tcpmsg->result)); 1592135446Strhodes do_cancel(disp); 1593135446Strhodes break; 1594135446Strhodes } 1595135446Strhodes 1596135446Strhodes /* 1597135446Strhodes * The event is statically allocated in the tcpmsg 1598135446Strhodes * structure, and destroy_disp() frees the tcpmsg, so we must 1599135446Strhodes * free the event *before* calling destroy_disp(). 1600135446Strhodes */ 1601135446Strhodes isc_event_free(&ev_in); 1602135446Strhodes 1603135446Strhodes disp->shutting_down = 1; 1604135446Strhodes disp->shutdown_why = tcpmsg->result; 1605135446Strhodes 1606135446Strhodes /* 1607135446Strhodes * If the recv() was canceled pass the word on. 1608135446Strhodes */ 1609135446Strhodes killit = destroy_disp_ok(disp); 1610135446Strhodes UNLOCK(&disp->lock); 1611135446Strhodes if (killit) 1612186462Sdougb isc_task_send(disp->task[0], &disp->ctlevent); 1613135446Strhodes return; 1614135446Strhodes } 1615135446Strhodes 1616135446Strhodes dispatch_log(disp, LVL(90), "result %d, length == %d, addr = %p", 1617135446Strhodes tcpmsg->result, 1618135446Strhodes tcpmsg->buffer.length, tcpmsg->buffer.base); 1619135446Strhodes 1620135446Strhodes /* 1621135446Strhodes * Peek into the buffer to see what we can see. 1622135446Strhodes */ 1623135446Strhodes dres = dns_message_peekheader(&tcpmsg->buffer, &id, &flags); 1624135446Strhodes if (dres != ISC_R_SUCCESS) { 1625135446Strhodes dispatch_log(disp, LVL(10), "got garbage packet"); 1626135446Strhodes goto restart; 1627135446Strhodes } 1628135446Strhodes 1629135446Strhodes dispatch_log(disp, LVL(92), 1630135446Strhodes "got valid DNS message header, /QR %c, id %u", 1631135446Strhodes ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id); 1632135446Strhodes 1633135446Strhodes /* 1634135446Strhodes * Allocate an event to send to the query or response client, and 1635135446Strhodes * allocate a new buffer for our use. 1636135446Strhodes */ 1637135446Strhodes 1638135446Strhodes /* 1639135446Strhodes * Look at flags. If query, drop it. If response, 1640135446Strhodes * look to see where it goes. 1641135446Strhodes */ 1642135446Strhodes if ((flags & DNS_MESSAGEFLAG_QR) == 0) { 1643135446Strhodes /* 1644135446Strhodes * Query. 1645135446Strhodes */ 1646135446Strhodes goto restart; 1647135446Strhodes } 1648135446Strhodes 1649135446Strhodes /* 1650135446Strhodes * Response. 1651135446Strhodes */ 1652180477Sdougb bucket = dns_hash(qid, &tcpmsg->address, id, disp->localport); 1653135446Strhodes LOCK(&qid->lock); 1654186462Sdougb resp = entry_search(qid, &tcpmsg->address, id, disp->localport, bucket); 1655135446Strhodes dispatch_log(disp, LVL(90), 1656135446Strhodes "search for response in bucket %d: %s", 1657135446Strhodes bucket, (resp == NULL ? "not found" : "found")); 1658135446Strhodes 1659135446Strhodes if (resp == NULL) 1660135446Strhodes goto unlock; 1661135446Strhodes queue_response = resp->item_out; 1662254897Serwin rev = allocate_devent(disp); 1663135446Strhodes if (rev == NULL) 1664135446Strhodes goto unlock; 1665135446Strhodes 1666135446Strhodes /* 1667135446Strhodes * At this point, rev contains the event we want to fill in, and 1668135446Strhodes * resp contains the information on the place to send it to. 1669135446Strhodes * Send the event off. 1670135446Strhodes */ 1671135446Strhodes dns_tcpmsg_keepbuffer(tcpmsg, &rev->buffer); 1672135446Strhodes disp->tcpbuffers++; 1673135446Strhodes rev->result = ISC_R_SUCCESS; 1674135446Strhodes rev->id = id; 1675135446Strhodes rev->addr = tcpmsg->address; 1676135446Strhodes if (queue_response) { 1677135446Strhodes ISC_LIST_APPEND(resp->items, rev, ev_link); 1678135446Strhodes } else { 1679135446Strhodes ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, DNS_EVENT_DISPATCH, 1680135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 1681135446Strhodes request_log(disp, resp, LVL(90), 1682135446Strhodes "[b] Sent event %p buffer %p len %d to task %p", 1683135446Strhodes rev, rev->buffer.base, rev->buffer.length, 1684135446Strhodes resp->task); 1685135446Strhodes resp->item_out = ISC_TRUE; 1686135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&rev)); 1687135446Strhodes } 1688135446Strhodes unlock: 1689135446Strhodes UNLOCK(&qid->lock); 1690135446Strhodes 1691135446Strhodes /* 1692135446Strhodes * Restart recv() to get the next packet. 1693135446Strhodes */ 1694135446Strhodes restart: 1695186462Sdougb (void)startrecv(disp, NULL); 1696135446Strhodes 1697135446Strhodes UNLOCK(&disp->lock); 1698135446Strhodes 1699135446Strhodes isc_event_free(&ev_in); 1700135446Strhodes} 1701135446Strhodes 1702135446Strhodes/* 1703135446Strhodes * disp must be locked. 1704135446Strhodes */ 1705186462Sdougbstatic isc_result_t 1706186462Sdougbstartrecv(dns_dispatch_t *disp, dispsocket_t *dispsock) { 1707135446Strhodes isc_result_t res; 1708135446Strhodes isc_region_t region; 1709186462Sdougb isc_socket_t *socket; 1710135446Strhodes 1711135446Strhodes if (disp->shutting_down == 1) 1712186462Sdougb return (ISC_R_SUCCESS); 1713135446Strhodes 1714135446Strhodes if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) 1715186462Sdougb return (ISC_R_SUCCESS); 1716135446Strhodes 1717186462Sdougb if (disp->recv_pending != 0 && dispsock == NULL) 1718186462Sdougb return (ISC_R_SUCCESS); 1719135446Strhodes 1720135446Strhodes if (disp->mgr->buffers >= disp->mgr->maxbuffers) 1721186462Sdougb return (ISC_R_NOMEMORY); 1722135446Strhodes 1723186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 && 1724186462Sdougb dispsock == NULL) 1725186462Sdougb return (ISC_R_SUCCESS); 1726186462Sdougb 1727186462Sdougb if (dispsock != NULL) 1728186462Sdougb socket = dispsock->socket; 1729186462Sdougb else 1730186462Sdougb socket = disp->socket; 1731186462Sdougb INSIST(socket != NULL); 1732186462Sdougb 1733135446Strhodes switch (disp->socktype) { 1734135446Strhodes /* 1735135446Strhodes * UDP reads are always maximal. 1736135446Strhodes */ 1737135446Strhodes case isc_sockettype_udp: 1738135446Strhodes region.length = disp->mgr->buffersize; 1739135446Strhodes region.base = allocate_udp_buffer(disp); 1740135446Strhodes if (region.base == NULL) 1741186462Sdougb return (ISC_R_NOMEMORY); 1742186462Sdougb if (dispsock != NULL) { 1743254897Serwin isc_task_t *dt = dispsock->task; 1744254897Serwin isc_socketevent_t *sev = 1745254897Serwin allocate_sevent(disp, socket, 1746254897Serwin ISC_SOCKEVENT_RECVDONE, 1747254897Serwin udp_exrecv, dispsock); 1748254897Serwin if (sev == NULL) { 1749254897Serwin free_buffer(disp, region.base, region.length); 1750254897Serwin return (ISC_R_NOMEMORY); 1751254897Serwin } 1752254897Serwin 1753254897Serwin res = isc_socket_recv2(socket, ®ion, 1, dt, sev, 0); 1754186462Sdougb if (res != ISC_R_SUCCESS) { 1755186462Sdougb free_buffer(disp, region.base, region.length); 1756186462Sdougb return (res); 1757186462Sdougb } 1758186462Sdougb } else { 1759254897Serwin isc_task_t *dt = disp->task[0]; 1760254897Serwin isc_socketevent_t *sev = 1761254897Serwin allocate_sevent(disp, socket, 1762254897Serwin ISC_SOCKEVENT_RECVDONE, 1763254897Serwin udp_shrecv, disp); 1764254897Serwin if (sev == NULL) { 1765254897Serwin free_buffer(disp, region.base, region.length); 1766254897Serwin return (ISC_R_NOMEMORY); 1767254897Serwin } 1768254897Serwin 1769254897Serwin res = isc_socket_recv2(socket, ®ion, 1, dt, sev, 0); 1770186462Sdougb if (res != ISC_R_SUCCESS) { 1771186462Sdougb free_buffer(disp, region.base, region.length); 1772186462Sdougb disp->shutdown_why = res; 1773186462Sdougb disp->shutting_down = 1; 1774186462Sdougb do_cancel(disp); 1775186462Sdougb return (ISC_R_SUCCESS); /* recover by cancel */ 1776186462Sdougb } 1777186462Sdougb INSIST(disp->recv_pending == 0); 1778186462Sdougb disp->recv_pending = 1; 1779135446Strhodes } 1780135446Strhodes break; 1781135446Strhodes 1782135446Strhodes case isc_sockettype_tcp: 1783186462Sdougb res = dns_tcpmsg_readmessage(&disp->tcpmsg, disp->task[0], 1784135446Strhodes tcp_recv, disp); 1785135446Strhodes if (res != ISC_R_SUCCESS) { 1786135446Strhodes disp->shutdown_why = res; 1787135446Strhodes disp->shutting_down = 1; 1788135446Strhodes do_cancel(disp); 1789186462Sdougb return (ISC_R_SUCCESS); /* recover by cancel */ 1790135446Strhodes } 1791135446Strhodes INSIST(disp->recv_pending == 0); 1792135446Strhodes disp->recv_pending = 1; 1793135446Strhodes break; 1794170222Sdougb default: 1795170222Sdougb INSIST(0); 1796170222Sdougb break; 1797135446Strhodes } 1798186462Sdougb 1799186462Sdougb return (ISC_R_SUCCESS); 1800135446Strhodes} 1801135446Strhodes 1802135446Strhodes/* 1803135446Strhodes * Mgr must be locked when calling this function. 1804135446Strhodes */ 1805135446Strhodesstatic isc_boolean_t 1806135446Strhodesdestroy_mgr_ok(dns_dispatchmgr_t *mgr) { 1807135446Strhodes mgr_log(mgr, LVL(90), 1808135446Strhodes "destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, " 1809254897Serwin "depool=%d, rpool=%d, dpool=%d", 1810135446Strhodes MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list), 1811254897Serwin isc_mempool_getallocated(mgr->depool), 1812135446Strhodes isc_mempool_getallocated(mgr->rpool), 1813135446Strhodes isc_mempool_getallocated(mgr->dpool)); 1814135446Strhodes if (!MGR_IS_SHUTTINGDOWN(mgr)) 1815135446Strhodes return (ISC_FALSE); 1816135446Strhodes if (!ISC_LIST_EMPTY(mgr->list)) 1817135446Strhodes return (ISC_FALSE); 1818254897Serwin if (isc_mempool_getallocated(mgr->depool) != 0) 1819135446Strhodes return (ISC_FALSE); 1820135446Strhodes if (isc_mempool_getallocated(mgr->rpool) != 0) 1821135446Strhodes return (ISC_FALSE); 1822135446Strhodes if (isc_mempool_getallocated(mgr->dpool) != 0) 1823135446Strhodes return (ISC_FALSE); 1824135446Strhodes 1825135446Strhodes return (ISC_TRUE); 1826135446Strhodes} 1827135446Strhodes 1828135446Strhodes/* 1829135446Strhodes * Mgr must be unlocked when calling this function. 1830135446Strhodes */ 1831135446Strhodesstatic void 1832135446Strhodesdestroy_mgr(dns_dispatchmgr_t **mgrp) { 1833135446Strhodes isc_mem_t *mctx; 1834135446Strhodes dns_dispatchmgr_t *mgr; 1835135446Strhodes 1836135446Strhodes mgr = *mgrp; 1837135446Strhodes *mgrp = NULL; 1838135446Strhodes 1839135446Strhodes mctx = mgr->mctx; 1840135446Strhodes 1841135446Strhodes mgr->magic = 0; 1842135446Strhodes mgr->mctx = NULL; 1843135446Strhodes DESTROYLOCK(&mgr->lock); 1844135446Strhodes mgr->state = 0; 1845135446Strhodes 1846180477Sdougb DESTROYLOCK(&mgr->arc4_lock); 1847180477Sdougb 1848254897Serwin isc_mempool_destroy(&mgr->depool); 1849135446Strhodes isc_mempool_destroy(&mgr->rpool); 1850135446Strhodes isc_mempool_destroy(&mgr->dpool); 1851224092Sdougb if (mgr->bpool != NULL) 1852224092Sdougb isc_mempool_destroy(&mgr->bpool); 1853224092Sdougb if (mgr->spool != NULL) 1854224092Sdougb isc_mempool_destroy(&mgr->spool); 1855135446Strhodes 1856254897Serwin DESTROYLOCK(&mgr->spool_lock); 1857254897Serwin DESTROYLOCK(&mgr->bpool_lock); 1858254897Serwin DESTROYLOCK(&mgr->dpool_lock); 1859254897Serwin DESTROYLOCK(&mgr->rpool_lock); 1860254897Serwin DESTROYLOCK(&mgr->depool_lock); 1861135446Strhodes 1862224092Sdougb#ifdef BIND9 1863135446Strhodes if (mgr->entropy != NULL) 1864135446Strhodes isc_entropy_detach(&mgr->entropy); 1865224092Sdougb#endif /* BIND9 */ 1866135446Strhodes if (mgr->qid != NULL) 1867135446Strhodes qid_destroy(mctx, &mgr->qid); 1868135446Strhodes 1869135446Strhodes DESTROYLOCK(&mgr->buffer_lock); 1870135446Strhodes 1871135446Strhodes if (mgr->blackhole != NULL) 1872135446Strhodes dns_acl_detach(&mgr->blackhole); 1873135446Strhodes 1874193149Sdougb if (mgr->stats != NULL) 1875193149Sdougb isc_stats_detach(&mgr->stats); 1876193149Sdougb 1877186462Sdougb if (mgr->v4ports != NULL) { 1878186462Sdougb isc_mem_put(mctx, mgr->v4ports, 1879186462Sdougb mgr->nv4ports * sizeof(in_port_t)); 1880186462Sdougb } 1881186462Sdougb if (mgr->v6ports != NULL) { 1882186462Sdougb isc_mem_put(mctx, mgr->v6ports, 1883186462Sdougb mgr->nv6ports * sizeof(in_port_t)); 1884186462Sdougb } 1885135446Strhodes isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); 1886135446Strhodes isc_mem_detach(&mctx); 1887135446Strhodes} 1888135446Strhodes 1889135446Strhodesstatic isc_result_t 1890186462Sdougbopen_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local, 1891254897Serwin unsigned int options, isc_socket_t **sockp, 1892254897Serwin isc_socket_t *dup_socket) 1893135446Strhodes{ 1894135446Strhodes isc_socket_t *sock; 1895135446Strhodes isc_result_t result; 1896135446Strhodes 1897186462Sdougb sock = *sockp; 1898254897Serwin if (sock != NULL) { 1899224092Sdougb#ifdef BIND9 1900186462Sdougb result = isc_socket_open(sock); 1901186462Sdougb if (result != ISC_R_SUCCESS) 1902186462Sdougb return (result); 1903224092Sdougb#else 1904224092Sdougb INSIST(0); 1905224092Sdougb#endif 1906254897Serwin } else if (dup_socket != NULL) { 1907254897Serwin result = isc_socket_dup(dup_socket, &sock); 1908254897Serwin if (result != ISC_R_SUCCESS) 1909254897Serwin return (result); 1910254897Serwin 1911254897Serwin isc_socket_setname(sock, "dispatcher", NULL); 1912254897Serwin *sockp = sock; 1913254897Serwin return (ISC_R_SUCCESS); 1914254897Serwin } else { 1915254897Serwin result = isc_socket_create(mgr, isc_sockaddr_pf(local), 1916254897Serwin isc_sockettype_udp, &sock); 1917254897Serwin if (result != ISC_R_SUCCESS) 1918254897Serwin return (result); 1919186462Sdougb } 1920135446Strhodes 1921254897Serwin isc_socket_setname(sock, "dispatcher", NULL); 1922254897Serwin 1923135446Strhodes#ifndef ISC_ALLOW_MAPPED 1924135446Strhodes isc_socket_ipv6only(sock, ISC_TRUE); 1925135446Strhodes#endif 1926182645Sdougb result = isc_socket_bind(sock, local, options); 1927135446Strhodes if (result != ISC_R_SUCCESS) { 1928186462Sdougb if (*sockp == NULL) 1929186462Sdougb isc_socket_detach(&sock); 1930224092Sdougb else { 1931224092Sdougb#ifdef BIND9 1932186462Sdougb isc_socket_close(sock); 1933224092Sdougb#else 1934224092Sdougb INSIST(0); 1935224092Sdougb#endif 1936224092Sdougb } 1937135446Strhodes return (result); 1938135446Strhodes } 1939135446Strhodes 1940135446Strhodes *sockp = sock; 1941135446Strhodes return (ISC_R_SUCCESS); 1942135446Strhodes} 1943135446Strhodes 1944186462Sdougb/*% 1945186462Sdougb * Create a temporary port list to set the initial default set of dispatch 1946186462Sdougb * ports: [1024, 65535]. This is almost meaningless as the application will 1947186462Sdougb * normally set the ports explicitly, but is provided to fill some minor corner 1948186462Sdougb * cases. 1949186462Sdougb */ 1950186462Sdougbstatic isc_result_t 1951186462Sdougbcreate_default_portset(isc_mem_t *mctx, isc_portset_t **portsetp) { 1952186462Sdougb isc_result_t result; 1953186462Sdougb 1954186462Sdougb result = isc_portset_create(mctx, portsetp); 1955186462Sdougb if (result != ISC_R_SUCCESS) 1956186462Sdougb return (result); 1957186462Sdougb isc_portset_addrange(*portsetp, 1024, 65535); 1958186462Sdougb 1959186462Sdougb return (ISC_R_SUCCESS); 1960186462Sdougb} 1961186462Sdougb 1962135446Strhodes/* 1963135446Strhodes * Publics. 1964135446Strhodes */ 1965135446Strhodes 1966135446Strhodesisc_result_t 1967135446Strhodesdns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy, 1968135446Strhodes dns_dispatchmgr_t **mgrp) 1969135446Strhodes{ 1970135446Strhodes dns_dispatchmgr_t *mgr; 1971135446Strhodes isc_result_t result; 1972186462Sdougb isc_portset_t *v4portset = NULL; 1973186462Sdougb isc_portset_t *v6portset = NULL; 1974135446Strhodes 1975135446Strhodes REQUIRE(mctx != NULL); 1976135446Strhodes REQUIRE(mgrp != NULL && *mgrp == NULL); 1977135446Strhodes 1978135446Strhodes mgr = isc_mem_get(mctx, sizeof(dns_dispatchmgr_t)); 1979135446Strhodes if (mgr == NULL) 1980135446Strhodes return (ISC_R_NOMEMORY); 1981135446Strhodes 1982135446Strhodes mgr->mctx = NULL; 1983135446Strhodes isc_mem_attach(mctx, &mgr->mctx); 1984135446Strhodes 1985135446Strhodes mgr->blackhole = NULL; 1986193149Sdougb mgr->stats = NULL; 1987135446Strhodes 1988135446Strhodes result = isc_mutex_init(&mgr->lock); 1989135446Strhodes if (result != ISC_R_SUCCESS) 1990135446Strhodes goto deallocate; 1991135446Strhodes 1992180477Sdougb result = isc_mutex_init(&mgr->arc4_lock); 1993135446Strhodes if (result != ISC_R_SUCCESS) 1994135446Strhodes goto kill_lock; 1995135446Strhodes 1996180477Sdougb result = isc_mutex_init(&mgr->buffer_lock); 1997180477Sdougb if (result != ISC_R_SUCCESS) 1998180477Sdougb goto kill_arc4_lock; 1999180477Sdougb 2000254897Serwin result = isc_mutex_init(&mgr->depool_lock); 2001135446Strhodes if (result != ISC_R_SUCCESS) 2002135446Strhodes goto kill_buffer_lock; 2003135446Strhodes 2004254897Serwin result = isc_mutex_init(&mgr->rpool_lock); 2005254897Serwin if (result != ISC_R_SUCCESS) 2006254897Serwin goto kill_depool_lock; 2007254897Serwin 2008254897Serwin result = isc_mutex_init(&mgr->dpool_lock); 2009254897Serwin if (result != ISC_R_SUCCESS) 2010254897Serwin goto kill_rpool_lock; 2011254897Serwin 2012254897Serwin result = isc_mutex_init(&mgr->bpool_lock); 2013254897Serwin if (result != ISC_R_SUCCESS) 2014254897Serwin goto kill_dpool_lock; 2015254897Serwin 2016254897Serwin result = isc_mutex_init(&mgr->spool_lock); 2017254897Serwin if (result != ISC_R_SUCCESS) 2018254897Serwin goto kill_bpool_lock; 2019254897Serwin 2020254897Serwin mgr->depool = NULL; 2021135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t), 2022254897Serwin &mgr->depool) != ISC_R_SUCCESS) { 2023135446Strhodes result = ISC_R_NOMEMORY; 2024254897Serwin goto kill_spool_lock; 2025135446Strhodes } 2026135446Strhodes 2027135446Strhodes mgr->rpool = NULL; 2028135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispentry_t), 2029135446Strhodes &mgr->rpool) != ISC_R_SUCCESS) { 2030135446Strhodes result = ISC_R_NOMEMORY; 2031254897Serwin goto kill_depool; 2032135446Strhodes } 2033135446Strhodes 2034135446Strhodes mgr->dpool = NULL; 2035135446Strhodes if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatch_t), 2036135446Strhodes &mgr->dpool) != ISC_R_SUCCESS) { 2037135446Strhodes result = ISC_R_NOMEMORY; 2038135446Strhodes goto kill_rpool; 2039135446Strhodes } 2040135446Strhodes 2041254897Serwin isc_mempool_setname(mgr->depool, "dispmgr_depool"); 2042254897Serwin isc_mempool_setmaxalloc(mgr->depool, 32768); 2043254897Serwin isc_mempool_setfreemax(mgr->depool, 32768); 2044254897Serwin isc_mempool_associatelock(mgr->depool, &mgr->depool_lock); 2045254897Serwin isc_mempool_setfillcount(mgr->depool, 256); 2046135446Strhodes 2047135446Strhodes isc_mempool_setname(mgr->rpool, "dispmgr_rpool"); 2048254897Serwin isc_mempool_setmaxalloc(mgr->rpool, 32768); 2049254897Serwin isc_mempool_setfreemax(mgr->rpool, 32768); 2050254897Serwin isc_mempool_associatelock(mgr->rpool, &mgr->rpool_lock); 2051254897Serwin isc_mempool_setfillcount(mgr->rpool, 256); 2052135446Strhodes 2053135446Strhodes isc_mempool_setname(mgr->dpool, "dispmgr_dpool"); 2054254897Serwin isc_mempool_setmaxalloc(mgr->dpool, 32768); 2055254897Serwin isc_mempool_setfreemax(mgr->dpool, 32768); 2056254897Serwin isc_mempool_associatelock(mgr->dpool, &mgr->dpool_lock); 2057254897Serwin isc_mempool_setfillcount(mgr->dpool, 256); 2058135446Strhodes 2059135446Strhodes mgr->buffers = 0; 2060135446Strhodes mgr->buffersize = 0; 2061135446Strhodes mgr->maxbuffers = 0; 2062135446Strhodes mgr->bpool = NULL; 2063186462Sdougb mgr->spool = NULL; 2064135446Strhodes mgr->entropy = NULL; 2065135446Strhodes mgr->qid = NULL; 2066135446Strhodes mgr->state = 0; 2067135446Strhodes ISC_LIST_INIT(mgr->list); 2068186462Sdougb mgr->v4ports = NULL; 2069186462Sdougb mgr->v6ports = NULL; 2070186462Sdougb mgr->nv4ports = 0; 2071186462Sdougb mgr->nv6ports = 0; 2072135446Strhodes mgr->magic = DNS_DISPATCHMGR_MAGIC; 2073135446Strhodes 2074186462Sdougb result = create_default_portset(mctx, &v4portset); 2075186462Sdougb if (result == ISC_R_SUCCESS) { 2076186462Sdougb result = create_default_portset(mctx, &v6portset); 2077186462Sdougb if (result == ISC_R_SUCCESS) { 2078186462Sdougb result = dns_dispatchmgr_setavailports(mgr, 2079186462Sdougb v4portset, 2080186462Sdougb v6portset); 2081186462Sdougb } 2082186462Sdougb } 2083186462Sdougb if (v4portset != NULL) 2084186462Sdougb isc_portset_destroy(mctx, &v4portset); 2085186462Sdougb if (v6portset != NULL) 2086186462Sdougb isc_portset_destroy(mctx, &v6portset); 2087186462Sdougb if (result != ISC_R_SUCCESS) 2088186462Sdougb goto kill_dpool; 2089186462Sdougb 2090224092Sdougb#ifdef BIND9 2091135446Strhodes if (entropy != NULL) 2092135446Strhodes isc_entropy_attach(entropy, &mgr->entropy); 2093224092Sdougb#else 2094224092Sdougb UNUSED(entropy); 2095224092Sdougb#endif 2096135446Strhodes 2097224092Sdougb dispatch_initrandom(&mgr->arc4ctx, mgr->entropy, &mgr->arc4_lock); 2098180477Sdougb 2099135446Strhodes *mgrp = mgr; 2100135446Strhodes return (ISC_R_SUCCESS); 2101135446Strhodes 2102186462Sdougb kill_dpool: 2103186462Sdougb isc_mempool_destroy(&mgr->dpool); 2104135446Strhodes kill_rpool: 2105135446Strhodes isc_mempool_destroy(&mgr->rpool); 2106254897Serwin kill_depool: 2107254897Serwin isc_mempool_destroy(&mgr->depool); 2108254897Serwin kill_spool_lock: 2109254897Serwin DESTROYLOCK(&mgr->spool_lock); 2110254897Serwin kill_bpool_lock: 2111254897Serwin DESTROYLOCK(&mgr->bpool_lock); 2112254897Serwin kill_dpool_lock: 2113254897Serwin DESTROYLOCK(&mgr->dpool_lock); 2114254897Serwin kill_rpool_lock: 2115254897Serwin DESTROYLOCK(&mgr->rpool_lock); 2116254897Serwin kill_depool_lock: 2117254897Serwin DESTROYLOCK(&mgr->depool_lock); 2118135446Strhodes kill_buffer_lock: 2119135446Strhodes DESTROYLOCK(&mgr->buffer_lock); 2120180477Sdougb kill_arc4_lock: 2121180477Sdougb DESTROYLOCK(&mgr->arc4_lock); 2122135446Strhodes kill_lock: 2123135446Strhodes DESTROYLOCK(&mgr->lock); 2124135446Strhodes deallocate: 2125135446Strhodes isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t)); 2126135446Strhodes isc_mem_detach(&mctx); 2127135446Strhodes 2128135446Strhodes return (result); 2129135446Strhodes} 2130135446Strhodes 2131135446Strhodesvoid 2132135446Strhodesdns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole) { 2133135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2134135446Strhodes if (mgr->blackhole != NULL) 2135135446Strhodes dns_acl_detach(&mgr->blackhole); 2136135446Strhodes dns_acl_attach(blackhole, &mgr->blackhole); 2137135446Strhodes} 2138135446Strhodes 2139135446Strhodesdns_acl_t * 2140135446Strhodesdns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr) { 2141135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2142135446Strhodes return (mgr->blackhole); 2143135446Strhodes} 2144135446Strhodes 2145135446Strhodesvoid 2146135446Strhodesdns_dispatchmgr_setblackportlist(dns_dispatchmgr_t *mgr, 2147135446Strhodes dns_portlist_t *portlist) 2148135446Strhodes{ 2149135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2150186462Sdougb UNUSED(portlist); 2151186462Sdougb 2152186462Sdougb /* This function is deprecated: use dns_dispatchmgr_setavailports(). */ 2153186462Sdougb return; 2154135446Strhodes} 2155135446Strhodes 2156135446Strhodesdns_portlist_t * 2157135446Strhodesdns_dispatchmgr_getblackportlist(dns_dispatchmgr_t *mgr) { 2158135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2159186462Sdougb return (NULL); /* this function is deprecated */ 2160135446Strhodes} 2161135446Strhodes 2162186462Sdougbisc_result_t 2163186462Sdougbdns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset, 2164186462Sdougb isc_portset_t *v6portset) 2165186462Sdougb{ 2166186462Sdougb in_port_t *v4ports, *v6ports, p; 2167186462Sdougb unsigned int nv4ports, nv6ports, i4, i6; 2168186462Sdougb 2169186462Sdougb REQUIRE(VALID_DISPATCHMGR(mgr)); 2170186462Sdougb 2171186462Sdougb nv4ports = isc_portset_nports(v4portset); 2172186462Sdougb nv6ports = isc_portset_nports(v6portset); 2173186462Sdougb 2174186462Sdougb v4ports = NULL; 2175186462Sdougb if (nv4ports != 0) { 2176186462Sdougb v4ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv4ports); 2177186462Sdougb if (v4ports == NULL) 2178186462Sdougb return (ISC_R_NOMEMORY); 2179186462Sdougb } 2180186462Sdougb v6ports = NULL; 2181186462Sdougb if (nv6ports != 0) { 2182186462Sdougb v6ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv6ports); 2183186462Sdougb if (v6ports == NULL) { 2184186462Sdougb if (v4ports != NULL) { 2185186462Sdougb isc_mem_put(mgr->mctx, v4ports, 2186186462Sdougb sizeof(in_port_t) * 2187186462Sdougb isc_portset_nports(v4portset)); 2188186462Sdougb } 2189186462Sdougb return (ISC_R_NOMEMORY); 2190186462Sdougb } 2191186462Sdougb } 2192186462Sdougb 2193186462Sdougb p = 0; 2194186462Sdougb i4 = 0; 2195186462Sdougb i6 = 0; 2196186462Sdougb do { 2197186462Sdougb if (isc_portset_isset(v4portset, p)) { 2198186462Sdougb INSIST(i4 < nv4ports); 2199186462Sdougb v4ports[i4++] = p; 2200186462Sdougb } 2201186462Sdougb if (isc_portset_isset(v6portset, p)) { 2202186462Sdougb INSIST(i6 < nv6ports); 2203186462Sdougb v6ports[i6++] = p; 2204186462Sdougb } 2205186462Sdougb } while (p++ < 65535); 2206186462Sdougb INSIST(i4 == nv4ports && i6 == nv6ports); 2207186462Sdougb 2208186462Sdougb PORTBUFLOCK(mgr); 2209186462Sdougb if (mgr->v4ports != NULL) { 2210186462Sdougb isc_mem_put(mgr->mctx, mgr->v4ports, 2211186462Sdougb mgr->nv4ports * sizeof(in_port_t)); 2212186462Sdougb } 2213186462Sdougb mgr->v4ports = v4ports; 2214186462Sdougb mgr->nv4ports = nv4ports; 2215186462Sdougb 2216186462Sdougb if (mgr->v6ports != NULL) { 2217186462Sdougb isc_mem_put(mgr->mctx, mgr->v6ports, 2218186462Sdougb mgr->nv6ports * sizeof(in_port_t)); 2219186462Sdougb } 2220186462Sdougb mgr->v6ports = v6ports; 2221186462Sdougb mgr->nv6ports = nv6ports; 2222186462Sdougb PORTBUFUNLOCK(mgr); 2223186462Sdougb 2224186462Sdougb return (ISC_R_SUCCESS); 2225186462Sdougb} 2226186462Sdougb 2227135446Strhodesstatic isc_result_t 2228135446Strhodesdns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr, 2229186462Sdougb unsigned int buffersize, unsigned int maxbuffers, 2230186462Sdougb unsigned int maxrequests, unsigned int buckets, 2231186462Sdougb unsigned int increment) 2232135446Strhodes{ 2233135446Strhodes isc_result_t result; 2234135446Strhodes 2235135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2236135446Strhodes REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); 2237135446Strhodes REQUIRE(maxbuffers > 0); 2238135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 2239135446Strhodes REQUIRE(increment > buckets); 2240135446Strhodes 2241135446Strhodes /* 2242135446Strhodes * Keep some number of items around. This should be a config 2243135446Strhodes * option. For now, keep 8, but later keep at least two even 2244135446Strhodes * if the caller wants less. This allows us to ensure certain 2245135446Strhodes * things, like an event can be "freed" and the next allocation 2246135446Strhodes * will always succeed. 2247135446Strhodes * 2248135446Strhodes * Note that if limits are placed on anything here, we use one 2249135446Strhodes * event internally, so the actual limit should be "wanted + 1." 2250135446Strhodes * 2251135446Strhodes * XXXMLG 2252135446Strhodes */ 2253135446Strhodes 2254135446Strhodes if (maxbuffers < 8) 2255135446Strhodes maxbuffers = 8; 2256135446Strhodes 2257135446Strhodes LOCK(&mgr->buffer_lock); 2258186462Sdougb 2259186462Sdougb /* Create or adjust buffer pool */ 2260135446Strhodes if (mgr->bpool != NULL) { 2261204619Sdougb /* 2262204619Sdougb * We only increase the maxbuffers to avoid accidental buffer 2263204619Sdougb * shortage. Ideally we'd separate the manager-wide maximum 2264204619Sdougb * from per-dispatch limits and respect the latter within the 2265204619Sdougb * global limit. But at this moment that's deemed to be 2266204619Sdougb * overkilling and isn't worth additional implementation 2267204619Sdougb * complexity. 2268204619Sdougb */ 2269204619Sdougb if (maxbuffers > mgr->maxbuffers) { 2270204619Sdougb isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); 2271254897Serwin isc_mempool_setfreemax(mgr->bpool, maxbuffers); 2272204619Sdougb mgr->maxbuffers = maxbuffers; 2273204619Sdougb } 2274186462Sdougb } else { 2275186462Sdougb result = isc_mempool_create(mgr->mctx, buffersize, &mgr->bpool); 2276186462Sdougb if (result != ISC_R_SUCCESS) { 2277186462Sdougb UNLOCK(&mgr->buffer_lock); 2278186462Sdougb return (result); 2279186462Sdougb } 2280186462Sdougb isc_mempool_setname(mgr->bpool, "dispmgr_bpool"); 2281186462Sdougb isc_mempool_setmaxalloc(mgr->bpool, maxbuffers); 2282254897Serwin isc_mempool_setfreemax(mgr->bpool, maxbuffers); 2283254897Serwin isc_mempool_associatelock(mgr->bpool, &mgr->bpool_lock); 2284254897Serwin isc_mempool_setfillcount(mgr->bpool, 256); 2285186462Sdougb } 2286186462Sdougb 2287186462Sdougb /* Create or adjust socket pool */ 2288186462Sdougb if (mgr->spool != NULL) { 2289254897Serwin if (maxrequests < DNS_DISPATCH_POOLSOCKS * 2) 2290254897Serwin isc_mempool_setmaxalloc(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2); 2291254897Serwin isc_mempool_setfreemax(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2); 2292135446Strhodes UNLOCK(&mgr->buffer_lock); 2293135446Strhodes return (ISC_R_SUCCESS); 2294135446Strhodes } 2295186462Sdougb result = isc_mempool_create(mgr->mctx, sizeof(dispsocket_t), 2296186462Sdougb &mgr->spool); 2297186462Sdougb if (result != ISC_R_SUCCESS) { 2298170222Sdougb UNLOCK(&mgr->buffer_lock); 2299186462Sdougb goto cleanup; 2300135446Strhodes } 2301186462Sdougb isc_mempool_setname(mgr->spool, "dispmgr_spool"); 2302186462Sdougb isc_mempool_setmaxalloc(mgr->spool, maxrequests); 2303254897Serwin isc_mempool_setfreemax(mgr->spool, maxrequests); 2304254897Serwin isc_mempool_associatelock(mgr->spool, &mgr->spool_lock); 2305254897Serwin isc_mempool_setfillcount(mgr->spool, 256); 2306135446Strhodes 2307186462Sdougb result = qid_allocate(mgr, buckets, increment, &mgr->qid, ISC_TRUE); 2308135446Strhodes if (result != ISC_R_SUCCESS) 2309135446Strhodes goto cleanup; 2310135446Strhodes 2311135446Strhodes mgr->buffersize = buffersize; 2312135446Strhodes mgr->maxbuffers = maxbuffers; 2313135446Strhodes UNLOCK(&mgr->buffer_lock); 2314135446Strhodes return (ISC_R_SUCCESS); 2315135446Strhodes 2316135446Strhodes cleanup: 2317135446Strhodes isc_mempool_destroy(&mgr->bpool); 2318186462Sdougb if (mgr->spool != NULL) 2319186462Sdougb isc_mempool_destroy(&mgr->spool); 2320135446Strhodes UNLOCK(&mgr->buffer_lock); 2321186462Sdougb return (result); 2322135446Strhodes} 2323135446Strhodes 2324135446Strhodesvoid 2325135446Strhodesdns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { 2326135446Strhodes dns_dispatchmgr_t *mgr; 2327135446Strhodes isc_boolean_t killit; 2328135446Strhodes 2329135446Strhodes REQUIRE(mgrp != NULL); 2330135446Strhodes REQUIRE(VALID_DISPATCHMGR(*mgrp)); 2331135446Strhodes 2332135446Strhodes mgr = *mgrp; 2333135446Strhodes *mgrp = NULL; 2334135446Strhodes 2335135446Strhodes LOCK(&mgr->lock); 2336135446Strhodes mgr->state |= MGR_SHUTTINGDOWN; 2337135446Strhodes 2338135446Strhodes killit = destroy_mgr_ok(mgr); 2339135446Strhodes UNLOCK(&mgr->lock); 2340135446Strhodes 2341135446Strhodes mgr_log(mgr, LVL(90), "destroy: killit=%d", killit); 2342135446Strhodes 2343135446Strhodes if (killit) 2344135446Strhodes destroy_mgr(&mgr); 2345135446Strhodes} 2346135446Strhodes 2347193149Sdougbvoid 2348193149Sdougbdns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, isc_stats_t *stats) { 2349193149Sdougb REQUIRE(VALID_DISPATCHMGR(mgr)); 2350193149Sdougb REQUIRE(ISC_LIST_EMPTY(mgr->list)); 2351193149Sdougb REQUIRE(mgr->stats == NULL); 2352193149Sdougb 2353193149Sdougb isc_stats_attach(stats, &mgr->stats); 2354193149Sdougb} 2355193149Sdougb 2356186462Sdougbstatic int 2357186462Sdougbport_cmp(const void *key, const void *ent) { 2358186462Sdougb in_port_t p1 = *(const in_port_t *)key; 2359186462Sdougb in_port_t p2 = *(const in_port_t *)ent; 2360186462Sdougb 2361186462Sdougb if (p1 < p2) 2362186462Sdougb return (-1); 2363186462Sdougb else if (p1 == p2) 2364186462Sdougb return (0); 2365186462Sdougb else 2366186462Sdougb return (1); 2367186462Sdougb} 2368186462Sdougb 2369135446Strhodesstatic isc_boolean_t 2370186462Sdougbportavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock, 2371186462Sdougb isc_sockaddr_t *sockaddrp) 2372180477Sdougb{ 2373135446Strhodes isc_sockaddr_t sockaddr; 2374135446Strhodes isc_result_t result; 2375186462Sdougb in_port_t *ports, port; 2376186462Sdougb unsigned int nports; 2377186462Sdougb isc_boolean_t available = ISC_FALSE; 2378135446Strhodes 2379180477Sdougb REQUIRE(sock != NULL || sockaddrp != NULL); 2380180477Sdougb 2381186462Sdougb PORTBUFLOCK(mgr); 2382180477Sdougb if (sock != NULL) { 2383180477Sdougb sockaddrp = &sockaddr; 2384180477Sdougb result = isc_socket_getsockname(sock, sockaddrp); 2385180477Sdougb if (result != ISC_R_SUCCESS) 2386186462Sdougb goto unlock; 2387180477Sdougb } 2388135446Strhodes 2389186462Sdougb if (isc_sockaddr_pf(sockaddrp) == AF_INET) { 2390186462Sdougb ports = mgr->v4ports; 2391186462Sdougb nports = mgr->nv4ports; 2392186462Sdougb } else { 2393186462Sdougb ports = mgr->v6ports; 2394186462Sdougb nports = mgr->nv6ports; 2395186462Sdougb } 2396186462Sdougb if (ports == NULL) 2397186462Sdougb goto unlock; 2398186462Sdougb 2399186462Sdougb port = isc_sockaddr_getport(sockaddrp); 2400186462Sdougb if (bsearch(&port, ports, nports, sizeof(in_port_t), port_cmp) != NULL) 2401186462Sdougb available = ISC_TRUE; 2402186462Sdougb 2403186462Sdougbunlock: 2404186462Sdougb PORTBUFUNLOCK(mgr); 2405186462Sdougb return (available); 2406135446Strhodes} 2407135446Strhodes 2408135446Strhodes#define ATTRMATCH(_a1, _a2, _mask) (((_a1) & (_mask)) == ((_a2) & (_mask))) 2409135446Strhodes 2410135446Strhodesstatic isc_boolean_t 2411135446Strhodeslocal_addr_match(dns_dispatch_t *disp, isc_sockaddr_t *addr) { 2412135446Strhodes isc_sockaddr_t sockaddr; 2413135446Strhodes isc_result_t result; 2414135446Strhodes 2415186462Sdougb REQUIRE(disp->socket != NULL); 2416186462Sdougb 2417135446Strhodes if (addr == NULL) 2418135446Strhodes return (ISC_TRUE); 2419135446Strhodes 2420135446Strhodes /* 2421186462Sdougb * Don't match wildcard ports unless the port is available in the 2422186462Sdougb * current configuration. 2423135446Strhodes */ 2424186462Sdougb if (isc_sockaddr_getport(addr) == 0 && 2425135446Strhodes isc_sockaddr_getport(&disp->local) == 0 && 2426186462Sdougb !portavailable(disp->mgr, disp->socket, NULL)) { 2427135446Strhodes return (ISC_FALSE); 2428186462Sdougb } 2429135446Strhodes 2430135446Strhodes /* 2431135446Strhodes * Check if we match the binding <address,port>. 2432135446Strhodes * Wildcard ports match/fail here. 2433135446Strhodes */ 2434135446Strhodes if (isc_sockaddr_equal(&disp->local, addr)) 2435135446Strhodes return (ISC_TRUE); 2436135446Strhodes if (isc_sockaddr_getport(addr) == 0) 2437135446Strhodes return (ISC_FALSE); 2438135446Strhodes 2439135446Strhodes /* 2440135446Strhodes * Check if we match a bound wildcard port <address,port>. 2441135446Strhodes */ 2442135446Strhodes if (!isc_sockaddr_eqaddr(&disp->local, addr)) 2443135446Strhodes return (ISC_FALSE); 2444135446Strhodes result = isc_socket_getsockname(disp->socket, &sockaddr); 2445135446Strhodes if (result != ISC_R_SUCCESS) 2446135446Strhodes return (ISC_FALSE); 2447135446Strhodes 2448135446Strhodes return (isc_sockaddr_equal(&sockaddr, addr)); 2449135446Strhodes} 2450135446Strhodes 2451135446Strhodes/* 2452135446Strhodes * Requires mgr be locked. 2453135446Strhodes * 2454135446Strhodes * No dispatcher can be locked by this thread when calling this function. 2455135446Strhodes * 2456135446Strhodes * 2457135446Strhodes * NOTE: 2458135446Strhodes * If a matching dispatcher is found, it is locked after this function 2459135446Strhodes * returns, and must be unlocked by the caller. 2460135446Strhodes */ 2461135446Strhodesstatic isc_result_t 2462135446Strhodesdispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local, 2463135446Strhodes unsigned int attributes, unsigned int mask, 2464135446Strhodes dns_dispatch_t **dispp) 2465135446Strhodes{ 2466135446Strhodes dns_dispatch_t *disp; 2467135446Strhodes isc_result_t result; 2468135446Strhodes 2469135446Strhodes /* 2470186462Sdougb * Make certain that we will not match a private or exclusive dispatch. 2471135446Strhodes */ 2472186462Sdougb attributes &= ~(DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE); 2473186462Sdougb mask |= (DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE); 2474135446Strhodes 2475135446Strhodes disp = ISC_LIST_HEAD(mgr->list); 2476135446Strhodes while (disp != NULL) { 2477135446Strhodes LOCK(&disp->lock); 2478135446Strhodes if ((disp->shutting_down == 0) 2479135446Strhodes && ATTRMATCH(disp->attributes, attributes, mask) 2480135446Strhodes && local_addr_match(disp, local)) 2481135446Strhodes break; 2482135446Strhodes UNLOCK(&disp->lock); 2483135446Strhodes disp = ISC_LIST_NEXT(disp, link); 2484135446Strhodes } 2485135446Strhodes 2486135446Strhodes if (disp == NULL) { 2487135446Strhodes result = ISC_R_NOTFOUND; 2488135446Strhodes goto out; 2489135446Strhodes } 2490135446Strhodes 2491135446Strhodes *dispp = disp; 2492135446Strhodes result = ISC_R_SUCCESS; 2493135446Strhodes out: 2494135446Strhodes 2495135446Strhodes return (result); 2496135446Strhodes} 2497135446Strhodes 2498135446Strhodesstatic isc_result_t 2499135446Strhodesqid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets, 2500186462Sdougb unsigned int increment, dns_qid_t **qidp, 2501186462Sdougb isc_boolean_t needsocktable) 2502135446Strhodes{ 2503135446Strhodes dns_qid_t *qid; 2504135446Strhodes unsigned int i; 2505170222Sdougb isc_result_t result; 2506135446Strhodes 2507135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2508135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 2509135446Strhodes REQUIRE(increment > buckets); 2510135446Strhodes REQUIRE(qidp != NULL && *qidp == NULL); 2511135446Strhodes 2512135446Strhodes qid = isc_mem_get(mgr->mctx, sizeof(*qid)); 2513135446Strhodes if (qid == NULL) 2514135446Strhodes return (ISC_R_NOMEMORY); 2515135446Strhodes 2516135446Strhodes qid->qid_table = isc_mem_get(mgr->mctx, 2517135446Strhodes buckets * sizeof(dns_displist_t)); 2518135446Strhodes if (qid->qid_table == NULL) { 2519135446Strhodes isc_mem_put(mgr->mctx, qid, sizeof(*qid)); 2520135446Strhodes return (ISC_R_NOMEMORY); 2521135446Strhodes } 2522135446Strhodes 2523186462Sdougb qid->sock_table = NULL; 2524186462Sdougb if (needsocktable) { 2525186462Sdougb qid->sock_table = isc_mem_get(mgr->mctx, buckets * 2526186462Sdougb sizeof(dispsocketlist_t)); 2527186462Sdougb if (qid->sock_table == NULL) { 2528186462Sdougb isc_mem_put(mgr->mctx, qid->qid_table, 2529186462Sdougb buckets * sizeof(dns_displist_t)); 2530225361Sdougb isc_mem_put(mgr->mctx, qid, sizeof(*qid)); 2531186462Sdougb return (ISC_R_NOMEMORY); 2532186462Sdougb } 2533186462Sdougb } 2534186462Sdougb 2535170222Sdougb result = isc_mutex_init(&qid->lock); 2536170222Sdougb if (result != ISC_R_SUCCESS) { 2537186462Sdougb if (qid->sock_table != NULL) { 2538186462Sdougb isc_mem_put(mgr->mctx, qid->sock_table, 2539186462Sdougb buckets * sizeof(dispsocketlist_t)); 2540186462Sdougb } 2541135446Strhodes isc_mem_put(mgr->mctx, qid->qid_table, 2542135446Strhodes buckets * sizeof(dns_displist_t)); 2543135446Strhodes isc_mem_put(mgr->mctx, qid, sizeof(*qid)); 2544170222Sdougb return (result); 2545135446Strhodes } 2546135446Strhodes 2547186462Sdougb for (i = 0; i < buckets; i++) { 2548135446Strhodes ISC_LIST_INIT(qid->qid_table[i]); 2549186462Sdougb if (qid->sock_table != NULL) 2550186462Sdougb ISC_LIST_INIT(qid->sock_table[i]); 2551186462Sdougb } 2552135446Strhodes 2553135446Strhodes qid->qid_nbuckets = buckets; 2554135446Strhodes qid->qid_increment = increment; 2555135446Strhodes qid->magic = QID_MAGIC; 2556135446Strhodes *qidp = qid; 2557135446Strhodes return (ISC_R_SUCCESS); 2558135446Strhodes} 2559135446Strhodes 2560135446Strhodesstatic void 2561135446Strhodesqid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) { 2562135446Strhodes dns_qid_t *qid; 2563135446Strhodes 2564135446Strhodes REQUIRE(qidp != NULL); 2565135446Strhodes qid = *qidp; 2566135446Strhodes 2567135446Strhodes REQUIRE(VALID_QID(qid)); 2568135446Strhodes 2569135446Strhodes *qidp = NULL; 2570135446Strhodes qid->magic = 0; 2571135446Strhodes isc_mem_put(mctx, qid->qid_table, 2572135446Strhodes qid->qid_nbuckets * sizeof(dns_displist_t)); 2573186462Sdougb if (qid->sock_table != NULL) { 2574186462Sdougb isc_mem_put(mctx, qid->sock_table, 2575186462Sdougb qid->qid_nbuckets * sizeof(dispsocketlist_t)); 2576186462Sdougb } 2577135446Strhodes DESTROYLOCK(&qid->lock); 2578135446Strhodes isc_mem_put(mctx, qid, sizeof(*qid)); 2579135446Strhodes} 2580135446Strhodes 2581135446Strhodes/* 2582135446Strhodes * Allocate and set important limits. 2583135446Strhodes */ 2584135446Strhodesstatic isc_result_t 2585135446Strhodesdispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests, 2586135446Strhodes dns_dispatch_t **dispp) 2587135446Strhodes{ 2588135446Strhodes dns_dispatch_t *disp; 2589170222Sdougb isc_result_t result; 2590135446Strhodes 2591135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2592135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 2593135446Strhodes 2594135446Strhodes /* 2595135446Strhodes * Set up the dispatcher, mostly. Don't bother setting some of 2596135446Strhodes * the options that are controlled by tcp vs. udp, etc. 2597135446Strhodes */ 2598135446Strhodes 2599135446Strhodes disp = isc_mempool_get(mgr->dpool); 2600135446Strhodes if (disp == NULL) 2601135446Strhodes return (ISC_R_NOMEMORY); 2602135446Strhodes 2603135446Strhodes disp->magic = 0; 2604135446Strhodes disp->mgr = mgr; 2605135446Strhodes disp->maxrequests = maxrequests; 2606135446Strhodes disp->attributes = 0; 2607135446Strhodes ISC_LINK_INIT(disp, link); 2608135446Strhodes disp->refcount = 1; 2609135446Strhodes disp->recv_pending = 0; 2610135446Strhodes memset(&disp->local, 0, sizeof(disp->local)); 2611180477Sdougb disp->localport = 0; 2612135446Strhodes disp->shutting_down = 0; 2613135446Strhodes disp->shutdown_out = 0; 2614135446Strhodes disp->connected = 0; 2615135446Strhodes disp->tcpmsg_valid = 0; 2616135446Strhodes disp->shutdown_why = ISC_R_UNEXPECTED; 2617135446Strhodes disp->requests = 0; 2618135446Strhodes disp->tcpbuffers = 0; 2619135446Strhodes disp->qid = NULL; 2620186462Sdougb ISC_LIST_INIT(disp->activesockets); 2621186462Sdougb ISC_LIST_INIT(disp->inactivesockets); 2622186462Sdougb disp->nsockets = 0; 2623224092Sdougb dispatch_initrandom(&disp->arc4ctx, mgr->entropy, NULL); 2624193149Sdougb disp->port_table = NULL; 2625193149Sdougb disp->portpool = NULL; 2626135446Strhodes 2627170222Sdougb result = isc_mutex_init(&disp->lock); 2628170222Sdougb if (result != ISC_R_SUCCESS) 2629135446Strhodes goto deallocate; 2630135446Strhodes 2631254897Serwin disp->failsafe_ev = allocate_devent(disp); 2632135446Strhodes if (disp->failsafe_ev == NULL) { 2633170222Sdougb result = ISC_R_NOMEMORY; 2634135446Strhodes goto kill_lock; 2635135446Strhodes } 2636135446Strhodes 2637135446Strhodes disp->magic = DISPATCH_MAGIC; 2638135446Strhodes 2639135446Strhodes *dispp = disp; 2640135446Strhodes return (ISC_R_SUCCESS); 2641135446Strhodes 2642135446Strhodes /* 2643135446Strhodes * error returns 2644135446Strhodes */ 2645135446Strhodes kill_lock: 2646135446Strhodes DESTROYLOCK(&disp->lock); 2647135446Strhodes deallocate: 2648135446Strhodes isc_mempool_put(mgr->dpool, disp); 2649135446Strhodes 2650170222Sdougb return (result); 2651135446Strhodes} 2652135446Strhodes 2653135446Strhodes 2654135446Strhodes/* 2655193149Sdougb * MUST be unlocked, and not used by anything. 2656135446Strhodes */ 2657135446Strhodesstatic void 2658262706Serwindispatch_free(dns_dispatch_t **dispp) { 2659135446Strhodes dns_dispatch_t *disp; 2660135446Strhodes dns_dispatchmgr_t *mgr; 2661193149Sdougb int i; 2662135446Strhodes 2663135446Strhodes REQUIRE(VALID_DISPATCH(*dispp)); 2664135446Strhodes disp = *dispp; 2665135446Strhodes *dispp = NULL; 2666135446Strhodes 2667135446Strhodes mgr = disp->mgr; 2668135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2669135446Strhodes 2670135446Strhodes if (disp->tcpmsg_valid) { 2671135446Strhodes dns_tcpmsg_invalidate(&disp->tcpmsg); 2672135446Strhodes disp->tcpmsg_valid = 0; 2673135446Strhodes } 2674135446Strhodes 2675135446Strhodes INSIST(disp->tcpbuffers == 0); 2676135446Strhodes INSIST(disp->requests == 0); 2677135446Strhodes INSIST(disp->recv_pending == 0); 2678186462Sdougb INSIST(ISC_LIST_EMPTY(disp->activesockets)); 2679186462Sdougb INSIST(ISC_LIST_EMPTY(disp->inactivesockets)); 2680135446Strhodes 2681254897Serwin isc_mempool_put(mgr->depool, disp->failsafe_ev); 2682135446Strhodes disp->failsafe_ev = NULL; 2683135446Strhodes 2684135446Strhodes if (disp->qid != NULL) 2685135446Strhodes qid_destroy(mgr->mctx, &disp->qid); 2686193149Sdougb 2687193149Sdougb if (disp->port_table != NULL) { 2688193149Sdougb for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++) 2689193149Sdougb INSIST(ISC_LIST_EMPTY(disp->port_table[i])); 2690193149Sdougb isc_mem_put(mgr->mctx, disp->port_table, 2691193149Sdougb sizeof(disp->port_table[0]) * 2692193149Sdougb DNS_DISPATCH_PORTTABLESIZE); 2693193149Sdougb } 2694193149Sdougb 2695193149Sdougb if (disp->portpool != NULL) 2696193149Sdougb isc_mempool_destroy(&disp->portpool); 2697193149Sdougb 2698135446Strhodes disp->mgr = NULL; 2699135446Strhodes DESTROYLOCK(&disp->lock); 2700135446Strhodes disp->magic = 0; 2701135446Strhodes isc_mempool_put(mgr->dpool, disp); 2702135446Strhodes} 2703135446Strhodes 2704135446Strhodesisc_result_t 2705135446Strhodesdns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, 2706135446Strhodes isc_taskmgr_t *taskmgr, unsigned int buffersize, 2707135446Strhodes unsigned int maxbuffers, unsigned int maxrequests, 2708135446Strhodes unsigned int buckets, unsigned int increment, 2709135446Strhodes unsigned int attributes, dns_dispatch_t **dispp) 2710135446Strhodes{ 2711135446Strhodes isc_result_t result; 2712135446Strhodes dns_dispatch_t *disp; 2713135446Strhodes 2714135446Strhodes UNUSED(maxbuffers); 2715135446Strhodes UNUSED(buffersize); 2716135446Strhodes 2717135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2718135446Strhodes REQUIRE(isc_socket_gettype(sock) == isc_sockettype_tcp); 2719135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0); 2720135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0); 2721135446Strhodes 2722135446Strhodes attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ 2723135446Strhodes 2724135446Strhodes LOCK(&mgr->lock); 2725135446Strhodes 2726135446Strhodes /* 2727135446Strhodes * dispatch_allocate() checks mgr for us. 2728135446Strhodes * qid_allocate() checks buckets and increment for us. 2729135446Strhodes */ 2730135446Strhodes disp = NULL; 2731135446Strhodes result = dispatch_allocate(mgr, maxrequests, &disp); 2732135446Strhodes if (result != ISC_R_SUCCESS) { 2733135446Strhodes UNLOCK(&mgr->lock); 2734135446Strhodes return (result); 2735135446Strhodes } 2736135446Strhodes 2737186462Sdougb result = qid_allocate(mgr, buckets, increment, &disp->qid, ISC_FALSE); 2738135446Strhodes if (result != ISC_R_SUCCESS) 2739135446Strhodes goto deallocate_dispatch; 2740135446Strhodes 2741135446Strhodes disp->socktype = isc_sockettype_tcp; 2742135446Strhodes disp->socket = NULL; 2743135446Strhodes isc_socket_attach(sock, &disp->socket); 2744135446Strhodes 2745254897Serwin disp->sepool = NULL; 2746254897Serwin 2747186462Sdougb disp->ntasks = 1; 2748186462Sdougb disp->task[0] = NULL; 2749186462Sdougb result = isc_task_create(taskmgr, 0, &disp->task[0]); 2750135446Strhodes if (result != ISC_R_SUCCESS) 2751135446Strhodes goto kill_socket; 2752135446Strhodes 2753135446Strhodes disp->ctlevent = isc_event_allocate(mgr->mctx, disp, 2754135446Strhodes DNS_EVENT_DISPATCHCONTROL, 2755135446Strhodes destroy_disp, disp, 2756135446Strhodes sizeof(isc_event_t)); 2757174187Sdougb if (disp->ctlevent == NULL) { 2758174187Sdougb result = ISC_R_NOMEMORY; 2759135446Strhodes goto kill_task; 2760174187Sdougb } 2761135446Strhodes 2762186462Sdougb isc_task_setname(disp->task[0], "tcpdispatch", disp); 2763135446Strhodes 2764135446Strhodes dns_tcpmsg_init(mgr->mctx, disp->socket, &disp->tcpmsg); 2765135446Strhodes disp->tcpmsg_valid = 1; 2766135446Strhodes 2767135446Strhodes disp->attributes = attributes; 2768135446Strhodes 2769135446Strhodes /* 2770135446Strhodes * Append it to the dispatcher list. 2771135446Strhodes */ 2772135446Strhodes ISC_LIST_APPEND(mgr->list, disp, link); 2773135446Strhodes UNLOCK(&mgr->lock); 2774135446Strhodes 2775135446Strhodes mgr_log(mgr, LVL(90), "created TCP dispatcher %p", disp); 2776186462Sdougb dispatch_log(disp, LVL(90), "created task %p", disp->task[0]); 2777135446Strhodes 2778135446Strhodes *dispp = disp; 2779135446Strhodes 2780135446Strhodes return (ISC_R_SUCCESS); 2781135446Strhodes 2782135446Strhodes /* 2783135446Strhodes * Error returns. 2784135446Strhodes */ 2785135446Strhodes kill_task: 2786186462Sdougb isc_task_detach(&disp->task[0]); 2787135446Strhodes kill_socket: 2788135446Strhodes isc_socket_detach(&disp->socket); 2789135446Strhodes deallocate_dispatch: 2790135446Strhodes dispatch_free(&disp); 2791135446Strhodes 2792135446Strhodes UNLOCK(&mgr->lock); 2793135446Strhodes 2794135446Strhodes return (result); 2795135446Strhodes} 2796135446Strhodes 2797135446Strhodesisc_result_t 2798254897Serwindns_dispatch_getudp_dup(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 2799135446Strhodes isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, 2800135446Strhodes unsigned int buffersize, 2801135446Strhodes unsigned int maxbuffers, unsigned int maxrequests, 2802135446Strhodes unsigned int buckets, unsigned int increment, 2803135446Strhodes unsigned int attributes, unsigned int mask, 2804254897Serwin dns_dispatch_t **dispp, dns_dispatch_t *dup_dispatch) 2805135446Strhodes{ 2806135446Strhodes isc_result_t result; 2807180477Sdougb dns_dispatch_t *disp = NULL; 2808135446Strhodes 2809135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 2810135446Strhodes REQUIRE(sockmgr != NULL); 2811135446Strhodes REQUIRE(localaddr != NULL); 2812135446Strhodes REQUIRE(taskmgr != NULL); 2813135446Strhodes REQUIRE(buffersize >= 512 && buffersize < (64 * 1024)); 2814135446Strhodes REQUIRE(maxbuffers > 0); 2815135446Strhodes REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */ 2816135446Strhodes REQUIRE(increment > buckets); 2817135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 2818135446Strhodes REQUIRE((attributes & DNS_DISPATCHATTR_TCP) == 0); 2819135446Strhodes 2820135446Strhodes result = dns_dispatchmgr_setudp(mgr, buffersize, maxbuffers, 2821186462Sdougb maxrequests, buckets, increment); 2822135446Strhodes if (result != ISC_R_SUCCESS) 2823135446Strhodes return (result); 2824135446Strhodes 2825135446Strhodes LOCK(&mgr->lock); 2826135446Strhodes 2827186462Sdougb if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) { 2828180477Sdougb REQUIRE(isc_sockaddr_getport(localaddr) == 0); 2829180477Sdougb goto createudp; 2830180477Sdougb } 2831180477Sdougb 2832135446Strhodes /* 2833193149Sdougb * See if we have a dispatcher that matches. 2834135446Strhodes */ 2835254897Serwin if (dup_dispatch == NULL) { 2836254897Serwin result = dispatch_find(mgr, localaddr, attributes, mask, &disp); 2837254897Serwin if (result == ISC_R_SUCCESS) { 2838254897Serwin disp->refcount++; 2839135446Strhodes 2840254897Serwin if (disp->maxrequests < maxrequests) 2841254897Serwin disp->maxrequests = maxrequests; 2842135446Strhodes 2843254897Serwin if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 2844254897Serwin && (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) 2845254897Serwin { 2846254897Serwin disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; 2847254897Serwin if (disp->recv_pending != 0) 2848254897Serwin isc_socket_cancel(disp->socket, 2849254897Serwin disp->task[0], 2850254897Serwin ISC_SOCKCANCEL_RECV); 2851254897Serwin } 2852135446Strhodes 2853254897Serwin UNLOCK(&disp->lock); 2854254897Serwin UNLOCK(&mgr->lock); 2855135446Strhodes 2856254897Serwin *dispp = disp; 2857135446Strhodes 2858254897Serwin return (ISC_R_SUCCESS); 2859254897Serwin } 2860135446Strhodes } 2861135446Strhodes 2862180477Sdougb createudp: 2863135446Strhodes /* 2864135446Strhodes * Nope, create one. 2865135446Strhodes */ 2866135446Strhodes result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr, 2867254897Serwin maxrequests, attributes, &disp, 2868254897Serwin dup_dispatch == NULL 2869254897Serwin ? NULL 2870254897Serwin : dup_dispatch->socket); 2871254897Serwin 2872135446Strhodes if (result != ISC_R_SUCCESS) { 2873135446Strhodes UNLOCK(&mgr->lock); 2874135446Strhodes return (result); 2875135446Strhodes } 2876135446Strhodes 2877135446Strhodes UNLOCK(&mgr->lock); 2878135446Strhodes *dispp = disp; 2879254897Serwin 2880135446Strhodes return (ISC_R_SUCCESS); 2881135446Strhodes} 2882135446Strhodes 2883254897Serwinisc_result_t 2884254897Serwindns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 2885254897Serwin isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, 2886254897Serwin unsigned int buffersize, 2887254897Serwin unsigned int maxbuffers, unsigned int maxrequests, 2888254897Serwin unsigned int buckets, unsigned int increment, 2889254897Serwin unsigned int attributes, unsigned int mask, 2890254897Serwin dns_dispatch_t **dispp) 2891254897Serwin{ 2892254897Serwin return (dns_dispatch_getudp_dup(mgr, sockmgr, taskmgr, localaddr, 2893254897Serwin buffersize, maxbuffers, maxrequests, 2894254897Serwin buckets, increment, attributes, 2895254897Serwin mask, dispp, NULL)); 2896254897Serwin} 2897254897Serwin 2898135446Strhodes/* 2899135446Strhodes * mgr should be locked. 2900135446Strhodes */ 2901165071Sdougb 2902165071Sdougb#ifndef DNS_DISPATCH_HELD 2903165071Sdougb#define DNS_DISPATCH_HELD 20U 2904165071Sdougb#endif 2905165071Sdougb 2906135446Strhodesstatic isc_result_t 2907186462Sdougbget_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp, 2908186462Sdougb isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr, 2909254897Serwin isc_socket_t **sockp, isc_socket_t *dup_socket) 2910186462Sdougb{ 2911186462Sdougb unsigned int i, j; 2912186462Sdougb isc_socket_t *held[DNS_DISPATCH_HELD]; 2913186462Sdougb isc_sockaddr_t localaddr_bound; 2914186462Sdougb isc_socket_t *sock = NULL; 2915186462Sdougb isc_result_t result = ISC_R_SUCCESS; 2916186462Sdougb isc_boolean_t anyport; 2917186462Sdougb 2918186462Sdougb INSIST(sockp != NULL && *sockp == NULL); 2919186462Sdougb 2920186462Sdougb localaddr_bound = *localaddr; 2921186462Sdougb anyport = ISC_TF(isc_sockaddr_getport(localaddr) == 0); 2922186462Sdougb 2923186462Sdougb if (anyport) { 2924186462Sdougb unsigned int nports; 2925186462Sdougb in_port_t *ports; 2926186462Sdougb 2927186462Sdougb /* 2928186462Sdougb * If no port is specified, we first try to pick up a random 2929186462Sdougb * port by ourselves. 2930186462Sdougb */ 2931254402Serwin if (isc_sockaddr_pf(localaddr) == AF_INET) { 2932186462Sdougb nports = disp->mgr->nv4ports; 2933186462Sdougb ports = disp->mgr->v4ports; 2934186462Sdougb } else { 2935186462Sdougb nports = disp->mgr->nv6ports; 2936186462Sdougb ports = disp->mgr->v6ports; 2937186462Sdougb } 2938186462Sdougb if (nports == 0) 2939186462Sdougb return (ISC_R_ADDRNOTAVAIL); 2940186462Sdougb 2941186462Sdougb for (i = 0; i < 1024; i++) { 2942186462Sdougb in_port_t prt; 2943186462Sdougb 2944224092Sdougb prt = ports[dispatch_uniformrandom( 2945186462Sdougb DISP_ARC4CTX(disp), 2946186462Sdougb nports)]; 2947186462Sdougb isc_sockaddr_setport(&localaddr_bound, prt); 2948186462Sdougb result = open_socket(sockmgr, &localaddr_bound, 2949254897Serwin 0, &sock, NULL); 2950254402Serwin /* 2951254402Serwin * Continue if the port choosen is already in use 2952254402Serwin * or the OS has reserved it. 2953254402Serwin */ 2954254402Serwin if (result == ISC_R_NOPERM || 2955254402Serwin result == ISC_R_ADDRINUSE) 2956254402Serwin continue; 2957254402Serwin disp->localport = prt; 2958254402Serwin *sockp = sock; 2959254402Serwin return (result); 2960186462Sdougb } 2961186462Sdougb 2962186462Sdougb /* 2963186462Sdougb * If this fails 1024 times, we then ask the kernel for 2964186462Sdougb * choosing one. 2965186462Sdougb */ 2966193149Sdougb } else { 2967193149Sdougb /* Allow to reuse address for non-random ports. */ 2968193149Sdougb result = open_socket(sockmgr, localaddr, 2969254897Serwin ISC_SOCKET_REUSEADDRESS, &sock, 2970254897Serwin dup_socket); 2971193149Sdougb 2972193149Sdougb if (result == ISC_R_SUCCESS) 2973193149Sdougb *sockp = sock; 2974193149Sdougb 2975193149Sdougb return (result); 2976186462Sdougb } 2977186462Sdougb 2978186462Sdougb memset(held, 0, sizeof(held)); 2979186462Sdougb i = 0; 2980186462Sdougb 2981186462Sdougb for (j = 0; j < 0xffffU; j++) { 2982254897Serwin result = open_socket(sockmgr, localaddr, 0, &sock, NULL); 2983186462Sdougb if (result != ISC_R_SUCCESS) 2984186462Sdougb goto end; 2985186462Sdougb else if (portavailable(mgr, sock, NULL)) 2986186462Sdougb break; 2987186462Sdougb if (held[i] != NULL) 2988186462Sdougb isc_socket_detach(&held[i]); 2989186462Sdougb held[i++] = sock; 2990186462Sdougb sock = NULL; 2991186462Sdougb if (i == DNS_DISPATCH_HELD) 2992186462Sdougb i = 0; 2993186462Sdougb } 2994186462Sdougb if (j == 0xffffU) { 2995186462Sdougb mgr_log(mgr, ISC_LOG_ERROR, 2996186462Sdougb "avoid-v%s-udp-ports: unable to allocate " 2997186462Sdougb "an available port", 2998186462Sdougb isc_sockaddr_pf(localaddr) == AF_INET ? "4" : "6"); 2999186462Sdougb result = ISC_R_FAILURE; 3000186462Sdougb goto end; 3001186462Sdougb } 3002186462Sdougb *sockp = sock; 3003186462Sdougb 3004186462Sdougbend: 3005186462Sdougb for (i = 0; i < DNS_DISPATCH_HELD; i++) { 3006186462Sdougb if (held[i] != NULL) 3007186462Sdougb isc_socket_detach(&held[i]); 3008186462Sdougb } 3009186462Sdougb 3010186462Sdougb return (result); 3011186462Sdougb} 3012186462Sdougb 3013186462Sdougbstatic isc_result_t 3014135446Strhodesdispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 3015135446Strhodes isc_taskmgr_t *taskmgr, 3016135446Strhodes isc_sockaddr_t *localaddr, 3017135446Strhodes unsigned int maxrequests, 3018135446Strhodes unsigned int attributes, 3019254897Serwin dns_dispatch_t **dispp, 3020254897Serwin isc_socket_t *dup_socket) 3021135446Strhodes{ 3022135446Strhodes isc_result_t result; 3023135446Strhodes dns_dispatch_t *disp; 3024165071Sdougb isc_socket_t *sock = NULL; 3025186462Sdougb int i = 0; 3026135446Strhodes 3027135446Strhodes /* 3028135446Strhodes * dispatch_allocate() checks mgr for us. 3029135446Strhodes */ 3030135446Strhodes disp = NULL; 3031135446Strhodes result = dispatch_allocate(mgr, maxrequests, &disp); 3032135446Strhodes if (result != ISC_R_SUCCESS) 3033135446Strhodes return (result); 3034135446Strhodes 3035186462Sdougb if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0) { 3036254897Serwin result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock, 3037254897Serwin dup_socket); 3038186462Sdougb if (result != ISC_R_SUCCESS) 3039186462Sdougb goto deallocate_dispatch; 3040254897Serwin 3041254897Serwin if (isc_log_wouldlog(dns_lctx, 90)) { 3042254897Serwin char addrbuf[ISC_SOCKADDR_FORMATSIZE]; 3043254897Serwin 3044254897Serwin isc_sockaddr_format(localaddr, addrbuf, 3045254897Serwin ISC_SOCKADDR_FORMATSIZE); 3046254897Serwin mgr_log(mgr, LVL(90), "dns_dispatch_createudp: Created" 3047254897Serwin " UDP dispatch for %s with socket fd %d\n", 3048254897Serwin addrbuf, isc_socket_getfd(sock)); 3049254897Serwin } 3050254897Serwin 3051186462Sdougb } else { 3052186462Sdougb isc_sockaddr_t sa_any; 3053180477Sdougb 3054186462Sdougb /* 3055186462Sdougb * For dispatches using exclusive sockets with a specific 3056186462Sdougb * source address, we only check if the specified address is 3057186462Sdougb * available on the system. Query sockets will be created later 3058186462Sdougb * on demand. 3059186462Sdougb */ 3060186462Sdougb isc_sockaddr_anyofpf(&sa_any, isc_sockaddr_pf(localaddr)); 3061186462Sdougb if (!isc_sockaddr_eqaddr(&sa_any, localaddr)) { 3062254897Serwin result = open_socket(sockmgr, localaddr, 0, &sock, NULL); 3063186462Sdougb if (sock != NULL) 3064186462Sdougb isc_socket_detach(&sock); 3065186462Sdougb if (result != ISC_R_SUCCESS) 3066186462Sdougb goto deallocate_dispatch; 3067180477Sdougb } 3068193149Sdougb 3069193149Sdougb disp->port_table = isc_mem_get(mgr->mctx, 3070193149Sdougb sizeof(disp->port_table[0]) * 3071193149Sdougb DNS_DISPATCH_PORTTABLESIZE); 3072193149Sdougb if (disp->port_table == NULL) 3073193149Sdougb goto deallocate_dispatch; 3074193149Sdougb for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++) 3075193149Sdougb ISC_LIST_INIT(disp->port_table[i]); 3076193149Sdougb 3077193149Sdougb result = isc_mempool_create(mgr->mctx, sizeof(dispportentry_t), 3078193149Sdougb &disp->portpool); 3079193149Sdougb if (result != ISC_R_SUCCESS) 3080193149Sdougb goto deallocate_dispatch; 3081193149Sdougb isc_mempool_setname(disp->portpool, "disp_portpool"); 3082193149Sdougb isc_mempool_setfreemax(disp->portpool, 128); 3083135446Strhodes } 3084135446Strhodes disp->socktype = isc_sockettype_udp; 3085135446Strhodes disp->socket = sock; 3086135446Strhodes disp->local = *localaddr; 3087135446Strhodes 3088186462Sdougb if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) 3089186462Sdougb disp->ntasks = MAX_INTERNAL_TASKS; 3090186462Sdougb else 3091186462Sdougb disp->ntasks = 1; 3092186462Sdougb for (i = 0; i < disp->ntasks; i++) { 3093186462Sdougb disp->task[i] = NULL; 3094186462Sdougb result = isc_task_create(taskmgr, 0, &disp->task[i]); 3095186462Sdougb if (result != ISC_R_SUCCESS) { 3096224092Sdougb while (--i >= 0) { 3097224092Sdougb isc_task_shutdown(disp->task[i]); 3098224092Sdougb isc_task_detach(&disp->task[i]); 3099224092Sdougb } 3100186462Sdougb goto kill_socket; 3101186462Sdougb } 3102186462Sdougb isc_task_setname(disp->task[i], "udpdispatch", disp); 3103186462Sdougb } 3104135446Strhodes 3105135446Strhodes disp->ctlevent = isc_event_allocate(mgr->mctx, disp, 3106135446Strhodes DNS_EVENT_DISPATCHCONTROL, 3107135446Strhodes destroy_disp, disp, 3108135446Strhodes sizeof(isc_event_t)); 3109174187Sdougb if (disp->ctlevent == NULL) { 3110174187Sdougb result = ISC_R_NOMEMORY; 3111135446Strhodes goto kill_task; 3112174187Sdougb } 3113135446Strhodes 3114254897Serwin disp->sepool = NULL; 3115254897Serwin if (isc_mempool_create(mgr->mctx, sizeof(isc_socketevent_t), 3116254897Serwin &disp->sepool) != ISC_R_SUCCESS) 3117254897Serwin { 3118254897Serwin result = ISC_R_NOMEMORY; 3119254897Serwin goto kill_ctlevent; 3120254897Serwin } 3121254897Serwin 3122254897Serwin result = isc_mutex_init(&disp->sepool_lock); 3123254897Serwin if (result != ISC_R_SUCCESS) 3124254897Serwin goto kill_sepool; 3125254897Serwin 3126254897Serwin isc_mempool_setname(disp->sepool, "disp_sepool"); 3127254897Serwin isc_mempool_setmaxalloc(disp->sepool, 32768); 3128254897Serwin isc_mempool_setfreemax(disp->sepool, 32768); 3129254897Serwin isc_mempool_associatelock(disp->sepool, &disp->sepool_lock); 3130254897Serwin isc_mempool_setfillcount(disp->sepool, 16); 3131254897Serwin 3132135446Strhodes attributes &= ~DNS_DISPATCHATTR_TCP; 3133135446Strhodes attributes |= DNS_DISPATCHATTR_UDP; 3134135446Strhodes disp->attributes = attributes; 3135135446Strhodes 3136135446Strhodes /* 3137135446Strhodes * Append it to the dispatcher list. 3138135446Strhodes */ 3139135446Strhodes ISC_LIST_APPEND(mgr->list, disp, link); 3140135446Strhodes 3141135446Strhodes mgr_log(mgr, LVL(90), "created UDP dispatcher %p", disp); 3142186462Sdougb dispatch_log(disp, LVL(90), "created task %p", disp->task[0]); /* XXX */ 3143186462Sdougb if (disp->socket != NULL) 3144186462Sdougb dispatch_log(disp, LVL(90), "created socket %p", disp->socket); 3145135446Strhodes 3146135446Strhodes *dispp = disp; 3147254897Serwin 3148186462Sdougb return (result); 3149135446Strhodes 3150135446Strhodes /* 3151135446Strhodes * Error returns. 3152135446Strhodes */ 3153254897Serwin kill_sepool: 3154254897Serwin isc_mempool_destroy(&disp->sepool); 3155254897Serwin kill_ctlevent: 3156254897Serwin isc_event_free(&disp->ctlevent); 3157135446Strhodes kill_task: 3158186462Sdougb for (i = 0; i < disp->ntasks; i++) 3159186462Sdougb isc_task_detach(&disp->task[i]); 3160135446Strhodes kill_socket: 3161186462Sdougb if (disp->socket != NULL) 3162186462Sdougb isc_socket_detach(&disp->socket); 3163135446Strhodes deallocate_dispatch: 3164135446Strhodes dispatch_free(&disp); 3165186462Sdougb 3166135446Strhodes return (result); 3167135446Strhodes} 3168135446Strhodes 3169135446Strhodesvoid 3170135446Strhodesdns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp) { 3171135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3172135446Strhodes REQUIRE(dispp != NULL && *dispp == NULL); 3173135446Strhodes 3174135446Strhodes LOCK(&disp->lock); 3175135446Strhodes disp->refcount++; 3176135446Strhodes UNLOCK(&disp->lock); 3177135446Strhodes 3178135446Strhodes *dispp = disp; 3179135446Strhodes} 3180135446Strhodes 3181135446Strhodes/* 3182135446Strhodes * It is important to lock the manager while we are deleting the dispatch, 3183135446Strhodes * since dns_dispatch_getudp will call dispatch_find, which returns to 3184135446Strhodes * the caller a dispatch but does not attach to it until later. _getudp 3185135446Strhodes * locks the manager, however, so locking it here will keep us from attaching 3186135446Strhodes * to a dispatcher that is in the process of going away. 3187135446Strhodes */ 3188135446Strhodesvoid 3189135446Strhodesdns_dispatch_detach(dns_dispatch_t **dispp) { 3190135446Strhodes dns_dispatch_t *disp; 3191186462Sdougb dispsocket_t *dispsock; 3192135446Strhodes isc_boolean_t killit; 3193135446Strhodes 3194135446Strhodes REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp)); 3195135446Strhodes 3196135446Strhodes disp = *dispp; 3197135446Strhodes *dispp = NULL; 3198135446Strhodes 3199135446Strhodes LOCK(&disp->lock); 3200135446Strhodes 3201135446Strhodes INSIST(disp->refcount > 0); 3202135446Strhodes disp->refcount--; 3203135446Strhodes if (disp->refcount == 0) { 3204135446Strhodes if (disp->recv_pending > 0) 3205186462Sdougb isc_socket_cancel(disp->socket, disp->task[0], 3206135446Strhodes ISC_SOCKCANCEL_RECV); 3207186462Sdougb for (dispsock = ISC_LIST_HEAD(disp->activesockets); 3208186462Sdougb dispsock != NULL; 3209186462Sdougb dispsock = ISC_LIST_NEXT(dispsock, link)) { 3210186462Sdougb isc_socket_cancel(dispsock->socket, dispsock->task, 3211186462Sdougb ISC_SOCKCANCEL_RECV); 3212186462Sdougb } 3213135446Strhodes disp->shutting_down = 1; 3214135446Strhodes } 3215135446Strhodes 3216135446Strhodes dispatch_log(disp, LVL(90), "detach: refcount %d", disp->refcount); 3217135446Strhodes 3218135446Strhodes killit = destroy_disp_ok(disp); 3219135446Strhodes UNLOCK(&disp->lock); 3220135446Strhodes if (killit) 3221186462Sdougb isc_task_send(disp->task[0], &disp->ctlevent); 3222135446Strhodes} 3223135446Strhodes 3224135446Strhodesisc_result_t 3225186462Sdougbdns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, 3226186462Sdougb isc_task_t *task, isc_taskaction_t action, void *arg, 3227186462Sdougb dns_messageid_t *idp, dns_dispentry_t **resp, 3228186462Sdougb isc_socketmgr_t *sockmgr) 3229135446Strhodes{ 3230135446Strhodes dns_dispentry_t *res; 3231135446Strhodes unsigned int bucket; 3232186462Sdougb in_port_t localport = 0; 3233135446Strhodes dns_messageid_t id; 3234135446Strhodes int i; 3235135446Strhodes isc_boolean_t ok; 3236135446Strhodes dns_qid_t *qid; 3237186462Sdougb dispsocket_t *dispsocket = NULL; 3238186462Sdougb isc_result_t result; 3239135446Strhodes 3240135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3241135446Strhodes REQUIRE(task != NULL); 3242135446Strhodes REQUIRE(dest != NULL); 3243135446Strhodes REQUIRE(resp != NULL && *resp == NULL); 3244135446Strhodes REQUIRE(idp != NULL); 3245186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) 3246186462Sdougb REQUIRE(sockmgr != NULL); 3247135446Strhodes 3248135446Strhodes LOCK(&disp->lock); 3249135446Strhodes 3250135446Strhodes if (disp->shutting_down == 1) { 3251135446Strhodes UNLOCK(&disp->lock); 3252135446Strhodes return (ISC_R_SHUTTINGDOWN); 3253135446Strhodes } 3254135446Strhodes 3255135446Strhodes if (disp->requests >= disp->maxrequests) { 3256135446Strhodes UNLOCK(&disp->lock); 3257135446Strhodes return (ISC_R_QUOTA); 3258135446Strhodes } 3259135446Strhodes 3260186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 && 3261186462Sdougb disp->nsockets > DNS_DISPATCH_SOCKSQUOTA) { 3262186462Sdougb dispsocket_t *oldestsocket; 3263186462Sdougb dns_dispentry_t *oldestresp; 3264186462Sdougb dns_dispatchevent_t *rev; 3265186462Sdougb 3266186462Sdougb /* 3267186462Sdougb * Kill oldest outstanding query if the number of sockets 3268186462Sdougb * exceeds the quota to keep the room for new queries. 3269186462Sdougb */ 3270186462Sdougb oldestsocket = ISC_LIST_HEAD(disp->activesockets); 3271186462Sdougb oldestresp = oldestsocket->resp; 3272186462Sdougb if (oldestresp != NULL && !oldestresp->item_out) { 3273254897Serwin rev = allocate_devent(oldestresp->disp); 3274186462Sdougb if (rev != NULL) { 3275186462Sdougb rev->buffer.base = NULL; 3276186462Sdougb rev->result = ISC_R_CANCELED; 3277186462Sdougb rev->id = oldestresp->id; 3278186462Sdougb ISC_EVENT_INIT(rev, sizeof(*rev), 0, 3279186462Sdougb NULL, DNS_EVENT_DISPATCH, 3280186462Sdougb oldestresp->action, 3281186462Sdougb oldestresp->arg, oldestresp, 3282186462Sdougb NULL, NULL); 3283186462Sdougb oldestresp->item_out = ISC_TRUE; 3284186462Sdougb isc_task_send(oldestresp->task, 3285186462Sdougb ISC_EVENT_PTR(&rev)); 3286193149Sdougb inc_stats(disp->mgr, 3287193149Sdougb dns_resstatscounter_dispabort); 3288186462Sdougb } 3289186462Sdougb } 3290186462Sdougb 3291186462Sdougb /* 3292186462Sdougb * Move this entry to the tail so that it won't (easily) be 3293186462Sdougb * examined before actually being canceled. 3294186462Sdougb */ 3295186462Sdougb ISC_LIST_UNLINK(disp->activesockets, oldestsocket, link); 3296186462Sdougb ISC_LIST_APPEND(disp->activesockets, oldestsocket, link); 3297186462Sdougb } 3298186462Sdougb 3299186462Sdougb qid = DNS_QID(disp); 3300186462Sdougb 3301186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) { 3302186462Sdougb /* 3303186462Sdougb * Get a separate UDP socket with a random port number. 3304186462Sdougb */ 3305254897Serwin result = get_dispsocket(disp, dest, sockmgr, &dispsocket, 3306186462Sdougb &localport); 3307186462Sdougb if (result != ISC_R_SUCCESS) { 3308186462Sdougb UNLOCK(&disp->lock); 3309193149Sdougb inc_stats(disp->mgr, dns_resstatscounter_dispsockfail); 3310186462Sdougb return (result); 3311186462Sdougb } 3312186462Sdougb } else { 3313186462Sdougb localport = disp->localport; 3314186462Sdougb } 3315186462Sdougb 3316135446Strhodes /* 3317135446Strhodes * Try somewhat hard to find an unique ID. 3318135446Strhodes */ 3319254897Serwin LOCK(&qid->lock); 3320224092Sdougb id = (dns_messageid_t)dispatch_random(DISP_ARC4CTX(disp)); 3321135446Strhodes ok = ISC_FALSE; 3322262706Serwin i = 0; 3323262706Serwin do { 3324262706Serwin bucket = dns_hash(qid, dest, id, localport); 3325186462Sdougb if (entry_search(qid, dest, id, localport, bucket) == NULL) { 3326135446Strhodes ok = ISC_TRUE; 3327135446Strhodes break; 3328135446Strhodes } 3329135446Strhodes id += qid->qid_increment; 3330135446Strhodes id &= 0x0000ffff; 3331262706Serwin } while (i++ < 64); 3332254897Serwin UNLOCK(&qid->lock); 3333135446Strhodes 3334135446Strhodes if (!ok) { 3335135446Strhodes UNLOCK(&disp->lock); 3336135446Strhodes return (ISC_R_NOMORE); 3337135446Strhodes } 3338135446Strhodes 3339135446Strhodes res = isc_mempool_get(disp->mgr->rpool); 3340135446Strhodes if (res == NULL) { 3341186462Sdougb if (dispsocket != NULL) 3342186462Sdougb destroy_dispsocket(disp, &dispsocket); 3343262706Serwin UNLOCK(&disp->lock); 3344135446Strhodes return (ISC_R_NOMEMORY); 3345135446Strhodes } 3346135446Strhodes 3347135446Strhodes disp->refcount++; 3348135446Strhodes disp->requests++; 3349135446Strhodes res->task = NULL; 3350135446Strhodes isc_task_attach(task, &res->task); 3351135446Strhodes res->disp = disp; 3352135446Strhodes res->id = id; 3353186462Sdougb res->port = localport; 3354135446Strhodes res->bucket = bucket; 3355135446Strhodes res->host = *dest; 3356135446Strhodes res->action = action; 3357135446Strhodes res->arg = arg; 3358186462Sdougb res->dispsocket = dispsocket; 3359186462Sdougb if (dispsocket != NULL) 3360186462Sdougb dispsocket->resp = res; 3361135446Strhodes res->item_out = ISC_FALSE; 3362135446Strhodes ISC_LIST_INIT(res->items); 3363135446Strhodes ISC_LINK_INIT(res, link); 3364135446Strhodes res->magic = RESPONSE_MAGIC; 3365254897Serwin 3366254897Serwin LOCK(&qid->lock); 3367135446Strhodes ISC_LIST_APPEND(qid->qid_table[bucket], res, link); 3368135446Strhodes UNLOCK(&qid->lock); 3369135446Strhodes 3370135446Strhodes request_log(disp, res, LVL(90), 3371135446Strhodes "attached to task %p", res->task); 3372135446Strhodes 3373135446Strhodes if (((disp->attributes & DNS_DISPATCHATTR_UDP) != 0) || 3374186462Sdougb ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) != 0)) { 3375186462Sdougb result = startrecv(disp, dispsocket); 3376186462Sdougb if (result != ISC_R_SUCCESS) { 3377186462Sdougb LOCK(&qid->lock); 3378186462Sdougb ISC_LIST_UNLINK(qid->qid_table[bucket], res, link); 3379186462Sdougb UNLOCK(&qid->lock); 3380135446Strhodes 3381186462Sdougb if (dispsocket != NULL) 3382186462Sdougb destroy_dispsocket(disp, &dispsocket); 3383186462Sdougb 3384186462Sdougb disp->refcount--; 3385186462Sdougb disp->requests--; 3386186462Sdougb 3387186462Sdougb UNLOCK(&disp->lock); 3388186462Sdougb isc_task_detach(&res->task); 3389186462Sdougb isc_mempool_put(disp->mgr->rpool, res); 3390186462Sdougb return (result); 3391186462Sdougb } 3392186462Sdougb } 3393186462Sdougb 3394186462Sdougb if (dispsocket != NULL) 3395186462Sdougb ISC_LIST_APPEND(disp->activesockets, dispsocket, link); 3396186462Sdougb 3397135446Strhodes UNLOCK(&disp->lock); 3398135446Strhodes 3399135446Strhodes *idp = id; 3400135446Strhodes *resp = res; 3401135446Strhodes 3402186462Sdougb if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) 3403186462Sdougb INSIST(res->dispsocket != NULL); 3404186462Sdougb 3405135446Strhodes return (ISC_R_SUCCESS); 3406135446Strhodes} 3407135446Strhodes 3408186462Sdougbisc_result_t 3409186462Sdougbdns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, 3410186462Sdougb isc_task_t *task, isc_taskaction_t action, void *arg, 3411186462Sdougb dns_messageid_t *idp, dns_dispentry_t **resp) 3412186462Sdougb{ 3413186462Sdougb REQUIRE(VALID_DISPATCH(disp)); 3414186462Sdougb REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0); 3415186462Sdougb 3416186462Sdougb return (dns_dispatch_addresponse2(disp, dest, task, action, arg, 3417186462Sdougb idp, resp, NULL)); 3418186462Sdougb} 3419186462Sdougb 3420135446Strhodesvoid 3421135446Strhodesdns_dispatch_starttcp(dns_dispatch_t *disp) { 3422135446Strhodes 3423135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3424135446Strhodes 3425186462Sdougb dispatch_log(disp, LVL(90), "starttcp %p", disp->task[0]); 3426135446Strhodes 3427135446Strhodes LOCK(&disp->lock); 3428135446Strhodes disp->attributes |= DNS_DISPATCHATTR_CONNECTED; 3429186462Sdougb (void)startrecv(disp, NULL); 3430135446Strhodes UNLOCK(&disp->lock); 3431135446Strhodes} 3432135446Strhodes 3433135446Strhodesvoid 3434135446Strhodesdns_dispatch_removeresponse(dns_dispentry_t **resp, 3435135446Strhodes dns_dispatchevent_t **sockevent) 3436135446Strhodes{ 3437135446Strhodes dns_dispatchmgr_t *mgr; 3438135446Strhodes dns_dispatch_t *disp; 3439135446Strhodes dns_dispentry_t *res; 3440186462Sdougb dispsocket_t *dispsock; 3441135446Strhodes dns_dispatchevent_t *ev; 3442135446Strhodes unsigned int bucket; 3443135446Strhodes isc_boolean_t killit; 3444135446Strhodes unsigned int n; 3445135446Strhodes isc_eventlist_t events; 3446135446Strhodes dns_qid_t *qid; 3447135446Strhodes 3448135446Strhodes REQUIRE(resp != NULL); 3449135446Strhodes REQUIRE(VALID_RESPONSE(*resp)); 3450135446Strhodes 3451135446Strhodes res = *resp; 3452135446Strhodes *resp = NULL; 3453135446Strhodes 3454135446Strhodes disp = res->disp; 3455135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3456135446Strhodes mgr = disp->mgr; 3457135446Strhodes REQUIRE(VALID_DISPATCHMGR(mgr)); 3458135446Strhodes 3459135446Strhodes qid = DNS_QID(disp); 3460135446Strhodes 3461135446Strhodes if (sockevent != NULL) { 3462135446Strhodes REQUIRE(*sockevent != NULL); 3463135446Strhodes ev = *sockevent; 3464135446Strhodes *sockevent = NULL; 3465135446Strhodes } else { 3466135446Strhodes ev = NULL; 3467135446Strhodes } 3468135446Strhodes 3469135446Strhodes LOCK(&disp->lock); 3470135446Strhodes 3471135446Strhodes INSIST(disp->requests > 0); 3472135446Strhodes disp->requests--; 3473135446Strhodes INSIST(disp->refcount > 0); 3474135446Strhodes disp->refcount--; 3475135446Strhodes if (disp->refcount == 0) { 3476135446Strhodes if (disp->recv_pending > 0) 3477186462Sdougb isc_socket_cancel(disp->socket, disp->task[0], 3478135446Strhodes ISC_SOCKCANCEL_RECV); 3479186462Sdougb for (dispsock = ISC_LIST_HEAD(disp->activesockets); 3480186462Sdougb dispsock != NULL; 3481186462Sdougb dispsock = ISC_LIST_NEXT(dispsock, link)) { 3482186462Sdougb isc_socket_cancel(dispsock->socket, dispsock->task, 3483186462Sdougb ISC_SOCKCANCEL_RECV); 3484186462Sdougb } 3485135446Strhodes disp->shutting_down = 1; 3486135446Strhodes } 3487135446Strhodes 3488135446Strhodes bucket = res->bucket; 3489135446Strhodes 3490135446Strhodes LOCK(&qid->lock); 3491135446Strhodes ISC_LIST_UNLINK(qid->qid_table[bucket], res, link); 3492135446Strhodes UNLOCK(&qid->lock); 3493135446Strhodes 3494135446Strhodes if (ev == NULL && res->item_out) { 3495135446Strhodes /* 3496135446Strhodes * We've posted our event, but the caller hasn't gotten it 3497135446Strhodes * yet. Take it back. 3498135446Strhodes */ 3499135446Strhodes ISC_LIST_INIT(events); 3500135446Strhodes n = isc_task_unsend(res->task, res, DNS_EVENT_DISPATCH, 3501135446Strhodes NULL, &events); 3502135446Strhodes /* 3503135446Strhodes * We had better have gotten it back. 3504135446Strhodes */ 3505135446Strhodes INSIST(n == 1); 3506135446Strhodes ev = (dns_dispatchevent_t *)ISC_LIST_HEAD(events); 3507135446Strhodes } 3508135446Strhodes 3509135446Strhodes if (ev != NULL) { 3510135446Strhodes REQUIRE(res->item_out == ISC_TRUE); 3511135446Strhodes res->item_out = ISC_FALSE; 3512135446Strhodes if (ev->buffer.base != NULL) 3513135446Strhodes free_buffer(disp, ev->buffer.base, ev->buffer.length); 3514254897Serwin free_devent(disp, ev); 3515135446Strhodes } 3516135446Strhodes 3517135446Strhodes request_log(disp, res, LVL(90), "detaching from task %p", res->task); 3518135446Strhodes isc_task_detach(&res->task); 3519135446Strhodes 3520186462Sdougb if (res->dispsocket != NULL) { 3521186462Sdougb isc_socket_cancel(res->dispsocket->socket, 3522186462Sdougb res->dispsocket->task, ISC_SOCKCANCEL_RECV); 3523186462Sdougb res->dispsocket->resp = NULL; 3524186462Sdougb } 3525186462Sdougb 3526135446Strhodes /* 3527135446Strhodes * Free any buffered requests as well 3528135446Strhodes */ 3529135446Strhodes ev = ISC_LIST_HEAD(res->items); 3530135446Strhodes while (ev != NULL) { 3531135446Strhodes ISC_LIST_UNLINK(res->items, ev, ev_link); 3532135446Strhodes if (ev->buffer.base != NULL) 3533135446Strhodes free_buffer(disp, ev->buffer.base, ev->buffer.length); 3534254897Serwin free_devent(disp, ev); 3535135446Strhodes ev = ISC_LIST_HEAD(res->items); 3536135446Strhodes } 3537135446Strhodes res->magic = 0; 3538135446Strhodes isc_mempool_put(disp->mgr->rpool, res); 3539135446Strhodes if (disp->shutting_down == 1) 3540135446Strhodes do_cancel(disp); 3541135446Strhodes else 3542186462Sdougb (void)startrecv(disp, NULL); 3543135446Strhodes 3544135446Strhodes killit = destroy_disp_ok(disp); 3545135446Strhodes UNLOCK(&disp->lock); 3546135446Strhodes if (killit) 3547186462Sdougb isc_task_send(disp->task[0], &disp->ctlevent); 3548135446Strhodes} 3549135446Strhodes 3550135446Strhodesstatic void 3551135446Strhodesdo_cancel(dns_dispatch_t *disp) { 3552135446Strhodes dns_dispatchevent_t *ev; 3553135446Strhodes dns_dispentry_t *resp; 3554135446Strhodes dns_qid_t *qid; 3555135446Strhodes 3556135446Strhodes if (disp->shutdown_out == 1) 3557135446Strhodes return; 3558135446Strhodes 3559135446Strhodes qid = DNS_QID(disp); 3560135446Strhodes 3561135446Strhodes /* 3562186462Sdougb * Search for the first response handler without packets outstanding 3563186462Sdougb * unless a specific hander is given. 3564135446Strhodes */ 3565135446Strhodes LOCK(&qid->lock); 3566135446Strhodes for (resp = linear_first(qid); 3567186462Sdougb resp != NULL && resp->item_out; 3568135446Strhodes /* Empty. */) 3569135446Strhodes resp = linear_next(qid, resp); 3570186462Sdougb 3571135446Strhodes /* 3572135446Strhodes * No one to send the cancel event to, so nothing to do. 3573135446Strhodes */ 3574135446Strhodes if (resp == NULL) 3575135446Strhodes goto unlock; 3576135446Strhodes 3577135446Strhodes /* 3578135446Strhodes * Send the shutdown failsafe event to this resp. 3579135446Strhodes */ 3580135446Strhodes ev = disp->failsafe_ev; 3581135446Strhodes ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH, 3582135446Strhodes resp->action, resp->arg, resp, NULL, NULL); 3583135446Strhodes ev->result = disp->shutdown_why; 3584135446Strhodes ev->buffer.base = NULL; 3585135446Strhodes ev->buffer.length = 0; 3586135446Strhodes disp->shutdown_out = 1; 3587135446Strhodes request_log(disp, resp, LVL(10), 3588135446Strhodes "cancel: failsafe event %p -> task %p", 3589135446Strhodes ev, resp->task); 3590135446Strhodes resp->item_out = ISC_TRUE; 3591135446Strhodes isc_task_send(resp->task, ISC_EVENT_PTR(&ev)); 3592135446Strhodes unlock: 3593135446Strhodes UNLOCK(&qid->lock); 3594135446Strhodes} 3595135446Strhodes 3596135446Strhodesisc_socket_t * 3597135446Strhodesdns_dispatch_getsocket(dns_dispatch_t *disp) { 3598135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3599135446Strhodes 3600135446Strhodes return (disp->socket); 3601135446Strhodes} 3602135446Strhodes 3603186462Sdougbisc_socket_t * 3604186462Sdougbdns_dispatch_getentrysocket(dns_dispentry_t *resp) { 3605186462Sdougb REQUIRE(VALID_RESPONSE(resp)); 3606186462Sdougb 3607186462Sdougb if (resp->dispsocket != NULL) 3608186462Sdougb return (resp->dispsocket->socket); 3609186462Sdougb else 3610186462Sdougb return (NULL); 3611186462Sdougb} 3612186462Sdougb 3613135446Strhodesisc_result_t 3614135446Strhodesdns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp) { 3615135446Strhodes 3616135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3617135446Strhodes REQUIRE(addrp != NULL); 3618135446Strhodes 3619135446Strhodes if (disp->socktype == isc_sockettype_udp) { 3620135446Strhodes *addrp = disp->local; 3621135446Strhodes return (ISC_R_SUCCESS); 3622135446Strhodes } 3623135446Strhodes return (ISC_R_NOTIMPLEMENTED); 3624135446Strhodes} 3625135446Strhodes 3626135446Strhodesvoid 3627135446Strhodesdns_dispatch_cancel(dns_dispatch_t *disp) { 3628135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3629135446Strhodes 3630135446Strhodes LOCK(&disp->lock); 3631135446Strhodes 3632135446Strhodes if (disp->shutting_down == 1) { 3633135446Strhodes UNLOCK(&disp->lock); 3634135446Strhodes return; 3635135446Strhodes } 3636135446Strhodes 3637135446Strhodes disp->shutdown_why = ISC_R_CANCELED; 3638135446Strhodes disp->shutting_down = 1; 3639135446Strhodes do_cancel(disp); 3640135446Strhodes 3641135446Strhodes UNLOCK(&disp->lock); 3642135446Strhodes 3643135446Strhodes return; 3644135446Strhodes} 3645135446Strhodes 3646186462Sdougbunsigned int 3647186462Sdougbdns_dispatch_getattributes(dns_dispatch_t *disp) { 3648186462Sdougb REQUIRE(VALID_DISPATCH(disp)); 3649186462Sdougb 3650186462Sdougb /* 3651186462Sdougb * We don't bother locking disp here; it's the caller's responsibility 3652186462Sdougb * to use only non volatile flags. 3653186462Sdougb */ 3654186462Sdougb return (disp->attributes); 3655186462Sdougb} 3656186462Sdougb 3657135446Strhodesvoid 3658135446Strhodesdns_dispatch_changeattributes(dns_dispatch_t *disp, 3659135446Strhodes unsigned int attributes, unsigned int mask) 3660135446Strhodes{ 3661135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3662186462Sdougb /* Exclusive attribute can only be set on creation */ 3663186462Sdougb REQUIRE((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0); 3664186462Sdougb /* Also, a dispatch with randomport specified cannot start listening */ 3665186462Sdougb REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0 || 3666186462Sdougb (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0); 3667135446Strhodes 3668135446Strhodes /* XXXMLG 3669135446Strhodes * Should check for valid attributes here! 3670135446Strhodes */ 3671135446Strhodes 3672135446Strhodes LOCK(&disp->lock); 3673135446Strhodes 3674135446Strhodes if ((mask & DNS_DISPATCHATTR_NOLISTEN) != 0) { 3675135446Strhodes if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0 && 3676135446Strhodes (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0) { 3677135446Strhodes disp->attributes &= ~DNS_DISPATCHATTR_NOLISTEN; 3678186462Sdougb (void)startrecv(disp, NULL); 3679135446Strhodes } else if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) 3680135446Strhodes == 0 && 3681135446Strhodes (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) { 3682135446Strhodes disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; 3683135446Strhodes if (disp->recv_pending != 0) 3684186462Sdougb isc_socket_cancel(disp->socket, disp->task[0], 3685135446Strhodes ISC_SOCKCANCEL_RECV); 3686135446Strhodes } 3687135446Strhodes } 3688135446Strhodes 3689135446Strhodes disp->attributes &= ~mask; 3690135446Strhodes disp->attributes |= (attributes & mask); 3691135446Strhodes UNLOCK(&disp->lock); 3692135446Strhodes} 3693135446Strhodes 3694135446Strhodesvoid 3695135446Strhodesdns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) { 3696135446Strhodes void *buf; 3697135446Strhodes isc_socketevent_t *sevent, *newsevent; 3698135446Strhodes 3699135446Strhodes REQUIRE(VALID_DISPATCH(disp)); 3700135446Strhodes REQUIRE((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0); 3701135446Strhodes REQUIRE(event != NULL); 3702135446Strhodes 3703135446Strhodes sevent = (isc_socketevent_t *)event; 3704135446Strhodes 3705135446Strhodes INSIST(sevent->n <= disp->mgr->buffersize); 3706135446Strhodes newsevent = (isc_socketevent_t *) 3707135446Strhodes isc_event_allocate(disp->mgr->mctx, NULL, 3708186462Sdougb DNS_EVENT_IMPORTRECVDONE, udp_shrecv, 3709135446Strhodes disp, sizeof(isc_socketevent_t)); 3710135446Strhodes if (newsevent == NULL) 3711135446Strhodes return; 3712135446Strhodes 3713135446Strhodes buf = allocate_udp_buffer(disp); 3714135446Strhodes if (buf == NULL) { 3715135446Strhodes isc_event_free(ISC_EVENT_PTR(&newsevent)); 3716135446Strhodes return; 3717135446Strhodes } 3718262706Serwin memmove(buf, sevent->region.base, sevent->n); 3719135446Strhodes newsevent->region.base = buf; 3720135446Strhodes newsevent->region.length = disp->mgr->buffersize; 3721135446Strhodes newsevent->n = sevent->n; 3722135446Strhodes newsevent->result = sevent->result; 3723135446Strhodes newsevent->address = sevent->address; 3724135446Strhodes newsevent->timestamp = sevent->timestamp; 3725135446Strhodes newsevent->pktinfo = sevent->pktinfo; 3726135446Strhodes newsevent->attributes = sevent->attributes; 3727186462Sdougb 3728186462Sdougb isc_task_send(disp->task[0], ISC_EVENT_PTR(&newsevent)); 3729135446Strhodes} 3730135446Strhodes 3731254897Serwindns_dispatch_t * 3732254897Serwindns_dispatchset_get(dns_dispatchset_t *dset) { 3733254897Serwin dns_dispatch_t *disp; 3734254897Serwin 3735254897Serwin /* check that dispatch set is configured */ 3736254897Serwin if (dset == NULL || dset->ndisp == 0) 3737254897Serwin return (NULL); 3738254897Serwin 3739254897Serwin LOCK(&dset->lock); 3740254897Serwin disp = dset->dispatches[dset->cur]; 3741254897Serwin dset->cur++; 3742254897Serwin if (dset->cur == dset->ndisp) 3743254897Serwin dset->cur = 0; 3744254897Serwin UNLOCK(&dset->lock); 3745254897Serwin 3746254897Serwin return (disp); 3747254897Serwin} 3748254897Serwin 3749254897Serwinisc_result_t 3750254897Serwindns_dispatchset_create(isc_mem_t *mctx, isc_socketmgr_t *sockmgr, 3751254897Serwin isc_taskmgr_t *taskmgr, dns_dispatch_t *source, 3752254897Serwin dns_dispatchset_t **dsetp, int n) 3753254897Serwin{ 3754254897Serwin isc_result_t result; 3755254897Serwin dns_dispatchset_t *dset; 3756254897Serwin dns_dispatchmgr_t *mgr; 3757254897Serwin int i, j; 3758254897Serwin 3759254897Serwin REQUIRE(VALID_DISPATCH(source)); 3760254897Serwin REQUIRE((source->attributes & DNS_DISPATCHATTR_UDP) != 0); 3761254897Serwin REQUIRE(dsetp != NULL && *dsetp == NULL); 3762254897Serwin 3763254897Serwin mgr = source->mgr; 3764254897Serwin 3765254897Serwin dset = isc_mem_get(mctx, sizeof(dns_dispatchset_t)); 3766254897Serwin if (dset == NULL) 3767254897Serwin return (ISC_R_NOMEMORY); 3768254897Serwin memset(dset, 0, sizeof(*dset)); 3769254897Serwin 3770254897Serwin result = isc_mutex_init(&dset->lock); 3771254897Serwin if (result != ISC_R_SUCCESS) 3772254897Serwin goto fail_alloc; 3773254897Serwin 3774254897Serwin dset->dispatches = isc_mem_get(mctx, sizeof(dns_dispatch_t *) * n); 3775254897Serwin if (dset == NULL) { 3776254897Serwin result = ISC_R_NOMEMORY; 3777254897Serwin goto fail_lock; 3778254897Serwin } 3779254897Serwin 3780254897Serwin isc_mem_attach(mctx, &dset->mctx); 3781254897Serwin dset->ndisp = n; 3782254897Serwin dset->cur = 0; 3783254897Serwin 3784254897Serwin dset->dispatches[0] = NULL; 3785254897Serwin dns_dispatch_attach(source, &dset->dispatches[0]); 3786254897Serwin 3787254897Serwin LOCK(&mgr->lock); 3788254897Serwin for (i = 1; i < n; i++) { 3789254897Serwin dset->dispatches[i] = NULL; 3790254897Serwin result = dispatch_createudp(mgr, sockmgr, taskmgr, 3791254897Serwin &source->local, 3792254897Serwin source->maxrequests, 3793254897Serwin source->attributes, 3794254897Serwin &dset->dispatches[i], 3795254897Serwin source->socket); 3796254897Serwin if (result != ISC_R_SUCCESS) 3797254897Serwin goto fail; 3798254897Serwin } 3799254897Serwin 3800254897Serwin UNLOCK(&mgr->lock); 3801254897Serwin *dsetp = dset; 3802254897Serwin 3803254897Serwin return (ISC_R_SUCCESS); 3804254897Serwin 3805254897Serwin fail: 3806254897Serwin UNLOCK(&mgr->lock); 3807254897Serwin 3808254897Serwin for (j = 0; j < i; j++) 3809254897Serwin dns_dispatch_detach(&(dset->dispatches[j])); 3810254897Serwin isc_mem_put(mctx, dset->dispatches, sizeof(dns_dispatch_t *) * n); 3811254897Serwin if (dset->mctx == mctx) 3812254897Serwin isc_mem_detach(&dset->mctx); 3813254897Serwin 3814254897Serwin fail_lock: 3815254897Serwin DESTROYLOCK(&dset->lock); 3816254897Serwin 3817254897Serwin fail_alloc: 3818254897Serwin isc_mem_put(mctx, dset, sizeof(dns_dispatchset_t)); 3819254897Serwin return (result); 3820254897Serwin} 3821254897Serwin 3822254897Serwinvoid 3823254897Serwindns_dispatchset_cancelall(dns_dispatchset_t *dset, isc_task_t *task) { 3824254897Serwin int i; 3825254897Serwin 3826254897Serwin REQUIRE(dset != NULL); 3827254897Serwin 3828254897Serwin for (i = 0; i < dset->ndisp; i++) { 3829254897Serwin isc_socket_t *sock; 3830254897Serwin sock = dns_dispatch_getsocket(dset->dispatches[i]); 3831254897Serwin isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL); 3832254897Serwin } 3833254897Serwin} 3834254897Serwin 3835254897Serwinvoid 3836254897Serwindns_dispatchset_destroy(dns_dispatchset_t **dsetp) { 3837254897Serwin dns_dispatchset_t *dset; 3838254897Serwin int i; 3839254897Serwin 3840254897Serwin REQUIRE(dsetp != NULL && *dsetp != NULL); 3841254897Serwin 3842254897Serwin dset = *dsetp; 3843254897Serwin for (i = 0; i < dset->ndisp; i++) 3844254897Serwin dns_dispatch_detach(&(dset->dispatches[i])); 3845254897Serwin isc_mem_put(dset->mctx, dset->dispatches, 3846254897Serwin sizeof(dns_dispatch_t *) * dset->ndisp); 3847254897Serwin DESTROYLOCK(&dset->lock); 3848254897Serwin isc_mem_putanddetach(&dset->mctx, dset, sizeof(dns_dispatchset_t)); 3849254897Serwin 3850254897Serwin *dsetp = NULL; 3851254897Serwin} 3852254897Serwin 3853135446Strhodes#if 0 3854135446Strhodesvoid 3855135446Strhodesdns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) { 3856135446Strhodes dns_dispatch_t *disp; 3857135446Strhodes char foo[1024]; 3858135446Strhodes 3859135446Strhodes disp = ISC_LIST_HEAD(mgr->list); 3860135446Strhodes while (disp != NULL) { 3861135446Strhodes isc_sockaddr_format(&disp->local, foo, sizeof(foo)); 3862135446Strhodes printf("\tdispatch %p, addr %s\n", disp, foo); 3863135446Strhodes disp = ISC_LIST_NEXT(disp, link); 3864135446Strhodes } 3865135446Strhodes} 3866135446Strhodes#endif 3867