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, &region, 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, &region, 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